Diff for /ZSQLExtend/importFMPXML.py between versions 1.5 and 1.25

version 1.5, 2007/01/09 14:00:59 version 1.25, 2008/07/02 11:58:45
Line 4 Line 4
 import string  import string
 import logging  import logging
 import sys  import sys
   import types
   import time
   
 from xml import sax  from xml import sax
 from amara import saxtools  from amara import saxtools
   
 try:  try:
     import psycopg2 as psycopg      import psycopg2 as psycopg
       import psycopg2.extensions
       # switch to unicode
       psycopg2.extensions.register_type(psycopg2.extensions.UNICODE)
     psyco = 2      psyco = 2
 except:  except:
     import psycopg      import psycopg
Line 17  except: Line 22  except:
   
 fm_ns = 'http://www.filemaker.com/fmpxmlresult'  fm_ns = 'http://www.filemaker.com/fmpxmlresult'
   
   version_string = "V0.6.1 ROC .2008"
   
   def unicodify(text, withNone=False):
       """decode str (utf-8 or latin-1 representation) into unicode object"""
       if withNone and text is None:
           return None
       if not text:
           return u""
       if isinstance(text, str):
           try:
               return text.decode('utf-8')
           except:
               return text.decode('latin-1')
       else:
           return text
   
   def utf8ify(text, withNone=False):
       """encode unicode object or string into byte string in utf-8 representation"""
       if withNone and text is None:
           return None
       if not text:
           return ""
       if isinstance(text, unicode):
           return text.encode('utf-8')
       else:
           return text
   
 def getTextFromNode(nodename):  def getTextFromNode(nodename):
     """get the cdata content of a node"""      """get the cdata content of a node"""
     if nodename is None:      if nodename is None:
