diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..0f8650e --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +# The MIT License (MIT) + +Copyright (c) 2014 Matthew Lewis + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..b0ded43 --- /dev/null +++ b/README.md @@ -0,0 +1,90 @@ +# csvtomd: markdown tables made easy + +*Version 0.1.0* + +Convert your CSV files into Markdown tables. + +[Tables Generator](http://www.tablesgenerator.com/markdown_tables) is a fantastic web tool for converting tabular data into all sorts of table layouts. I like how it lets me import CSV files, but I need the ability to convert many CSV files in batch for a docset on which I'm working. + +I built `csvtomd` to convert one or more CSV files into nicely-padded Markdown tables. Now you can build your tables in Excel and convert them for use in GitHub, Bitbucket, or [Mou](http://mouapp.com/) Markdown files without having to construct them by hand. + +# Usage + +## Requirements + +Python 3. Tested on Python 3.4.1. + +## Input + +File: `thrones.csv` + +``` +First Name,Last Name,Location,Allegiance +Mance,Rayder,North of the Wall,Wildlings +Margaery,Tyrell,The Reach,House Tyrell +Danerys,Targaryen,Meereen,House Targaryen +Tyrion,Lannister,King's Landing,House Lannister +``` + +## Markdown Table + +First Name | Last Name | Location | Allegiance +------------|-------------|---------------------|----------------- +Mance | Rayder | North of the Wall | Wildlings +Margaery | Tyrell | The Reach | House Tyrell +Danerys | Targaryen | Meereen | House Targaryen +Tyrion | Lannister | King's Landing | House Lannister + +## Raw Output + +Command: `./csvtomd.py thrones.csv` + +``` +First Name | Last Name | Location | Allegiance +------------|-------------|---------------------|----------------- +Mance | Rayder | North of the Wall | Wildlings +Margaery | Tyrell | The Reach | House Tyrell +Danerys | Targaryen | Meereen | House Targaryen +Tyrion | Lannister | King's Landing | House Lannister +``` + +Command: `./csvtomd.py --padding 0 thrones.csv` + +``` +First Name|Last Name|Location |Allegiance +----------|---------|-----------------|--------------- +Mance |Rayder |North of the Wall|Wildlings +Margaery |Tyrell |The Reach |House Tyrell +Danerys |Targaryen|Meereen |House Targaryen +Tyrion |Lannister|King's Landing |House Lannister +``` + +## Help + +Command: `./csvtomd.py --help` + +``` +usage: csvtomd.py [-h] [-n] [-p PADDING] csv_file [csv_file ...] + +Read one or more CSV files and output their contents in the form of Markdown +tables. + +positional arguments: + csv_file One or more CSV files to be converted + +optional arguments: + -h, --help show this help message and exit + -n, --no-filenames Don't display filenames when outputting multiple + Markdown tables. + -p PADDING, --padding PADDING + The number of spaces to add between table cells and + column dividers. Default is 2 spaces. +``` + +# Contributions + +Bug reports, fixes, or features? Feel free to open an issue or pull request any time. You can also tweet me at [@mplewis](http://twitter.com/mplewis) or email me at [matt@mplewis.com](mailto:matt@mplewis.com). + +# License + +Copyright (c) 2014 Matthew Lewis. Licensed under [the MIT License](http://opensource.org/licenses/MIT). \ No newline at end of file diff --git a/csvtomd.py b/csvtomd.py new file mode 100755 index 0000000..63994fd --- /dev/null +++ b/csvtomd.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python3 + +""" +csvtomd v0.1.0 + +Convert your CSV files into Markdown tables. + +More info: http://github.com/mplewis/csvtomd +""" + +import argparse +from csv import reader + + +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 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 + """ + # Output data buffer + output = '' + # Get max length of any cell for each column + col_sizes = [max(map(len, col)) for col in zip(*table)] + # Set up the horizontal header dividers + header_divs = [None] * len(col_sizes) + num_cols = len(col_sizes) + for cell_num in range(num_cols): + # Pad header divs to the column size + header_divs[cell_num] = header_div * (col_sizes[cell_num] + + padding * 2) + # Trim first and last padding chars, if they exist + if (padding > 0): + header_div_row = divider.join(header_divs)[padding:-padding] + else: + header_div_row = divider.join(header_divs) + for row in table: + for cell_num, cell in enumerate(row): + # Pad each cell to the column size + row[cell_num] = pad_to(cell, col_sizes[cell_num]) + # Split out the header from the body + header = table[0] + body = table[1:] + # Build the inter-column dividers using the padding settings above + multipad = ' ' * padding + divider = multipad + divider + multipad + output += divider.join(header) + '\n' + output += header_div_row + '\n' + for row in body: + output += divider.join(row) + '\n' + # Strip the last newline + if output.endswith('\n'): + output = output[:-1] + return output + + +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='+', help='One or more CSV files to be converted') +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.") + +args = parser.parse_args() +first = True +for file_num, filename in enumerate(args.files): + # Print space between consecutive tables + if not first: + print('') + else: + first = False + # Read the CSV files + with open(filename, 'rU') as f: + csv = reader(f) + table = [row for row in csv] + # 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))