diff --git a/rattail/sil.py b/rattail/sil.py index 3d4d4cff830f83b9b576849da59ac6ae20b24ce2..729f515e5bd2a177b7d4ba557c3cccdf11a8c55b 100644 --- a/rattail/sil.py +++ b/rattail/sil.py @@ -30,6 +30,9 @@ Please see the `Standard Interchange Language Specifications for more information. """ +import datetime +from decimal import Decimal + import edbob import rattail @@ -42,26 +45,30 @@ def val(value): If ``value`` is ``None``, an empty string is returned. - If it is an integer, it is converted directly to a string (i.e. not - quoted). + If it is an ``int`` or ``decimal.Decimal`` instance, it is converted + directly to a string (i.e. not quoted). + + If it is a ``datetime.date`` instance, it will be formatted as ``'%Y%j'``. + + If it is a ``datetime.time`` instance, it will be formatted as ``'%H%M'``. - If it is a string, but contains only digits, it is wrapped in single - quotes. If the string contains any apostrophes or commas, it is wrapped in - single quotes with the apostrophes escaped. Otherwise it is is returned - as-is without quoting. + Otherwise, it is converted to a string if necessary, and quoted with + apostrophes escaped. """ if value is None: return '' if isinstance(value, int): return str(value) + if isinstance(value, Decimal): + return str(value) + if isinstance(value, datetime.date): + return value.strftime('%Y%j') + if isinstance(value, datetime.time): + return value.strftime('%H%M') if not isinstance(value, basestring): value = str(value) - if value.isdigit(): - return "'%s'" % value - if value.find("'") > -1 or value.find(',') > -1: - return "'%s'" % value.replace("'", "''") - return value + return "'%s'" % value.replace("'", "''") def consume_batch_id(): @@ -92,9 +99,8 @@ def write_batch_header(fileobj, H03='RATAIL', **kwargs): correspond to the SIL specification for the Batch Header Dictionary. If you do not override ``H03`` (Source Identifier), then Rattail will - provide defaults for ``H02`` (Batch Identifier) and ``H20`` (Software - Revision) - that is, unless you've supplied those yourself.. If you do - override ``H03`` then you must also provide values for ``H02`` and ``H20``. + provide a default value for ``H20`` (Software Revision) - that is, unless + you've supplied it yourself. **Batch Header Dictionary:** @@ -128,54 +134,88 @@ def write_batch_header(fileobj, H03='RATAIL', **kwargs): Consult the SIL Specification for more information. """ - H02 = kwargs.get('H02') - H20 = kwargs.get('H20') - if H03 == 'RATAIL': - if not H02: - H02 = consume_batch_id() - if not H20: - H20 = rattail.__version__[:4] - kw = kwargs - fileobj.write('INSERT INTO HEADER_DCT VALUES\n(' + ','.join(( - val(kw.get('H01')), - val(H02), - val(H03), - val(kw.get('H04')), - val(kw.get('H05')), - val(kw.get('H06')), - val(kw.get('H07')), - val(kw.get('H08')), - val(kw.get('H09')), - val(kw.get('H10')), - val(kw.get('H11')), - val(kw.get('H12')), - val(kw.get('H13')), - val(kw.get('H14')), - val(kw.get('H15')), - val(kw.get('H16')), - val(kw.get('H17')), - val(kw.get('H18')), - val(kw.get('H19')), - val(H20), - val(kw.get('H21')), - val(kw.get('H22')), - val(kw.get('H23')), - )) + ');\n') - - -def write_row(fileobj, row, last=False): + + # Provide default for H20 if batch origin is 'RATAIL'. + H20 = kw.get('H20') + if H03 == 'RATAIL' and H20 is None: + H20 = rattail.__version__[:4] + + # Don't quote H09 if special "immediate" value. + H09 = kw.get('H09') + if H09 != '0000000': + H09 = val(H09) + + # Don't quote H10 if special "immediate" value. + H10 = kw.get('H10') + if H10 != '0000': + H10 = val(H10) + + row = [ + val(kw.get('H01')), + val(kw.get('H02')), + val(H03), + val(kw.get('H04')), + val(kw.get('H05')), + val(kw.get('H06')), + val(kw.get('H07')), + val(kw.get('H08')), + H09, + H10, + val(kw.get('H11')), + val(kw.get('H12')), + val(kw.get('H13')), + val(kw.get('H14')), + val(kw.get('H15')), + val(kw.get('H16')), + val(kw.get('H17')), + val(kw.get('H18')), + val(kw.get('H19')), + val(H20), + val(kw.get('H21')), + val(kw.get('H22')), + val(kw.get('H23')), + ] + + fileobj.write('INSERT INTO HEADER_DCT VALUES\n') + write_row(fileobj, row, quote=False, last=True) + fileobj.write('\n') + + +def write_row(fileobj, row, quote=True, last=False): """ Writes a SIL row string to ``fileobj``. - ``row`` should be a sequence of values. If ``last`` is ``True``, then - ``';'`` will be used as the statement terminator; otherwise ``','`` is - used. + ``row`` should be a sequence of values. + + If ``quote`` is ``True``, each value in ``row`` will be ran through the + :func:`val()` function before being written. If it is ``False``, the + values are written as-is. + + If ``last`` is ``True``, then ``';'`` will be used as the statement + terminator; otherwise ``','`` is used. """ terminator = ';' if last else ',' - fileobj.write('(' + ','.join([val(x) for x in row]) + ')' - + terminator + '\n') + if quote: + row = [val(x) for x in row] + fileobj.write('(' + ','.join(row) + ')' + terminator + '\n') + + +def write_rows(fileobj, rows): + """ + Writes a set of SIL row strings to ``fileobj``. + + ``rows`` should be a sequence of sequences, each of which should be + suitable for use with :func:`write_row()`. + + (This funcion primarily exists to handle the mundane task of setting the + ``last`` flag when calling :func:`write_row()`.) + """ + + last = len(rows) - 1 + for i, row in enumerate(rows): + write_row(fileobj, row, last=i == last) # # from pkg_resources import iter_entry_points