#!/usr/bin/python
#
# Converts the gamedb.json to a Excel XML spreadsheet
# It takes its input from reading a json and an optional filter text file and outputs to stdout.
#
# Example usage:
#
# ./ConvertGameDBToExcel.py gamedb.json > GameDB.xml - Converts the whole json file to xml
# ./ConvertGameDBToExcel.py gamedb.json filter.txt > GameDB.xml - Only export the GED data
# ./ConvertGameDBToExcel.py gamedb.json filter.txt --noged > GameDB.xml - Only export the data not defined in the filter file
#
import sys, csv, json
import GameDBUtils
class GameDataBase:
""" The GameDB """
data = []
def __init__(self):
self.data = json.load(open(sys.argv[1], 'rb'))
class CsvSpreadSheet:
""" A spreadsheet """
def __init__(self):
self.csvwriter = csv.writer(sys.stdout)
def NewWorkSheet(self, Name):
self.csvwriter.writerow([ Name ])
def ColumnWidths(self, ColumnWidths):
pass
def RowHeader(self, RowHeaderData):
self.csvwriter.writerow([''] + RowHeaderData)
def RowData(self, RowData):
self.csvwriter.writerow([''] + RowData)
class SchemaFilter:
"""The db schema file reader"""
arrayKeys = []
inited=False
excludeGEDData=False
#Parse the command line and extract the needed parameters
def __init__(self):
if len(sys.argv)>=3:
with open(sys.argv[2], 'rb') as schemacsvfile:
self.csvreader = csv.reader(schemacsvfile, delimiter=' ')
# Store every line that ends with a capital E
fullLengthKeys = []
for row in self.csvreader:
if ((len(row) != 0 ) and (str(row[1]) == 'E')):
fullLengthKeys.append(row[0])
# Create a list of array filters
# Keep track of the dbtype name so we can filter the entries correctly
# An array name could be in several dbtype but we might only want to see
# it for a certain type
for entry in fullLengthKeys:
# Dbtype name is always the start of the line followed by a dot
pos = entry.find('.')
if (pos != -1):
newStringArray = entry.split('.', 1);
numDelmins = newStringArray[1].count('[')
arrayKeyEntry = []
arrayKeyEntry.append(newStringArray[0])
if numDelmins!=0:
while (newStringArray[1].find('[') != -1):
stringPart = newStringArray[1].split('[', 1);
newStringArray[1] = stringPart[1];
arrayKeyEntry.append(stringPart[0] + '[')
arrayKeyEntry.append(newStringArray[1])
self.arrayKeys.append(arrayKeyEntry)
self.inited=True
# Are we exporting the xml excluding the GED data as defined by the filter file ?
if len(sys.argv)>=4:
param = str(sys.argv[3])
if param=="--noged":
self.excludeGEDData=True
class XmlSpreadSheet:
""" A spreadsheet """
haveWorkSheet = False
def __init__(self):
print('<?xml version="1.0"?>')
print('<ss:Workbook xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet">')
print(' <ss:Styles>')
print(' <ss:Style ss:ID="Heading">')
print(' <ss:Font ss:Bold="1"/>')
print(' <ss:Alignment ss:Horizontal="Left" ss:Rotate="60"/>')
print(' </ss:Style>')
print(' <ss:Style ss:ID="Float">')
print(' <ss:NumberFormat ss:Format="Fixed"/>')
print(' </ss:Style>')
print(' </ss:Styles>')
def __del__(self):
if self.haveWorkSheet == True:
print(' </ss:Table>')
print(' </ss:Worksheet>')
print('</ss:Workbook>')
def NewWorkSheet(self, Name):
if self.haveWorkSheet == True:
print(' </ss:Table>')
print(' </ss:Worksheet>')
self.haveWorkSheet = True
print(' <ss:Worksheet ss:Name="' + Name + '">')
print(' <ss:Table>')
def ColumnWidths(self, ColumnWidths):
for w in ColumnWidths:
print(' <ss:Column ss:Width="' + str(max(w * 8, 20)) + '"/>')
def RowHeader(self, RowHeaderData):
print(' <ss:Row ss:StyleID="Heading" ss:Height="200">')
for item in RowHeaderData:
print(' <ss:Cell>')
print(' <ss:Data ss:Type="String">' + str(item) + '</ss:Data>')
print(' </ss:Cell>')
print(' </ss:Row>')
def RowData(self, RowData):
print(' <ss:Row>')
for item in RowData:
if isinstance( item, float ):
print(' <ss:Cell ss:StyleID="Float">')
else:
print(' <ss:Cell>')
if str(item) == 'True' and not item == 'True':
print(' <ss:Data ss:Type="Boolean">1</ss:Data>')
elif str(item) == 'False' and not item == 'False':
print(' <ss:Data ss:Type="Boolean">0</ss:Data>')
elif isinstance( item, (int, float, long)):
print(' <ss:Data ss:Type="Number">' + str(item) + '</ss:Data>')
else:
print(' <ss:Data ss:Type="String">' + str(item) + '</ss:Data>')
print(' </ss:Cell>')
print(' </ss:Row>')
gameDB = GameDataBase()
output = XmlSpreadSheet()
gedSchemaFilter = SchemaFilter()
gamedbkeys = gameDB.data.keys()
gamedbkeys.sort()
# Iterate the tables of the GameDB
for row in gamedbkeys:
arr = gameDB.data[row]
if len(arr) > 0:
# This is both the unique set of keys of all the items in the sheet
# And also the max width of the values of that key as strings
uniqueKeyWidths = {}
for item in arr:
itemFlat = GameDBUtils.FlattenJson(item, True)
for k in itemFlat.keys():
stringLength = len(str(itemFlat[k]))
if k in uniqueKeyWidths:
uniqueKeyWidths[k] = max(uniqueKeyWidths[k], stringLength)
else:
uniqueKeyWidths[k] = stringLength
# Compile the list of keys that are present in uniqueKeyWidths.keys() and in gedSchemaFilter.arrayKeys for that row
keys = []
tempKeys = uniqueKeyWidths.keys()
if 'uid' in tempKeys:
tempKeys.remove('uid')
if gedSchemaFilter.inited==False:
#No filter file is applied
tempKeys.sort()
keys = tempKeys
else:
# a filter file is applied
for entry in tempKeys:
addToKeys=False
# The entry excludes the dbtype name which is stored in 'row'
# We need to add it back to make correct matches
fullEntry = str(row) + "." + entry
for keysInArray in gedSchemaFilter.arrayKeys:
numCount = 0
keyPartCounter = 0
for keyPart in keysInArray:
# matches have to be careful not to match against part of the work
# so look for '.' + keypart unless it's an array declaration starting with '[' or ']'
# in that case don't add the '.'
keyPartToTest = keyPart
arraySepPos = keyPart.find('[')
if arraySepPos == -1 or arraySepPos != 0:
arraySepPos = keyPart.find(']')
if keyPartCounter != 0 and ( arraySepPos != 0 ):
keyPartToTest = '.' + keyPart
keyPartCounter += 1
if fullEntry.find(keyPartToTest)==-1:
break
numCount += 1
if numCount==len(keysInArray):
addToKeys=True
break
if addToKeys==True and gedSchemaFilter.excludeGEDData==False:
keys.append(entry)
elif addToKeys==False and gedSchemaFilter.excludeGEDData==True:
keys.append(entry)
if (len(keys)==0):
continue
keys.sort()
keys = ['uid'] + keys
# Start a new worksheet (we output each table on a new page)
output.NewWorkSheet(row)
# Sort the widths in the new order of the columns
widths = []
for k in keys:
widths.append(uniqueKeyWidths[k])
# Output the column widths and the column titles
output.ColumnWidths(widths)
output.RowHeader(keys)
# Output all the rows of the current table
for item in arr:
itemFlat = GameDBUtils.FlattenJson(item, True)
vals = []
for k in keys:
# print(k)
val = ''
if k in itemFlat:
val = itemFlat[k]
vals.append(val)
output.RowData(vals)