Newer
Older
Import / applications / Python / JsonTools.py
#!/usr/bin/python


import json
import collections
import os
import xml.dom.minidom


def FlattenDict(data, prefix = ''):
	if isinstance(data, list):
		#if prefix == '':
		#	print("prefix required for lists")
		#	return data
		output = {}
		for i in range(0,len(data)):
			output.update(FlattenDict(data[i], prefix + '[' + str(i) + ']'))
		return output
	if isinstance(data, dict):
		if not prefix == '':
			prefix = prefix + '.'
		output = {}
		for k in data.keys():
			output.update(FlattenDict(data[k], prefix + k))
		return output
	if prefix == '':
		return data
	return { prefix : data }


# Convert a python dict to a 2D table / matrix of values, still as a dict
def DictToTable(data):
	rowKeys = data.keys()
	columnKeys = set()
	flattenedData = {}
	for k in rowKeys:
		flattenedData[k] = FlattenDict(data[k])
		colKeys = flattenedData[k].keys()
		for c in colKeys:
			columnKeys.add(c)
	return { "rows" : sorted(rowKeys),
		 "columns" : sorted(list(columnKeys)),
		 "data" : flattenedData }
	

def DictToHtmlTable(data):
	table = DictToTable(data)
	items = ['<table>', '<tr><th></th>']
	for col in table["columns"]:
	    items.append('<th>%s</th>' % col)
	items.append('</tr>')
	for row in table["rows"]:
	    items.append('<tr><th>%s</th>' % row)
	    for col in table["columns"]:
		val = ''
		if col in data[row]:
			val = data[row][col]
		items.append('<td>%s</td>' % val)
	    items.append('</tr>')
	items.append('</table>')
	return '\n'.join(items)


def TableToCSV(table):
	lines = []
	line = '## row-name'
	for col in table["columns"]:
	    line = line + ' , ' + col
	lines.append(line)
	for row in table["rows"]:
	    line = row
	    for col in table["columns"]:
		val = ''
		if col in table["data"][row]:
			val = table["data"][row][col]
	    	line = line + ' , ' + str(val)
	    lines.append(line)
	return '\n'.join(lines)


def DictToCSVTable(data):
	return TableToCSV(DictToTable(data))


def TableToXmlWorksheet(table, name='Worksheet1'):
	columnCount = len(table["columns"])
	maxColWidth = 2
	for col in table["columns"]:
		maxColWidth = max(maxColWidth, len(col))
	maxRowWidth = 2
	for row in table["rows"]:
		maxRowWidth = max(maxRowWidth, len(row))
	lines = []
	lines.append('<ss:Worksheet ss:Name="' + name + '">')
	lines.append('<Table ss:StyleID="table">')
	# Width needs to be as wide as widest row name
	lines.append('<Column ss:Width="50.0"/>')
	lines.append('<Column ss:Width="' + str(maxRowWidth*8) + '.0"/>')
	for col in table["columns"]:
		lines.append('<Column ss:Width="20.0"/>')
	lines.append('<Column ss:Width="50.0"/>')
	lines.append('<Row ss:Height="50.0">')
	lines.append('<Cell ss:MergeAcross="' + str(columnCount+2) + '" ss:StyleID="Heading"> <Data ss:Type="String">' + name + '</Data> </Cell>')
	lines.append('</Row>')
 	# Height needs to be as high as widest column name
	lines.append('<Row ss:Height="' + str(maxColWidth*6) + '.0"><Cell ss:StyleID="Blank"/><Cell ss:StyleID="ColumnHeader"/>')
	for col in table["columns"]:
		lines.append('<Cell ss:StyleID="ColumnHeader"> <Data ss:Type="String">' + col + '</Data> </Cell>')
	lines.append('<Cell ss:StyleID="Blank"/></Row>')
	alt = 0
	for row in table["rows"]:
		lines.append('<Row ss:AutoFitHeight="0" ss:Height="13.0">')
		lines.append('<Cell ss:StyleID="Left"/>')
		lines.append('<Cell ss:StyleID="Row' + str(alt) + '"> <Data ss:Type="String">' + str(row) + '</Data> </Cell>')
		for col in table["columns"]:
			val = ''
			if col in table["data"][row]:
				val = table["data"][row][col]
			lines.append('<Cell ss:StyleID="Value' + str(alt) + '"> <Data ss:Type="String">' + str(val) + '</Data> </Cell>')
		lines.append('<Cell ss:StyleID="Right"/>')
		lines.append('</Row>')
		alt = 1 - alt
	lines.append('<Row ss:Height="50.0"><Cell ss:StyleID="Blank"/>')
	lines.append('<Cell ss:MergeAcross="' + str(columnCount) + '" ss:StyleID="Bottom"> </Cell>')
	lines.append('<Cell ss:StyleID="Blank"/></Row>')
	lines.append('</Table>')
	lines.append('<x:WorksheetOptions/>')
	lines.append('</ss:Worksheet>')
	return '\n'.join(lines)