Line 36  def sql_quote(v): Line 68  def sql_quote(v):
             v=string.join(string.split(v,dkey),quote_dict[dkey])              v=string.join(string.split(v,dkey),quote_dict[dkey])
     return "'%s'"%v      return "'%s'"%v
   
 def SimpleSearch(curs,query, args=None):  def SimpleSearch(curs,query, args=None, ascii=False):
     """execute sql query and return data"""      """execute sql query and return data"""
     logging.debug("executing: "+query)      #logger.debug("executing: "+query)
     if psyco == 1:      if ascii:
         query = query.encode("UTF-8")          # encode all in UTF-8
           query = utf8ify(query)
           if args is not None:
               encargs = []
               for a in args:
                   encargs.append(utf8ify(a, withNone=True))
               
               args = encargs
   
     curs.execute(query, args)      curs.execute(query, args)
     logging.debug("sql done")      #logger.debug("sql done")
     try:      try:
         return curs.fetchall()          return curs.fetchall()
     except:      except:
         return None          return None
   
   
   class TableColumn:
       """simple type for storing sql column name and type"""
       
       def __init__(self, name, type=None):
           #print "new tablecolumn(%s,%s)"%(name, type)
           self.name = name
           self.type = type
           
       def getName(self):
           return self.name
       
       def getType(self):
           if self.type is not None:
               return self.type
           else:
               return "text"
   
       def __str__(self):
           return self.name
       
   
 class xml_handler:  class xml_handler:
       def __init__(self,options):
           """SAX handler to import FileMaker XML file (FMPXMLRESULT format) into the table.
           @param options: dict of options
           @param options.dsn: database connection string
           @param options.table: name of the table the xml shall be imported into
           @param options.filename: xmlfile filename
           @param options.update_fields: (optional) list of fields to update; default is to create all fields
           @param options.id_field: (optional) field which uniquely identifies an entry for updating purposes.
           @param options.sync_mode: (optional) really synchronise, i.e. delete entries not in XML file
           @param options.lc_names: (optional) lower case and clean up field names from XML
           @param options.keep_fields: (optional) don't add fields to SQL database
           @param options.ascii_db: (optional) assume ascii encoding in db
           @param options.replace_table: (optional) delete and re-insert data
           @param options.backup_table: (optional) create backup of old table (breaks indices)
           @param options.use_logger_instance: (optional) use this instance of a logger
           """
           
           # set up logger
           if hasattr(options, 'use_logger_instance'):
               self.logger = options.use_logger_instance
           else:
               self.logger = logging.getLogger('db.import.fmpxml')
   
           
     def __init__(self,dsn,table,update_fields=None,id_field=None,sync_mode=False):  
         '''  
         SAX handler to import FileMaker XML file (FMPXMLRESULT format) into the table.  
         @param dsn: database connection string  
         @param table: name of the table the xml shall be imported into  
         @param filename: xmlfile filename  
         @param update_fields: (optional) list of fields to update; default is to create all fields  
         @param id_field: (optional) field which uniquely identifies an entry for updating purposes.  
         @param sync_mode: (optional) really synchronise, i.e. delete entries not in XML file  
         '''  
         # set up parser          # set up parser
         self.event = None          self.event = None
         self.top_dispatcher = {           self.top_dispatcher = { 
             (saxtools.START_ELEMENT, fm_ns, u'METADATA'):               (saxtools.START_ELEMENT, fm_ns, u'METADATA'): 
             self.handle_meta_fields,              self.handle_meta_fields,
             (saxtools.START_ELEMENT, fm_ns, u'RESULTSET'):               (saxtools.START_ELEMENT, fm_ns, u'RESULTSET'): 
             self.handle_data,              self.handle_data_fields,
             }              }
                   
         # connect database          # connect database
         self.dbCon = psycopg.connect(dsn)          self.dbCon = psycopg.connect(options.dsn)
           logging.debug("DB encoding: %s"%getattr(self.dbCon, 'encoding', 'UNKNOWN'))
         self.db = self.dbCon.cursor()          self.db = self.dbCon.cursor()
         assert self.db, "AIIEE no db cursor for %s!!"%dsn          assert self.db, "AIIEE no db cursor for %s!!"%options.dsn
           
         logging.debug("dsn: "+repr(dsn))          self.table = getattr(options,"table",None)
         logging.debug("table: "+repr(table))          self.update_fields = getattr(options,"update_fields",None)
         logging.debug("update_fields: "+repr(update_fields))          self.id_field = getattr(options,"id_field",None)
         logging.debug("id_field: "+repr(id_field))          self.sync_mode = getattr(options,"sync_mode",None)
         logging.debug("sync_mode: "+repr(sync_mode))          self.lc_names = getattr(options,"lc_names",None)
           self.keep_fields = getattr(options,"keep_fields",None)
         self.table = table          self.ascii_db = getattr(options,"ascii_db",None)
         self.update_fields = update_fields          self.replace_table = getattr(options,"replace_table",None)
         self.id_field = id_field          self.backup_table = getattr(options,"backup_table",None)
         self.sync_mode = sync_mode          self.read_before_update = getattr(options,"read_before_update",None)
   
           self.logger.debug("dsn: "+repr(getattr(options,"dsn",None)))
           self.logger.debug("table: "+repr(self.table))
           self.logger.debug("update_fields: "+repr(self.update_fields))
           self.logger.debug("id_field: "+repr(self.id_field))
           self.logger.debug("sync_mode: "+repr(self.sync_mode))
           self.logger.debug("lc_names: "+repr(self.lc_names))
           self.logger.debug("keep_fields: "+repr(self.keep_fields))
           self.logger.debug("ascii_db: "+repr(self.ascii_db))
           self.logger.debug("replace_table: "+repr(self.replace_table))
           self.logger.debug("backup_table: "+repr(self.backup_table))
           self.logger.debug("read_before_update: "+repr(self.read_before_update))
                   
         self.dbIDs = {}          self.dbIDs = {}
         self.rowcnt = 0          self.rowcnt = 0
                                   
         if id_field is not None:          if self.id_field is not None:
             # prepare a list of ids for sync mode              # prepare a list of ids for sync mode
             qstr="select %s from %s"%(id_field,table)              qstr="select %s from %s"%(self.id_field,self.table)
             for id in SimpleSearch(self.db, qstr):              for id in SimpleSearch(self.db, qstr):
                 # value 0: not updated                  # value 0: not updated
                 self.dbIDs[id[0]] = 0;                  self.dbIDs[id[0]] = 0;
                 self.rowcnt += 1                  self.rowcnt += 1
                                   
             logging.info("%d entries in DB to sync"%self.rowcnt)              self.logger.info("%d entries in DB to sync"%self.rowcnt)
                   
         self.fieldNames = []          # names of fields in XML file
           self.xml_field_names = []
           # map XML field names to SQL field names
           self.xml_field_map = {}
           # and vice versa
           self.sql_field_map = {}
                   
         return          return
   
