From: W. Trevor King Date: Thu, 30 Apr 2009 13:23:32 +0000 (-0400) Subject: Added --inventory, --door-warning to chem_db.py's command-line interface. X-Git-Tag: 0.4~1 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=cd4b2afb1ef26565a691707da229d310ff13d36a;p=chemdb.git Added --inventory, --door-warning to chem_db.py's command-line interface. Also their associated options --valid-record, --sort-field, and --pdf-title. The door_warning format doesn't have an obvious title location, so I need to think about that some more... Also added --audit, to check for troublesome entries. --- diff --git a/DEPENDENCIES b/DEPENDENCIES index c71e2c5..b03675b 100644 --- a/DEPENDENCIES +++ b/DEPENDENCIES @@ -18,6 +18,7 @@ stat string sys time +types EXTERNAL MODULES diff --git a/chem_db.py b/chem_db.py index cb9af64..7d74ca7 100755 --- a/chem_db.py +++ b/chem_db.py @@ -9,6 +9,7 @@ import re import os import os.path import time +import types def valid_CASno(cas_string, debug=False): """ @@ -71,11 +72,11 @@ class MSDS_manager (object) : else : os.mkdir(self.dir) def basepath(self, id) : - assert type(id) == type(1), 'id must be an integer, not %s (%s)' \ + assert type(id) == types.IntType, 'id must be an integer, not %s (%s)' \ % (type(id), str(id)) return os.path.join(self.dir, "%d" % id) def local_basepath(self, id) : # for symbolic links - assert type(id) == type(1), 'id must be an integer, not %s (%s)' \ + assert type(id) == types.IntType, 'id must be an integer, not %s (%s)' \ % (type(id), str(id)) return "./%d" % id def MIME_ext(self, mime) : @@ -171,13 +172,21 @@ class docgen (object) : path = os.path.join('./docs/', target_file) os.system('cp ./docs/main.pdf %s' % path) return path - def inventory(self, namewidth='a') : - "Create a pdf list of all currently owned chemicals." + def inventory(self, title=None, + namewidth='a', sort_field='db_id', + valid_record=lambda r: r['Disposed'] == '') : + """Create a pdf list of all maching chemicals. The default is to + match all currently owned chemicals. Matching chemicals can be sorted + by any field (defaults to 'ID').""" + if title == None: + title == 'Inventory' pp = db_pretty_printer(self.db) active_ids = [] for record in self.db.records() : - if record['Disposed'] == '' : # get ids for chemicals we still have + if valid_record(record) : # get ids for matching chemicals active_ids.append(record['db_id']) + active_ids.sort(cmp=lambda a,b: cmp(self.db.record(a)[sort_field], + self.db.record(b)[sort_field])) active_fields = ['ID', 'Name', 'Amount', 'H', 'F', 'R', 'O', 'M', 'C', 'T'] width = {} @@ -210,6 +219,7 @@ class docgen (object) : self._latex_safe(record['C']), self._latex_safe(record['T'])) string += "\\end{longtable}\n" + print >> file('./docs/inventory_title.tex', 'w'), title print >> file('./docs/inventory_data.tex', 'w'), string ## alter main.tex to point to the inventory template. self._set_main_target('inventory_template') @@ -344,44 +354,65 @@ def close_IOfiles(ifilename=None, ifile=stdin, ofile.close() -if __name__ == "__main__" : +if __name__ == '__main__' : from optparse import OptionParser - parser = OptionParser(usage="usage: %prog [options]", version="%prog 0.1") + parser = OptionParser(usage='usage: %prog [options]', version='%prog 0.1') - parser.add_option('-f', '--input-file', dest="ifilename", - help="Read input from FILE (default stdin)", - type='string', metavar="FILE") - parser.add_option('-o', '--output-file', dest="ofilename", - help="Write output to FILE (default stdout)", - type='string', metavar="FILE") - parser.add_option('-d', '--delimiter', dest="FS", # field seperator + parser.add_option('-f', '--input-file', dest='ifilename', + help='Read input from FILE (default stdin)', + type='string', metavar='FILE') + parser.add_option('-o', '--output-file', dest='ofilename', + help='Write output to FILE (default stdout)', + type='string', metavar='FILE') + parser.add_option('-d', '--delimiter', dest='FS', # field seperator help="Set field delimiter (default '%default')", - type='string', metavar="DELIM", default='\t') - parser.add_option('-p', '--print-fields', dest="print_fields", - help="Only print certain fields (e.g. 0,3,4,2)", - type='string', metavar="FIELDS") - parser.add_option('-r', '--print-records', dest="print_records", - help="Only print certain records (e.g. 0:3)", - type='string', metavar="RECORDS") - parser.add_option('-w', '--column-width', dest="width", - help="Set column width for short-format output.", - type='string', metavar="WIDTH") - parser.add_option('-L', '--long-format', dest="long_format", - help="Print long format (several lines per record)", + type='string', metavar='DELIM', default='\t') + parser.add_option('-p', '--print-fields', dest='print_fields', + help='Only print certain fields (e.g. 0,3,4,2)', + type='string', metavar='FIELDS') + parser.add_option('-r', '--print-records', dest='print_records', + help='Only print certain records (e.g. 0:3)', + type='string', metavar='RECORDS') + parser.add_option('-w', '--column-width', dest='width', + help='Set column width for short-format output.', + type='string', metavar='WIDTH') + parser.add_option('-L', '--long-format', dest='long_format', + help='Print long format (several lines per record)', action='store_true', default=False) - parser.add_option('-l', '--short-format', dest="long_format", - help="Print short format (default) (one lines per record)", + parser.add_option('-l', '--short-format', dest='long_format', + help='Print short format (default) (one lines per record)', action='store_false', default=False) - parser.add_option('-t', '--test', dest="test", - help="Run docutils tests on db.py", - action="store_true", default=False) - parser.add_option('-V', '--validate', dest="validate", - help="Validate CAS#s (no other output)", - action="store_true", default=False) - parser.add_option('-v', '--verbose', dest="verbose", - help="Print lots of debugging information", - action="store_true", default=False) + parser.add_option('--valid-record', dest='valid_record', + help="Select fields where True == lambda r : eval(EXPRESSION). default '%default'", + type='string', metavar='EXPRESSION', default="r['Disposed'] == ''") + parser.add_option('--sort-field', dest='sort_field', + help="Sort matching records by FIELD (defauly '%default')", + type='string', metavar='FIELD', default='db_id') + parser.add_option('--pdf-title', dest='pdf_title', + help='Override the default PDF title', + type='string', metavar='TITLE') + parser.add_option('--inventory', dest='inventory', + help='Output a PDF inventory of matching records', + action='store_true', default=False) + parser.add_option('--door-warning', dest='door_warning', + help='Output a PDF door warning of matching records', + action='store_true', default=False) + parser.add_option('-t', '--test', dest='test', + help='Run docutils tests on db.py', + action='store_true', default=False) + parser.add_option('--list-locations', dest='locations', + help='List all currently used locations (no other output)', + action='store_true', default=False) + parser.add_option('-V', '--validate', dest='validate', + help='Validate CAS#s (no other output)', + action='store_true', default=False) + parser.add_option('-A', '--audit', dest='audit', + help='Search for troublesome entries (no other output)', + action='store_true', default=False) + parser.add_option('-v', '--verbose', dest='verbose', + help='Print lots of debugging information', + action='store_true', default=False) (options, args) = parser.parse_args() parser.destroy() @@ -391,6 +422,19 @@ if __name__ == "__main__" : if options.test : _test() + elif options.locations : + db = text_db(filename=None) + pp = db_pretty_printer(db) + + # read in and parse the file + db._parse(ifile.read()) + + locations = [] + for record in db.records(): + if len(record['Location']) > 0 and record['Location'] not in locations: + locations.append(record['Location']) + locations.sort() + print >> ofile, '\n'.join(locations) elif options.validate : db = text_db(filename=None) pp = db_pretty_printer(db) @@ -416,7 +460,52 @@ if __name__ == "__main__" : if not valid : print >> ofile, "in record %s: %s" % (record['ID'], record['Name']) #pp.full_record_string(record) + elif options.audit : + db = text_db(filename=None) + pp = db_pretty_printer(db) + + # read in and parse the file + db._parse(ifile.read()) + + for record in db.records(): + # check for extra spaces + for key,value in record.items(): + if type(value) in types.StringTypes and value.strip() != value: + print >> ofile, "Extra whitespace for %s - %s field %s : '%s'" % (record['ID'], record['Name'], key, value) + # make sure we know the location of all current chemicals + if len(record['Disposed']) == 0 and len(record['Location']) == 0: + print >> ofile, "Misplaced record: %s - %s" % (record['ID'], record['Name']) + elif options.inventory: + db = text_db(filename=None) + pp = db_pretty_printer(db) + # read in and parse the file + db._parse(ifile.read()) + + dgen = docgen(db) + def valid_record(r) : + return eval(options.valid_record, # expression + {'__builtins__':None}, # globals + {'r':r}) # locals + path = dgen.inventory(title=options.pdf_title, + namewidth=40, + sort_field=options.sort_field, + valid_record=valid_record) + print >> ofile, '\n', path + elif options.door_warning: + db = text_db(filename=None) + pp = db_pretty_printer(db) + + # read in and parse the file + db._parse(ifile.read()) + + dgen = docgen(db) + def valid_record(r) : + return eval(options.valid_record, # expression + {'__builtins__':None}, # globals + {'r':r}) # locals + path = dgen.door_warning(valid_record=valid_record) + print >> ofile, '\n', path else : db = text_db(filename=None) @@ -428,10 +517,9 @@ if __name__ == "__main__" : string = pp.full_record_string_id(id) else : # pythonize the width option - if (options.width != None - and options.width != 'a' - and len(options.width.split(':')) == 1 - ) : + if options.width == None or options.width == 'a': + width = options.width + elif len(options.width.split(':')) == 1 : width = int(options.width) elif len(options.width.split(':')) > 1 : width = {} diff --git a/docs/README b/docs/README index 338876a..b4df7b0 100644 --- a/docs/README +++ b/docs/README @@ -5,3 +5,6 @@ documents from the data. The `mp' directory contains metapost source and assorted infrastructure for building and previewing the graphics. +Note that due to my wimpy Makefile rules, you may need to make a door +warning before making an inventory, in order to avoid + make: *** No rule to make target `mp/NFPA.mp', needed by `mp/NFPA.1'. Stop. diff --git a/docs/inventory_template.tex b/docs/inventory_template.tex index 77f5e77..c856a19 100644 --- a/docs/inventory_template.tex +++ b/docs/inventory_template.tex @@ -42,7 +42,7 @@ \begin{document} \begin{center} -{\headfont Inventory}\\ +{\headfont \input{inventory_title}}\\ \contfont Generated \today\ by chem\_db.py\\ \vskip 10pt