def ConvertToExcelFile(data):
	lines = [ '<?xml version="1.0" ?>',
		 '<?mso-application progid="Excel.Sheet"?>',
		 '<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"',
		 'xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet">',
		 '<Styles>', '<Style ss:ID="Default" ss:Name="Default"/>',
		
		 '<Style ss:ID="Heading"><Alignment ss:Vertical="Center" ss:Horizontal="Center"/>',
		 '<Interior ss:Color="#dddddd" ss:Pattern="Solid"/>',
		 '<Font ss:Bold="1" ss:Size="16"/></Style>',

		 '<Style ss:ID="ColumnHeader"> <Alignment ss:Horizontal="Left" ss:Rotate="45"/><Borders>',
		 '<Border ss:Color="#000000" ss:LineStyle="Continuous" ss:Position="Bottom" ss:Weight="1"/>',
		 '</Borders><Interior ss:Color="#dddddd" ss:Pattern="Solid"/></Style>',

		 '<Style ss:ID="Left"><Borders>',
		 '<Border ss:Color="#000000" ss:LineStyle="Continuous" ss:Position="Right" ss:Weight="1"/>',
		 '</Borders><Interior ss:Color="#dddddd" ss:Pattern="Solid"/></Style>',

		 '<Style ss:ID="Right"><Borders>',
		 '<Border ss:Color="#000000" ss:LineStyle="Continuous" ss:Position="Left" ss:Weight="1"/>',
		 '</Borders><Interior ss:Color="#dddddd" ss:Pattern="Solid"/></Style>',

		 '<Style ss:ID="Bottom"><Borders>',
		 '<Border ss:Color="#000000" ss:LineStyle="Continuous" ss:Position="Top" ss:Weight="1"/>',
		 '</Borders><Interior ss:Color="#dddddd" ss:Pattern="Solid"/></Style>',

		 '<Style ss:ID="Blank"><Interior ss:Color="#dddddd" ss:Pattern="Solid"/></Style>',

		 '<Style ss:ID="Row0"><Interior ss:Color="#dddddd" ss:Pattern="Solid"/></Style>',
		 '<Style ss:ID="Row1"><Interior ss:Color="#eeeeee" ss:Pattern="Solid"/></Style>',

		 '<Style ss:ID="Value0"><Borders><Border ss:Color="#000000" ss:LineStyle="Continuous" ss:Position="Left" ss:Weight="1"/>',
		 '</Borders><Interior ss:Color="#ddddee" ss:Pattern="Solid"/></Style>',

		 '<Style ss:ID="Value1"><Borders><Border ss:Color="#000000" ss:LineStyle="Continuous" ss:Position="Left" ss:Weight="1"/>',
		 '</Borders><Interior ss:Color="#eeeeff" ss:Pattern="Solid"/></Style>',

		 '<Style ss:ID="table"> <Font ss:Size="10" ss:VerticalAlign="Subscript"/> </Style>',

		 '</Styles>' ]
	for worksheet in sorted(data.keys()):
		lines.append(TableToXmlWorksheet(DictToTable(data[worksheet]), worksheet))
	lines.append('</Workbook>')
	return '\n'.join(lines)


def PrettyPrintJson(data):
	return json.dumps(data, indent=4, sort_keys=True)


def PrettyPrintXmlString(string):
	xmlVal = xml.dom.minidom.parseString(string)
	return xmlVal.toprettyxml()


def PrettyPrintXmlFile(filename):
	xmlVal = xml.dom.minidom.parse(filename)
	return xmlVal.toprettyxml()