Line 111  class xml_handler: Line 202  class xml_handler:
             }              }
         #First round through the generator corresponds to the          #First round through the generator corresponds to the
         #start element event          #start element event
         logging.debug("START METADATA")          self.logger.info("reading metadata...")
           self.logger.debug("START METADATA")
         yield None          yield None
           
         #delegate is a generator that handles all the events "within"          #delegate is a generator that handles all the events "within"
Line 123  class xml_handler: Line 215  class xml_handler:
             yield None              yield None
                   
         #Element closed. Wrap up          #Element closed. Wrap up
         logging.debug("END METADATA")          self.logger.debug("END METADATA")
           
           # rename table for backup
           if self.backup_table:
               self.orig_table = self.table
               self.tmp_table = self.table + "_tmp"
               backup_name = "%s_%s"%(self.table,time.strftime('%Y_%m_%d_%H_%M_%S'))
               
               # remove old temp table
               qstr = "DROP TABLE %s"%(self.tmp_table)
               try:
                   self.db.execute(qstr)
               except:
                   pass
               
               self.dbCon.commit()
              
               if self.id_field:
                   # sync mode -- copy backup table, update current table 
                   self.logger.info("copy table %s to %s"%(self.table,backup_name))
                   qstr = "CREATE TABLE %s AS (SELECT * FROM %s)"%(backup_name,self.table)
   
               else:
                   # replace mode -- create empty tmp table, insert into tmp table
                   self.table = self.tmp_table
                   self.logger.info("create empty table %s"%(self.table))
                   qstr = "CREATE TABLE %s AS (SELECT * FROM %s WHERE 1=0)"%(self.table,self.orig_table)
               
               self.db.execute(qstr)
               self.dbCon.commit()
           
           # delete data from table for replace
           if self.replace_table:
               self.logger.info("delete data from table %s"%(self.table))
               qstr = "TRUNCATE TABLE %s"%(self.table)
               self.db.execute(qstr)
               self.dbCon.commit()
              
           # try to match date style with XML
           self.db.execute("set datestyle to 'german'")
           
           #self.logger.debug("xml-fieldnames:"+repr(self.xml_field_names))
           # get list of fields and types of db table
           qstr="select attname, format_type(pg_attribute.atttypid, pg_attribute.atttypmod) from pg_attribute, pg_class where attrelid = pg_class.oid and pg_attribute.attnum > 0 and relname = '%s'"
           self.sql_fields={}
           for f in SimpleSearch(self.db, qstr%self.table):
               fn = f[0]
               ft = f[1]
               #print "SQL fields: %s (%s)"%(n,t)
               self.sql_fields[fn] = TableColumn(fn,ft)
           
           # translate id_field (SQL-name) to XML-name
           self.xml_id = self.sql_field_map.get(self.id_field, None)
           # get type of id_field
           if self.id_field:
               self.id_type = self.sql_fields[self.id_field].getType()
           else:
               self.id_type = None
           
           # check fields to update
         if self.update_fields is None:          if self.update_fields is None:
               if self.keep_fields:
                   # update all existing fields from sql (when they are in the xml file)
                   self.update_fields = {}
                   for f in self.sql_fields.keys():
                       if self.sql_field_map.has_key(f):
                           xf = self.sql_field_map[f]
                           self.update_fields[f] = self.xml_field_map[xf]
   
               else:
             # update all fields              # update all fields
             self.update_fields = self.fieldNames                  if self.lc_names:
                       # create dict with sql names
                       self.update_fields = {}
                       for f in self.xml_field_map.values():
                           self.update_fields[f.getName()] = f
                           
                   else:
                       self.update_fields = self.xml_field_map
                                   
           # and translate to list of xml fields
           if self.lc_names:
               self.xml_update_list = [self.sql_field_map[x] for x in self.update_fields]
           else:
               self.xml_update_list = self.update_fields.keys()
   
           if not self.keep_fields:
               # adjust db table to fields in XML and update_fields
               for f in self.xml_field_map.values():
                   self.logger.debug("sync-fieldname: %s"%f.getName())
                   sf = self.sql_fields.get(f.getName(), None)
                   uf = self.update_fields.get(f.getName(), None)
                   if sf is not None:
                       # name in db -- check type
                       if f.getType() != sf.getType():
                           self.logger.debug("field %s has different type (%s vs %s)"%(f,f.getType(),sf.getType()))
                   elif uf is not None:
                       # add field to table
                       fn = uf.getName()
                       ft = uf.getType()
                       qstr="alter table %s add \"%s\" %s"%(self.table,fn,ft)
                       self.logger.info("db add field:"+qstr)
                       
                       if self.ascii_db and type(qstr)==types.UnicodeType:
                           qstr=qstr.encode('utf-8')
                   
         logging.debug("xml-fieldnames:"+repr(self.fieldNames))  
         # get list of fields in db table  
         qstr="""select attname from pg_attribute, pg_class where attrelid = pg_class.oid and relname = '%s'"""  
         columns=[x[0] for x in SimpleSearch(self.db, qstr%self.table)]  
           
         # adjust db table to fields in XML and fieldlist  
         for fieldName in self.fieldNames:  
             logging.debug("db-fieldname:"+repr(fieldName))                       
             if (fieldName not in columns) and (fieldName in self.update_fields):  
                 qstr="alter table %s add %s %s"%(self.table,fieldName,'text')  
                 logging.info("db add field:"+qstr)  
                 self.db.execute(qstr)                  self.db.execute(qstr)
                 self.dbCon.commit()                  self.dbCon.commit()
                       # add field to field list
                       self.sql_fields[fn] = TableColumn(fn, ft)
   
         # prepare sql statements for update          # prepare sql statements for update (do not update id_field)
         setStr=string.join(["%s = %%s"%f for f in self.update_fields], ', ')          setStr=string.join(["\"%s\" = %%s"%self.xml_field_map[f] for f in self.xml_update_list if f != self.xml_id], ', ')
         self.updQuery="UPDATE %s SET %s WHERE %s = %%s"%(self.table,setStr,self.id_field)          self.updQuery="UPDATE %s SET %s WHERE \"%s\" = %%s"%(self.table,setStr,self.id_field)
           # and select (for update check)
           selStr=string.join([self.xml_field_map[f].getName() for f in self.xml_update_list if f != self.xml_id], ', ')
           self.selQuery="SELECT %s FROM %s WHERE \"%s\" = %%s"%(selStr,self.table,self.id_field)
         # and insert          # and insert
         fields=string.join(self.update_fields, ',')          fields=string.join(["\"%s\""%self.xml_field_map[x].getName() for x in self.xml_update_list], ',')
         values=string.join(['%s' for f in self.update_fields], ',')          values=string.join(['%s' for f in self.xml_update_list], ',')
         self.addQuery="INSERT INTO %s (%s) VALUES (%s)"%(self.table,fields,values)          self.addQuery="INSERT INTO %s (%s) VALUES (%s)"%(self.table,fields,values)
         #print "upQ: ", self.updQuery          self.logger.debug("update-query: "+self.updQuery)
         #print "adQ: ", self.addQuery          self.logger.debug("sel-query: "+self.selQuery)
                                   self.logger.debug("add-query: "+self.addQuery)
         return          return
   
     def handle_meta_field(self, end_condition):      def handle_meta_field(self, end_condition):
         name = self.params.get((None, u'NAME'))          name = self.params.get((None, u'NAME'))
         yield None          yield None
         #Element closed.  Wrap up          #Element closed.  Wrap up
         self.fieldNames.append(name)          if self.lc_names:
         logging.debug("FIELD name: "+name)              # clean name
               sqlname = name.replace(" ","_").lower() 
           else:
               sqlname = name
           self.xml_field_names.append(name)
           # map to sql name and default text type
           self.xml_field_map[name] = TableColumn(sqlname, 'text')
           self.sql_field_map[sqlname] = name
           self.logger.debug("FIELD name: "+name)
         return          return
   
     def handle_data(self, end_condition):      def handle_data_fields(self, end_condition):
         dispatcher = {          dispatcher = {
             (saxtools.START_ELEMENT, fm_ns, u'ROW'):              (saxtools.START_ELEMENT, fm_ns, u'ROW'):
             self.handle_row,              self.handle_row,
             }              }
         #First round through the generator corresponds to the          #First round through the generator corresponds to the
         #start element event          #start element event
         logging.debug("START RESULTSET")          self.logger.info("reading data...")
           self.logger.debug("START RESULTSET")
         self.rowcnt = 0          self.rowcnt = 0
         yield None          yield None
           
