diff --git a/rattail/sil.py b/rattail/sil.py index 60bd7647916074f2f68281db83a34e609ad23aa4..b0675fb1e4081160624379bda4e149f3673ce50f 100644 --- a/rattail/sil.py +++ b/rattail/sil.py @@ -23,72 +23,224 @@ ################################################################################ """ -``rattail.sil`` -- SIL Interface +``rattail.sil`` -- Standard Interchange Language + +Please see the `Standard Interchange Language Specifications +`_ +for more information. """ -# from pkg_resources import iter_entry_points +import edbob -# import rattail -# from rattail.batch import make_batch, RattailBatchTerminal -from rattail.batches import RattailBatchTerminal +import rattail -# _junctions = None +def val(value): + """ + Returns a string version of ``value``, suitable for inclusion within a data + row of a SIL batch. The conversion is done as follows: + If ``value`` is ``None``, an empty string is returned. -# class SILError(Exception): -# """ -# Base class for SIL errors. -# """ + If it is an integer, it is converted directly to a string (i.e. not + quoted). -# pass - + 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. + """ -# class ElementRequiredError(SILError): -# """ -# Raised when a batch import or export is attempted, but the element list -# supplied is missing a required element. -# """ + if value is None: + return '' + if isinstance(value, int): + return str(value) + 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 -# def __init__(self, required, using): -# self.required = required -# self.using = using -# def __str__(self): -# return "The element list supplied is missing required element '%s': %s" % ( -# self.required, self.using) +def consume_batch_id(): + """ + Returns the next available batch identifier, incrementing the number to + preserve uniqueness. + """ + + config = edbob.AppConfigParser('rattail') + config_path = config.get_user_file('rattail.conf', create=True) + config.read(config_path) + + batch_id = config.get('rattail.sil', 'next_batch_id', default='') + if not batch_id.isdigit(): + batch_id = '1' + batch_id = int(batch_id) + + config.set('rattail.sil', 'next_batch_id', str(batch_id + 1)) + config_file = open(config_path, 'w') + config.write(config_file) + config_file.close() + return '%08u' % batch_id -def default_display(field): +def write_batch_header(fileobj, H01='HM', H03='RATAIL', **kwargs): """ - Returns the default UI display value for a SIL field, according to the - Rattail field map. + Writes a SIL batch header string to ``fileobj``. All keyword arguments + 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``. + + **Batch Header Dictionary:** + + ==== ==== ==== =========== + Name Type Size Description + ==== ==== ==== =========== + H01 CHAR 2 Batch Type + H02 CHAR 8 Batch Identifier + H03 CHAR 6 Source Identifier + H04 CHAR 6 Destination Identifier + H05 CHAR 12 Audit File Name + H06 CHAR 12 Response File Name + H07 DATE 7 Origin Date + H08 TIME 4 Origin Time + H09 DATE 7 Execution (Apply) Date + H10 DATE 4 Execution (Apply) Time + H11 DATE 7 Purge Date + H12 CHAR 6 Action Type + H13 CHAR 50 Batch Description + H14 CHAR 30 User Defined + H15 CHAR 30 User Defined + H16 CHAR 30 User Defined + H17 NUMBER 1 Warning Level + H18 NUMBER 5 Maximum Error Count + H19 CHAR 7 SIL Level/Revision + H20 CHAR 4 Software Revision + H21 CHAR 50 Primary Key + H22 CHAR 512 System Specific Command + H23 CHAR 8 Dictionary Revision + + Consult the SIL Specification for more information. """ - return RattailBatchTerminal.fieldmap_user[field] + 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(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): + """ + 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. + """ -# def get_available_junctions(): -# """ -# Returns a dictionary of available :class:`rattail.BatchJunction` classes, -# keyed by entry point name. -# """ + terminator = ';' if last else ',' + fileobj.write('(' + ','.join([val(x) for x in row]) + ')' + + terminator + '\n') -# global _junctions -# if _junctions is None: -# _junctions = {} -# for entry_point in iter_entry_points('rattail.batch_junctions'): -# _junctions[entry_point.name] = entry_point.load() -# return _junctions +# # from pkg_resources import iter_entry_points -# def get_junction_display(name): +# # import rattail +# # from rattail.batch import make_batch, RattailBatchTerminal +# from rattail.batches import RattailBatchTerminal + + +# # _junctions = None + + +# # class SILError(Exception): +# # """ +# # Base class for SIL errors. +# # """ + +# # pass + + +# # class ElementRequiredError(SILError): +# # """ +# # Raised when a batch import or export is attempted, but the element list +# # supplied is missing a required element. +# # """ + +# # def __init__(self, required, using): +# # self.required = required +# # self.using = using + +# # def __str__(self): +# # return "The element list supplied is missing required element '%s': %s" % ( +# # self.required, self.using) + + +# def default_display(field): # """ -# Returns the ``display`` value for a registered -# :class:`rattail.BatchJunction` class, given its ``name``. +# Returns the default UI display value for a SIL field, according to the +# Rattail field map. # """ -# juncs = get_available_junctions() -# if name in juncs: -# return juncs[name].display -# return None +# return RattailBatchTerminal.fieldmap_user[field] + + +# # def get_available_junctions(): +# # """ +# # Returns a dictionary of available :class:`rattail.BatchJunction` classes, +# # keyed by entry point name. +# # """ + +# # global _junctions +# # if _junctions is None: +# # _junctions = {} +# # for entry_point in iter_entry_points('rattail.batch_junctions'): +# # _junctions[entry_point.name] = entry_point.load() +# # return _junctions + + +# # def get_junction_display(name): +# # """ +# # Returns the ``display`` value for a registered +# # :class:`rattail.BatchJunction` class, given its ``name``. +# # """ + +# # juncs = get_available_junctions() +# # if name in juncs: +# # return juncs[name].display +# # return None