# Outputs these files: <name>.csv <name>.pg and <name>.png
# the PNG output is the image of the graph
def GraphData(name, data, x_label='', y_label='', title=''):
	table = DictToTable(data)
	csvData = TableToCSV(table)
	with open(name + ".csv", "w") as csvFile:
		csvFile.write(csvData)
	lines = [ '#!/usr/bin/gnuplot', 'reset', 'set terminal png size 1200,800',
		'set style data lines', 'set datafile separator ","',
		'set xlabel "' + x_label + '"', 'set ylabel "' + y_label + '"',
		'set title  "' + title + '"', 'set output "' + name + '.png"',
		#set xdata time
		#set timefmt "%Y-%m-%dT%H:%M:%S"
		#set format x "%d/%m"
	]
	columns = table["columns"]
	cols = []
	for i in range(len(columns)):
		cols.append('"' + name + '.csv" using 1:'
			 + str(i+2) + ' with lines t "' + columns[i] + '"')
	cols[0] = 'plot ' + cols[0]
	lines.append(','.join(cols))
	commands = '\n'.join(lines)
	with open(name + ".pg", "w") as pgFile:
		pgFile.write(commands)
	print(commands)
	os.system("gnuplot " + name + ".pg")
	return '\n'.join(lines)




###########################
# Tests
###########################

def FlattenDictTest(data, expected):
	ordered = json.dumps(collections.OrderedDict(sorted(FlattenDict(data).items())))
	comparison = json.dumps(collections.OrderedDict(sorted(expected.items())))
	if ordered != comparison:
		print("FAIL: ")
		print("         " + ordered)
		print("      != " + comparison)
	else:
		print("PASS")


def FlattenDictTests():
	FlattenDictTest({ "a" : { "b" : 1,  "c" : 2  }  },
	                { "a.b" : 1, "a.c" : 2 })
	FlattenDictTest({ "a" : { "b" : 1,  "c" : { "d" : 2, "e" : 3 } } },
                        { "a.b" : 1, "a.c.d" : 2, "a.c.e" : 3 })
	FlattenDictTest({ "a" : { "b" : 1,  "c" : [ 2, 3 ] } },
			{ "a.b" : 1, "a.c[0]" : 2, "a.c[1]" : 3 })
	FlattenDictTest({ "a" : [ 1, 2, 3 ] },
			{ "a[0]" : 1, "a[1]" : 2, "a[2]" : 3 })
	FlattenDictTest([ 1, 2, 3 ],
			{ "[0]" : 1, "[1]" : 2, "[2]" : 3})


def UnitTests():

  FlattenDictTests()

  print(json.dumps(DictToTable({
    "a" : {   "m_a" : 1, "m_b" : 2 },
    "b" : {   "m_b" : 1, "m_c" : 2 },
    "c" : {   "m_a" : 1, "m_c" : 2 },
    "d" : {   "m_d" : 1, "m_b" : 2 },
    "e" : {   "m_c" : 1 },
    "f" : {   }
  })))

  print(DictToHtmlTable({
    "a" : {   "m_a" : 1, "m_b" : 2 },
    "b" : {   "m_b" : 1, "m_c" : 2 },
    "c" : {   "m_a" : 1, "m_c" : 2 },
    "d" : {   "m_d" : 1, "m_b" : 2 },
    "e" : {   "m_c" : 1 },
    "f" : {   }
  }))

  print(DictToCSVTable({
    "a" : {   "m_a" : 1, "m_b" : 2 },
    "b" : {   "m_b" : 1, "m_c" : 2 },
    "c" : {   "m_a" : 1, "m_c" : 2 },
    "d" : {   "m_d" : 1, "m_b" : 2 },
    "e" : {   "m_c" : 1 },
    "f" : {   }
  }))

  graphData = {
    "0" : { "AA" : 0, "BB" : 3 },
    "1" : { "AA" : 1, "BB" : 1 },
    "2" : { "CC" : 2, "BB" : 2 },
    "3" : { "CC" : 3, "BB" : 0 },
  }

  # GraphData('blah', graphData, x_label="X", y_label="Y", title="Title")
  GraphData('generated', graphData)

  print(PrettyPrintJson(graphData))

  print(PrettyPrintXmlString("<xml> <title> blah </title> </xml>"))

  excelStr = ConvertToExcelFile({
	"Worksheet A" : { "uidA" : { "m_a.x" :  1, "m_a.y" :  2, "m_a.z" :  3 },
			  "uidB" : { "m_a.x" :  4, "m_a.y" :  5, "m_a.z" :  6 },
			  "uidC" : { "m_a.x" :  7, "m_a.y" :  8, "m_a.z" :  9 } },
	"Worksheet B" : { "uidA" : { "m_a.x" : 10, "m_a.y" : 11, "m_a.z" : 12 },
			  "uidB" : { "m_a.x" : 13, "m_a.y" : 14, "m_a.z" : 15 },
			  "uidC" : { "m_a.x" : 16, "m_a.y" : 17, "m_a.z" : 18 } },
	"Worksheet C" : {  }
  })
  with open("generated.xml", "w") as xlsFile:
  	xlsFile.write(excelStr)


UnitTests()