Line 182  class xml_handler: Line 378  class xml_handler:
             yield None              yield None
                   
         #Element closed.  Wrap up          #Element closed.  Wrap up
         logging.debug("END RESULTSET")          self.logger.debug("END RESULTSET")
         self.dbCon.commit()          self.dbCon.commit()
                   
         if self.sync_mode:          if self.sync_mode:
             # delete unmatched entries in db              # delete unmatched entries in db
               self.logger.info("deleting unmatched rows from db")
               delQuery = "DELETE FROM %s WHERE \"%s\" = %%s"%(self.table,self.id_field)
             for id in self.dbIDs.keys():              for id in self.dbIDs.keys():
                 # find all not-updated fields                  # find all not-updated fields
                 if self.dbIDs[id] == 0:                  if self.dbIDs[id] == 0:
                     logging.info(" delete:"+id)                      self.logger.info(" delete:"+id)
                     qstr = "DELETE FROM %s WHERE %%s = '%%s'"%self.table                      SimpleSearch(self.db, delQuery, [id], ascii=self.ascii_db)
                     SimpleSearch(self.db, qstr, (self.id_field,id))  
                                           
                 elif self.dbIDs[id] > 1:                  elif self.dbIDs[id] > 1:
                     logging.info(" sync:"+"id used more than once?"+id)                      self.logger.info(" sync: ID %s used more than once?"%id)
               
               self.dbCon.commit()
                           
           # reinstate backup tables
           if self.backup_table and not self.id_field:
               backup_name = "%s_%s"%(self.orig_table,time.strftime('%Y_%m_%d_%H_%M_%S'))
               self.logger.info("rename backup table %s to %s"%(self.orig_table,backup_name))
               qstr = "ALTER TABLE %s RENAME TO %s"%(self.orig_table,backup_name)
               self.db.execute(qstr)
               self.logger.info("rename working table %s to %s"%(self.table,self.orig_table))
               qstr = "ALTER TABLE %s RENAME TO %s"%(self.table,self.orig_table)
               self.db.execute(qstr)
             self.dbCon.commit()              self.dbCon.commit()
                   
         return          return
