#!/usr/bin/env python3
# generates markdown tables from CSV files, after doing lovely sorting
# by different columns, blah blah...
from copy import deepcopy
import functools
def write_mdwn_row(f, row):
row = "|".join(row)
row = "|%s|\n" % row
f.write(row)
def underlines(header):
row = []
for col in header:
row.append("-" * len(col))
return row
# sorting functions
def is_svp64_page(page):
return page in ['sv/setvl', 'sv/svstep', 'sv/remap']
def sort_by_page(p1, p2):
p1 = p1['page']
p2 = p2['page']
if not (is_svp64_page(p1) ^ is_svp64_page(p2)):
if p1 < p2: return -1
if p1 > p2: return 1
return 0
if is_svp64_page(p1):
return -1
if is_svp64_page(p2):
return 1
return 0
levels = ['SFFS', 'SV/E', 'SV/D', 'SV/S', 'opt']
def sort_by_level(p1, p2):
p1 = levels.index(p1['level'])
p2 = levels.index(p2['level'])
if p1 < p2: return -1
if p1 > p2: return 1
return 0
priorities = ['high', 'med', 'low', 'TBD']
def sort_by_priority(p1, p2):
p1 = priorities.index(p1['priority'])
p2 = priorities.index(p2['priority'])
if p1 < p2: return -1
if p1 > p2: return 1
return 0
def sort_by_cost(p1, p2):
p1 = p1['cost']
p2 = p2['cost']
if not p1.isdigit(): p1 = 0
if not p2.isdigit(): p2 = 0
p1 = int(p1)
p2 = int(p2)
if p1 < p2: return -1
if p1 > p2: return 1
return 0
def sort_by_level_priority_cost(p1, p2):
v = sort_by_level(p1, p2)
if v == 0:
v = sort_by_priority(p1, p2)
if v == 0:
v = sort_by_cost(p1, p2)
return v
def sort_by_cost_priority_page(p1, p2):
v = sort_by_cost(p1, p2)
if v == 0:
v = sort_by_priority(p1, p2)
if v == 0:
v = sort_by_page(p1, p2)
return v
def by_levelpriority_then_cost(areas):
# first blat all columns together (drop area-dict)
res = []
for row in areas.values():
res += row
# now sort them
res = sorted(res, key=functools.cmp_to_key(sort_by_level_priority_cost))
# now split out into a dict again this time by cost-priority
levels = {}
for row in res:
key = row['level']+row['priority']
if key not in levels:
levels[key] = []
levels[key].append(row)
return levels
def by_cost_then_priority_then_page(areas):
# first blat all columns together (drop area-dict)
res = []
for row in areas.values():
res += row
# now sort them
res = sorted(res, key=functools.cmp_to_key(sort_by_cost_priority_page))
# now split out into a dict again this time by cost-priority
costs = {}
for row in res:
cost = row['cost']
if cost not in costs:
costs[cost] = []
costs[cost].append(row)
return costs
# For prettier printing, replace short column heading
# names with full, consistent names.
# Expected input is a list of column strings
def column_header_replacement(header):
replacement_col = {'cost': 'XO Cost'}
new_header = deepcopy(header)
for index, shortname in enumerate(replacement_col.keys()):
# update with replacement if any otherwise leave alone
new_header[index] = replacement_col.get(shortname, shortname)
return new_header
def print_table(title, header, areas, sortby):
fname = title.lower().replace(" ", "_")
with open("ls012/%s.mdwn" % fname, "w") as f:
# write out the page header
f.write("\\newpage{}\n")
f.write("\n")
f.write("# %s\n" % title)
f.write("\n")
# sort everything if required
if sortby is not None:
areas = sortby(areas)
linecount = None
# start writing out areas
for title, rows in areas.items():
# start new page (if not first newpage)
if linecount is not None:
# allow 58 rows per page
linecount += len(rows)
if linecount >= 58:
linecount = 0
if linecount == 0:
f.write("\\newpage{}\n")
f.write("\n")
if linecount is None: # skipped first newpage
linecount = 0
f.write("## %s\n" % title)
f.write("\n")
# work out maximum length of items, and adjust header
hdr = deepcopy(header)
cols = {}
for hd in hdr:
cols[hd] = len(hd)
for row in rows:
for hd, value in row.items():
cols[hd] = max(cols[hd], len(value))
# adjust header (add spaces)
for i, hd in enumerate(hdr):
n_spaces = cols[hd] - len(hd)
hdr[i] = hdr[i] + " " * n_spaces
# write out header
write_mdwn_row(f, hdr)
write_mdwn_row(f, underlines(hdr))
for row in rows:
# adjust row (add same spaces as header width)
r = []
for key in row.keys():
col_len, value = cols[key], row[key]
if key == 'page':
prefix = 'https://libre-soc.org/openpower/'
v = value.replace("_", "\_") # latex, duh
url = '[%s](%s%s)' % (value, prefix, v)
r.append(url)
elif key == 'rfc' and value.startswith('ls'):
prefix = 'https://libre-soc.org/openpower/sv/rfc/'
url = '[%s](%s%s)' % (value, prefix, value)
r.append(url)
else:
value = value.replace("_", "\_") # latex, duh
n_spaces = col_len - len(value)
r.append(value + " " * n_spaces)
# write row
write_mdwn_row(f, r)
f.write("\n\n")
# approx 8 lines per header
linecount += 9
if __name__ == '__main__':
with open("ls012/optable.csv") as f:
l = map(str.strip, f.readlines())
areas = {}
header = None
for line in l:
if line.startswith("#"):
area = line[1:].strip()
areas[area]= []
continue
# split line by commas, whitespace-strip it
line = list(map(str.strip, line.split(',')))
# identify header
if header is None:
header = line
continue
# create a dictionary by tuple of header+line
linedict = dict(zip(header, line))
#print (area)
#print (linedict)
#print ()
# store line in area
areas[area].append(linedict)
# excellent - now have a dictionary of list of dictionaries:
# area - list-of-instructions - dictionary-by-heading
print_table("Areas", header, areas, None)
# now sort by cost and then by page
print_table("XO cost", header, areas, by_cost_then_priority_then_page)
# now sort by level combined with priority, then cost
print_table("Level", header, areas, by_levelpriority_then_cost)