157 lines
		
	
	
	
		
			4.9 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			157 lines
		
	
	
	
		
			4.9 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
| #!/usr/bin/env python3
 | |
| 
 | |
| """
 | |
| csvtomd 0.2.1
 | |
| 
 | |
| Convert your CSV files into Markdown tables.
 | |
| 
 | |
| More info: http://github.com/mplewis/csvtomd
 | |
| """
 | |
| 
 | |
| import argparse
 | |
| import csv
 | |
| import sys
 | |
| 
 | |
| 
 | |
| def check_negative(value):
 | |
|     try:
 | |
|         ivalue = int(value)
 | |
|     except ValueError:
 | |
|         raise argparse.ArgumentTypeError(
 | |
|             '"%s" must be an integer' % value)
 | |
|     if ivalue < 0:
 | |
|         raise argparse.ArgumentTypeError(
 | |
|             '"%s" must not be a negative value' % value)
 | |
|     return ivalue
 | |
| 
 | |
| 
 | |
| def pad_to(unpadded, target_len):
 | |
|     """
 | |
|     Pad a string to the target length in characters, or return the original
 | |
|     string if it's longer than the target length.
 | |
|     """
 | |
|     under = target_len - len(unpadded)
 | |
|     if under <= 0:
 | |
|         return unpadded
 | |
|     return unpadded + (' ' * under)
 | |
| 
 | |
| 
 | |
| def normalize_cols(table):
 | |
|     """
 | |
|     Pad short rows to the length of the longest row to help render "jagged"
 | |
|     CSV files
 | |
|     """
 | |
|     longest_row_len = max([len(row) for row in table])
 | |
|     for row in table:
 | |
|         while len(row) < longest_row_len:
 | |
|             row.append('')
 | |
|     return table
 | |
| 
 | |
| 
 | |
| def pad_cells(table):
 | |
|     """Pad each cell to the size of the largest cell in its column."""
 | |
|     col_sizes = [max(map(len, col)) for col in zip(*table)]
 | |
|     for row in table:
 | |
|         for cell_num, cell in enumerate(row):
 | |
|             row[cell_num] = pad_to(cell, col_sizes[cell_num])
 | |
|     return table
 | |
| 
 | |
| 
 | |
| def horiz_div(col_widths, horiz, vert, padding):
 | |
|     """
 | |
|     Create the column dividers for a table with given column widths.
 | |
| 
 | |
|     col_widths: list of column widths
 | |
|     horiz: the character to use for a horizontal divider
 | |
|     vert: the character to use for a vertical divider
 | |
|     padding: amount of padding to add to each side of a column
 | |
|     """
 | |
|     horizs = [horiz * w for w in col_widths]
 | |
|     div = ''.join([padding * horiz, vert, padding * horiz])
 | |
|     return div.join(horizs)
 | |
| 
 | |
| 
 | |
| def add_dividers(row, divider, padding):
 | |
|     """Add dividers and padding to a row of cells and return a string."""
 | |
|     div = ''.join([padding * ' ', divider, padding * ' '])
 | |
|     return div.join(row)
 | |
| 
 | |
| 
 | |
| def md_table(table, *, padding=1, divider='|', header_div='-'):
 | |
|     """
 | |
|     Convert a 2D array of items into a Markdown table.
 | |
| 
 | |
|     padding: the number of padding spaces on either side of each divider
 | |
|     divider: the vertical divider to place between columns
 | |
|     header_div: the horizontal divider to place between the header row and
 | |
|         body cells
 | |
|     """
 | |
|     table = normalize_cols(table)
 | |
|     table = pad_cells(table)
 | |
|     header = table[0]
 | |
|     body = table[1:]
 | |
| 
 | |
|     col_widths = [len(cell) for cell in header]
 | |
|     horiz = horiz_div(col_widths, header_div, divider, padding)
 | |
| 
 | |
|     header = add_dividers(header, divider, padding)
 | |
|     body = [add_dividers(row, divider, padding) for row in body]
 | |
| 
 | |
|     table = [header, horiz]
 | |
|     table.extend(body)
 | |
|     table = [row.rstrip() for row in table]
 | |
|     return '\n'.join(table)
 | |
| 
 | |
| 
 | |
| def csv_to_table(file, delimiter):
 | |
|     return list(csv.reader(file, delimiter=delimiter))
 | |
| 
 | |
| 
 | |
| def main():
 | |
|     parser = argparse.ArgumentParser(
 | |
|         description='Read one or more CSV files and output their contents in '
 | |
|                     'the form of Markdown tables.')
 | |
|     parser.add_argument('files', metavar='csv_file', type=str, nargs='*',
 | |
|                         default=['-'],
 | |
|                         help="One or more CSV files to be converted. "
 | |
|                         "Use - for stdin.")
 | |
|     parser.add_argument('-n', '--no-filenames', action='store_false',
 | |
|                         dest='show_filenames',
 | |
|                         help="Don't display filenames when outputting "
 | |
|                              "multiple Markdown tables.")
 | |
|     parser.add_argument('-p', '--padding', type=check_negative, default=2,
 | |
|                         help="The number of spaces to add between table cells "
 | |
|                              "and column dividers. Default is 2 spaces.")
 | |
|     parser.add_argument('-d', '--delimiter', default=',',
 | |
|                         help='The delimiter to use when parsing CSV data. '
 | |
|                              'Default is "%(default)s"')
 | |
| 
 | |
|     args = parser.parse_args()
 | |
|     first = True
 | |
| 
 | |
|     if '-' in args.files and len(args.files) > 1:
 | |
|         print('Standard input can only be used alone.', file=sys.stderr)
 | |
|         exit(1)
 | |
|     for file_num, filename in enumerate(args.files):
 | |
|         # Print space between consecutive tables
 | |
|         if not first:
 | |
|             print('')
 | |
|         else:
 | |
|             first = False
 | |
|         # Read the CSV files
 | |
|         if filename == '-':
 | |
|             table = csv_to_table(sys.stdin, args.delimiter)
 | |
|         else:
 | |
|             with open(filename, 'rU') as f:
 | |
|                 table = csv_to_table(f, args.delimiter)
 | |
|         # Print filename for each table if --no-filenames wasn't passed and
 | |
|         # more than one CSV was provided
 | |
|         file_count = len(args.files)
 | |
|         if args.show_filenames and file_count > 1:
 | |
|             print(filename + '\n')
 | |
|         # Generate and print Markdown table
 | |
|         print(md_table(table, padding=args.padding))
 | |
| 
 | |
| 
 | |
| if __name__ == '__main__':
 | |
|     main()
 |