Line 206  class xml_handler: Line 414  class xml_handler:
             (saxtools.START_ELEMENT, fm_ns, u'COL'):              (saxtools.START_ELEMENT, fm_ns, u'COL'):
             self.handle_col,              self.handle_col,
             }              }
         logging.debug("START ROW")          self.logger.debug("START ROW")
         self.dataSet = {}          self.xml_data = {}
         self.colIdx = 0          self.colIdx = 0
         yield None          yield None
           
Line 220  class xml_handler: Line 428  class xml_handler:
             yield None              yield None
                   
         #Element closed.  Wrap up          #Element closed.  Wrap up
         logging.debug("END ROW")          self.logger.debug("END ROW")
         self.rowcnt += 1          self.rowcnt += 1
         # process collected row data          # process collected row data
         update=False          update=False
         id_val=''          id_val=''
         # synchronize by id_field          # synchronize by id_field
         if self.id_field:          if self.id_field:
             id_val=self.dataSet[self.id_field]              if self.id_type == 'integer':
                   id_val = int(self.xml_data[self.xml_id])
               else:
                   id_val = self.xml_data[self.xml_id]
                   
             if id_val in self.dbIDs:              if id_val in self.dbIDs:
                 self.dbIDs[id_val] += 1                  self.dbIDs[id_val] += 1
                 update=True                  update=True
                   
           # collect all values
           args = []
           for fn in self.xml_update_list:
               # do not update id_field
               if update and fn == self.xml_id:
                   continue
               
               f = self.xml_field_map[fn]
               val = self.xml_data[fn]
               type = self.sql_fields[f.getName()].getType()
               if type == "date" and len(val) == 0: 
                   # empty date field
                   val = None
                   
               elif type == "integer" and len(val) == 0: 
                   # empty int field
                   val = None
                   
               args.append(val)
                       
         if update:          if update:
             # update existing row (by id_field)              # update existing row (by id_field)
             #setvals=[]              if self.read_before_update:
             #for fieldName in self.update_fields:                  # read data
             #    setvals.append("%s = %s"%(fieldName,sql_quote(self.dataSet[fieldName])))                  self.logger.debug("update check: %s = %s"%(id_val, args))
             #setStr=string.join(setvals, ',')                  oldrow = SimpleSearch(self.db, self.selQuery, [id_val], ascii=self.ascii_db)
             id_val=self.dataSet[self.id_field]                  #i = 0
             #qstr="UPDATE %s SET %s WHERE %s = '%s' "%(self.table,setStr,self.id_field,id_val)                  #for v in oldrow[0]:
             args = [self.dataSet[f] for f in self.update_fields]                  #    logging.debug("v: %s = %s (%s)"%(v,args[i],v==args[i]))
             args.append(id_val)                  #    i += 1
             SimpleSearch(self.db, self.updQuery, args)                  if tuple(oldrow[0]) != tuple(args):
             logging.debug("update: %s"%id_val)                      # data has changed -- update
                       self.logger.debug("really update: %s = %s"%(id_val, args))
                       args.append(id_val) # last arg is id
                       SimpleSearch(self.db, self.updQuery, args, ascii=self.ascii_db)
                       
               else:
                   # always update
                   self.logger.debug("update: %s = %s"%(id_val, args))
                   args.append(id_val) # last arg is id
                   SimpleSearch(self.db, self.updQuery, args, ascii=self.ascii_db)
   
         else:          else:
             # create new row              # create new row
             #fields=string.join(update_fields, ',')              self.logger.debug("insert: %s"%args)
             #values=string.join([" %s "%sql_quote(self.dataSet[x]) for x in self.update_fields], ',')              SimpleSearch(self.db, self.addQuery, args, ascii=self.ascii_db)
             #qstr="INSERT INTO %s (%s) VALUES (%s)"%(self.table,fields,self.values)  
             args = [self.dataSet[f] for f in self.update_fields]          #self.logger.info(" row:"+"%d (%s)"%(self.rowcnt,id_val))
             SimpleSearch(self.db, self.addQuery, args)          if (self.rowcnt % 100) == 0:
             logging.debug("add: %s"%self.dataSet.get(self.id_field, rowcnt))              self.logger.info(" row:"+"%d (id:%s)"%(self.rowcnt,id_val))
   
         #logging.info(" row:"+"%d (%s)"%(self.rowcnt,id_val))  
         if (self.rowcnt % 10) == 0:  
             logging.info(" row:"+"%d (%s)"%(self.rowcnt,id_val))  
             self.dbCon.commit()              self.dbCon.commit()
                           
         return          return
