From e91e498be862732d89392fa59c85045a92a584b0 Mon Sep 17 00:00:00 2001 From: Arthur de Jong Date: Thu, 5 Apr 2018 18:24:48 +0200 Subject: Add --columns option This option can be used to override the list of columns as found in the first line of the CSV file or provide a mapping for values found in the first line to PSKC properties. --- docs/csv2pskc.rst | 19 ++++++++++ pskc/scripts/csv2pskc.py | 27 +++++++++++--- tests/test_csv2pskc.doctest | 86 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 127 insertions(+), 5 deletions(-) diff --git a/docs/csv2pskc.rst b/docs/csv2pskc.rst index 1c82a9f..06aabce 100644 --- a/docs/csv2pskc.rst +++ b/docs/csv2pskc.rst @@ -32,6 +32,25 @@ Options By default :program:`csv2pskc` writes a PSKC file to stdout. This option can be used to save to a file instead. +.. option:: -c COL,COL,.., --columns COL,COL,.. + + Specify the meaning of the columns in the CSV file. By default the first + row of the CSV file is expected to list the names of the columns. + + Any property of :class:`~pskc.key.Key` instances can be used as well as + :class:`~pskc.policy.Policy` properties via ``policy``. For example: + ``serial``, ``secret``, ``counter``, ``time_offset``, ``time_interval``, + ``interval``, ``time_drift``, ``issuer``, ``manufacturer``, + ``response_length``, ``policy.pin_min_length``. + + This option can either specify a list of columns or a COL:KEY mapping + where COL refers to the value found in the first line of the CSV file and + KEY refers to a property as described above. + + It is possible to map a single column in the CSV file to multiple PSKC + properties (e.g. use of ``id+serial`` sets both the ID and device serial + number to the value found in that column). + .. option:: -p PASS/FILE, --password PASS/FILE, --passwd PASS/FILE Encrypt the PSKC file with the specified password. If the argument refers diff --git a/pskc/scripts/csv2pskc.py b/pskc/scripts/csv2pskc.py index 814e756..a815755 100644 --- a/pskc/scripts/csv2pskc.py +++ b/pskc/scripts/csv2pskc.py @@ -52,6 +52,9 @@ parser.add_argument( parser.add_argument( '-o', '--output', metavar='FILE', help='write PSKC to file instead of stdout') +parser.add_argument( + '-c', '--columns', metavar='COL|COL:LABEL,..', + help='list of columns or label to column mapping to import') parser.add_argument( '-p', '--password', '--passwd', metavar='PASS/FILE', help='password to use for encrypting the PSKC file)') @@ -103,14 +106,28 @@ def main(): args = parser.parse_args() # open the CSV file csvfile = open_csvfile(open(args.input, 'r') if args.input else sys.stdin) - columns = next(csvfile) + # figure out the meaning of the columns + columns = [x.lower().replace(' ', '_') for x in next(csvfile)] + if args.columns: + if ':' in args.columns: + # --columns is a list of mappings + mapping = dict( + (label.lower().replace(' ', '_'), key.lower()) + for label, key in ( + column.split(':') + for column in args.columns.split(','))) + columns = [mapping.get(column, column) for column in columns] + else: + # --columns is a list of columns + columns = [x.lower() for x in args.columns.split(',')] # store rows in PSKC structure pskcfile = pskc.PSKC() for row in csvfile: - data = dict( - (key, from_column(key, value, args)) - for key, value in zip(columns, row) - if value and key not in ('', '-')) + data = {} + for column, value in zip(columns, row): + for key in column.split('+'): + if value and key not in ('', '-'): + data[key] = from_column(key, value, args) pskcfile.add_key(**data) # encrypt the file if needed if args.secret: diff --git a/tests/test_csv2pskc.doctest b/tests/test_csv2pskc.doctest index afa82e2..0c6e8d5 100644 --- a/tests/test_csv2pskc.doctest +++ b/tests/test_csv2pskc.doctest @@ -306,6 +306,92 @@ automatically pick up tab-separated files. +We can use the --columns option to override using the first row to specify +the key properties. + +>>> f = tempfile.NamedTemporaryFile('w+t') +>>> x = f.write(''' +... nr,key,start date,info +... 121232,6848464354638468468835346896846846846846,2017-04-01,something +... 213422,9843138168168196616849849634548496832446,2017-02-12,else +... '''.lstrip()) +>>> f.flush() +>>> sys.argv = ['csv2pskc', f.name, '--columns', 'id+serial,secret,start_date,-'] +>>> main() #doctest: +REPORT_UDIFF +NORMALIZE_WHITESPACE + + + + + 121232 + 2017-04-01T00:00:00 + + + + + aEhGQ1RjhGhGiDU0aJaEaEaEaEY= + + + + + + + 213422 + 2017-02-12T00:00:00 + + + + + mEMTgWgWgZZhaEmEljRUhJaDJEY= + + + + + + + +Alternatively, we can provide a mapping for column names found in the CSV +file to key properties. + +>>> f = tempfile.NamedTemporaryFile('w+t') +>>> x = f.write(''' +... nr,key,start date,info +... 121232,6848464354638468468835346896846846846846,2017-04-01,something +... 213422,9843138168168196616849849634548496832446,2017-02-12,else +... '''.lstrip()) +>>> f.flush() +>>> sys.argv = ['csv2pskc', f.name, '--columns', 'key:secret,nr:id+serial,info:-'] +>>> main() #doctest: +REPORT_UDIFF +NORMALIZE_WHITESPACE + + + + + 121232 + 2017-04-01T00:00:00 + + + + + aEhGQ1RjhGhGiDU0aJaEaEaEaEY= + + + + + + + 213422 + 2017-02-12T00:00:00 + + + + + mEMTgWgWgZZhaEmEljRUhJaDJEY= + + + + + + + We can encrypt the resulting PSKC file with a passphrase. >>> f = tempfile.NamedTemporaryFile('w+t') -- cgit v1.2.3