#!/usr/bin/python3
# pylint: disable=no-name-in-module,import-error,no-member,superfluous-parens
# superfluous-parens: Parentheses are valuable for clarity and portability
'''Create HTML tables from CSV inputs'''
import sys
try:
import urllib.parse
except ImportError:
import urllib
HAVE_URLLIB_PARSE = False
else:
HAVE_URLLIB_PARSE = True
def usage(retval):
'''Output a usage message and exit'''
write = sys.stderr.write
write("Convert a whitespace (or arbitrarily) delimited\n")
write("CSV table, one row per line, to an HTML table.\n\n")
write("Usage:\n")
write(" -d use a delimiter of |\n")
write(" --delimiter ch use a delimiter of ch\n")
write(" --help this stuff\n")
write(" --suppressible Output javascript code to allow suppressing unwanted rows and columns\n")
write(" --tooltips Output CSS styling to allow row/column tooltips\n")
write(' --selections Output clickable selections\n')
write(" --yes-no Output yes's and no's (yes's and empty spaces really) instead of the\n")
write(" actual table values\n")
write(' --subtables Build a subtable for each row\n')
write(' --links Hyperlink appropriately using 2nd column\n')
write(' --no-comment Do not output the input CSV as a series of comments\n')
sys.exit(retval)
INDENT = ' '
FALSE_VALUES = frozenset(['', '0', 'n', 'no', 'No', 'N', 'false', 'False', 'n/a', 'N/A'])
def supression_functions():
'''Output javascript code needed for suppressing rows and columns'''
return '''
'''
def tooltip_style():
'''Output enough CSS that we can bring up tooltips for table elements, describing what row and column they correspond to'''
return '''
'''
def deal_with_special_chars(human_readable_name):
'''Escape special characters in a row or column name, for use in selecting a row or column by name'''
spaces_dashed = human_readable_name.replace(' ', '-')
if HAVE_URLLIB_PARSE:
browser_happy_name = urllib.parse.quote(spaces_dashed)
else:
browser_happy_name = urllib.quote(spaces_dashed)
return browser_happy_name
class Field(object):
'''Store one field in the table, and perform operations on it'''
row_urls = []
def __init__(self, options, row, col, string):
self.options = options
self.row = row
self.col = col
self.string = string
def is_true(self):
'''Return true if this field has a value'''
if self.string in FALSE_VALUES:
return False
return True
def is_left(self):
'''Is this field on the left edge of the table?'''
return self.col == 0
def is_right(self):
'''Is this field on the right edge of the table?'''
return self.col == self.options.width - 1
def is_top(self):
'''Is this field on the top edge of the table?'''
return self.row == 0
def is_bottom(self):
'''Is this field on the bottom edge of the table?'''
return self.row == self.options.height - 1
def column_supression_button(self):
'''Return the HTML for a column supression button'''
if self.is_top() and not self.is_left():
return '
' % (self.col + 1)
return ''
def row_supression_button(self):
'''Return the HTML for a row supression button'''
if not self.is_top() and self.is_left():
result = '' % (self.row + 1)
else:
result = ''
return result
def column_selection_button(self):
'''Return the HTML for a column selection button'''
if self.is_top() and not self.is_left():
result = '' % (
deal_with_special_chars(self.string), )
else:
result = ''
return result
def row_selection_button(self):
'''Return the HTML for a row selection button'''
if not self.is_top() and self.is_left():
result = '' % (
deal_with_special_chars(self.string), )
else:
result = ''
return result
def id_row(self):
'''Output a row id for supression'''
if self.options.suppressible:
result = ' id="row-%d"' % (self.row + 1)
else:
result = ''
return result
def id_col(self):
'''Output a column id for supression'''
if self.options.suppressible:
result = ' name="col-%d"' % (self.col + 1)
else:
result = ''
return result
def id_table(self):
'''Output a table id for supression'''
if self.options.suppressible:
result = 'id="table" '
else:
result = ''
return result
def span_start(self, row_description, col_description):
'''Output just the start of the CSS span'''
if self.options.tooltips and not self.is_left() and not self.is_top():
result = '' % (
row_description,
col_description,
)
else:
result = ''
return result
def span_end(self):
'''Output just the end of the CSS span'''
if self.options.tooltips and not self.is_left() and not self.is_top():
result = ''
else:
result = ''
return result
def stringer(self):
'''Convert a field to a yes/no value, except for the left and top edges'''
if self.is_left() or self.is_top() or not self.options.yesno:
if self.is_left() and not self.is_top() and self.options.links:
return '%s' % (Field.row_urls[self.row], self.string)
return self.string
else:
if self.string in FALSE_VALUES:
return ''
return 'Yes'
def to_string(self, row_description, col_description):
'''Convert this field to a string, with appropriate HTML/CSS/Javascript added'''
stuff = []
if self.is_left():
if self.is_top():
if self.options.tooltips:
stuff.append(tooltip_style())
if self.options.suppressible:
stuff.append(supression_functions())
stuff.append('
' % (INDENT*2, self.id_col()))
if self.options.suppressible:
stuff.append(self.column_supression_button())
stuff.append(self.row_supression_button())
if self.options.selections:
stuff.append(self.column_selection_button())
stuff.append(self.row_selection_button())
if self.options.tooltips:
stuff.append(self.span_start(row_description, col_description))
if self.is_top():
stuff.append(' ') stuff.append(self.stringer()) if self.options.tooltips: stuff.append(self.span_end()) stuff.append(' | \n')
if self.is_right():
stuff.append('%s
%s | %s | \n' % (INDENT*4, self.get_field(0, col).string, self.get_field(row, col).string), ) list_.append('%s