Line 289  class xml_handler: Line 527  class xml_handler:
                 content += self.params                  content += self.params
             yield None              yield None
         #Element closed.  Wrap up          #Element closed.  Wrap up
         field = self.fieldNames[self.colIdx]          fn = self.xml_field_names[self.colIdx]
         self.dataSet[field] = content          self.xml_data[fn] = content
         #print "  DATA(", field, ") ", repr(content)  
         return          return
   
   
   def importFMPXML(options):
       """import FileMaker XML file (FMPXMLRESULT format) into the table.     
           @param options: dict of options
           @param options.dsn: database connection string
           @param options.table: name of the table the xml shall be imported into
           @param options.filename: xmlfile filename
           @param options.update_fields: (optional) list of fields to update; default is to create all fields
           @param options.id_field: (optional) field which uniquely identifies an entry for updating purposes.
           @param options.sync_mode: (optional) really synchronise, i.e. delete entries not in XML file
           @param options.lc_names: (optional) lower case and clean up field names from XML
           @param options.keep_fields: (optional) don't add fields to SQL database
           @param options.ascii_db: (optional) assume ascii encoding in db
           @param options.replace_table: (optional) delete and re-insert data
           @param options.backup_table: (optional) create backup of old table
           """
           
       if getattr(options,'update_fields',None):
           uf = {}
           for f in options.update_fields.split(','):
               if f.find(':') > 0:
                   (n,t) = f.split(':')
               else:
                   n = f
                   t = None
               uf[n] = TableColumn(n,t)
   
           options.update_fields = uf
   
       if getattr(options,'id_field',None) and getattr(options,'replace_table',None):
           logging.error("ABORT: sorry, you can't do both sync (id_field) and replace")
           return
           
       parser = sax.make_parser()
       #The "consumer" is our own handler
       consumer = xml_handler(options)
       #Initialize Tenorsax with handler
       handler = saxtools.tenorsax(consumer)
       #Resulting tenorsax instance is the SAX handler 
       parser.setContentHandler(handler)
       parser.setFeature(sax.handler.feature_namespaces, 1)
       parser.parse(options.filename)  
   
 ##  
 ## public static int main()  
 ##  
   
   if __name__ == "__main__":
 from optparse import OptionParser  from optparse import OptionParser
   
 opars = OptionParser()  opars = OptionParser()
Line 316  opars.add_option("-t", "--table", Line 590  opars.add_option("-t", "--table",
                  help="database table name")                   help="database table name")
 opars.add_option("--fields", default=None,   opars.add_option("--fields", default=None, 
                  dest="update_fields",                    dest="update_fields", 
                  help="list of fields to update (comma separated)", metavar="LIST")                       help="list of fields to update (comma separated, sql-names)", metavar="LIST")
 opars.add_option("--id-field", default=None,   opars.add_option("--id-field", default=None, 
                  dest="id_field",                    dest="id_field", 
                  help="name of id field for synchronisation (only appends data otherwise)", metavar="NAME")                       help="name of id field for synchronisation (only appends data otherwise, sql-name)", metavar="NAME")
 opars.add_option("--sync-mode", default=False, action="store_true",       opars.add_option("--sync", "--sync-mode", default=False, action="store_true", 
                  dest="sync_mode",                    dest="sync_mode", 
                  help="do full sync based on id field (remove unmatched fields from db)")                   help="do full sync based on id field (remove unmatched fields from db)")
       opars.add_option("--lc-names", default=False, action="store_true", 
                        dest="lc_names", 
                        help="clean and lower case field names from XML")
       opars.add_option("--keep-fields", default=False, action="store_true", 
                        dest="keep_fields", 
                        help="don't add fields from XML to SQL table")
       opars.add_option("--ascii-db", default=False, action="store_true", 
                        dest="ascii_db", 
                        help="the SQL database stores ASCII instead of unicode")
       opars.add_option("--replace", default=False, action="store_true", 
                        dest="replace_table", 
                        help="replace table i.e. delete and re-insert data")
       opars.add_option("--backup", default=False, action="store_true", 
                        dest="backup_table", 
                        help="create backup of old table")
       opars.add_option("--read-before-update", default=False, action="store_true", 
                        dest="read_before_update", 
                        help="read all data to check if it really changed")
 opars.add_option("-d", "--debug", default=False, action="store_true",   opars.add_option("-d", "--debug", default=False, action="store_true", 
                  dest="debug",                    dest="debug", 
                  help="debug mode (more output)")                   help="debug mode (more output)")
Line 330  opars.add_option("-d", "--debug", defaul Line 622  opars.add_option("-d", "--debug", defaul
 (options, args) = opars.parse_args()  (options, args) = opars.parse_args()
   
 if len(sys.argv) < 2 or options.filename is None or options.dsn is None:  if len(sys.argv) < 2 or options.filename is None or options.dsn is None:
           print "importFMPXML "+version_string
     opars.print_help()      opars.print_help()
     sys.exit(1)      sys.exit(1)
   
Line 342  logging.basicConfig(level=loglevel, Line 635  logging.basicConfig(level=loglevel,
                     format='%(asctime)s %(levelname)s %(message)s',                      format='%(asctime)s %(levelname)s %(message)s',
                     datefmt='%H:%M:%S')                      datefmt='%H:%M:%S')
   
 update_fields = None      importFMPXML(options)
   
 if options.update_fields:  
     update_fields = [string.strip(s) for s in options.update_fields.split(',')]  
   
 parser = sax.make_parser()  
 #The "consumer" is our own handler  
 consumer = xml_handler(dsn=options.dsn,table=options.table,  
              update_fields=update_fields,id_field=options.id_field,  
              sync_mode=options.sync_mode)  
 #Initialize Tenorsax with handler  
 handler = saxtools.tenorsax(consumer)  
 #Resulting tenorsax instance is the SAX handler   
 parser.setContentHandler(handler)  
 parser.setFeature(sax.handler.feature_namespaces, 1)  
 parser.parse(options.filename)    
   
   
 print "DONE!"  

Removed from v.1.5  
changed lines
  Added in v.1.25


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>