version 1.27, 2008/09/05 19:05:57
|
version 1.33, 2011/06/23 09:14:53
|
Line 6 import logging
|
Line 6 import logging
|
import sys |
import sys |
import types |
import types |
import time |
import time |
|
import re |
|
|
from xml import sax |
from xml import sax |
from amara import saxtools |
from amara import saxtools |
Line 22 except:
|
Line 23 except:
|
|
|
fm_ns = 'http://www.filemaker.com/fmpxmlresult' |
fm_ns = 'http://www.filemaker.com/fmpxmlresult' |
|
|
version_string = "V0.6.1 ROC 2.7.2008" |
version_string = "V0.6.7 ROC 21.6.2011" |
|
|
def unicodify(text, withNone=False): |
def unicodify(text, withNone=False): |
"""decode str (utf-8 or latin-1 representation) into unicode object""" |
"""decode str (utf-8 or latin-1 representation) into unicode object""" |
Line 68 def sql_quote(v):
|
Line 69 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 sqlName(s, lc=True, more=''): |
|
"""returns restricted ASCII-only version of string""" |
|
if s is None: |
|
return "" |
|
|
|
# remove ' |
|
s = s.replace("'","") |
|
# all else -> "_" |
|
s = re.sub('[^A-Za-z0-9_'+more+']','_',s) |
|
if lc: |
|
return s.lower() |
|
|
|
return s |
|
|
def SimpleSearch(curs,query, args=None, ascii=False): |
def SimpleSearch(curs,query, args=None, ascii=False): |
"""execute sql query and return data""" |
"""execute sql query and return data""" |
#logger.debug("executing: "+query) |
#logger.debug("executing: "+query) |
Line 160 class xml_handler:
|
Line 175 class xml_handler:
|
self.replace_table = getattr(options,"replace_table",None) |
self.replace_table = getattr(options,"replace_table",None) |
self.backup_table = getattr(options,"backup_table",None) |
self.backup_table = getattr(options,"backup_table",None) |
self.read_before_update = getattr(options,"read_before_update",None) |
self.read_before_update = getattr(options,"read_before_update",None) |
|
self.debug_data = getattr(options,"debug_data",None) |
|
|
self.logger.debug("dsn: "+repr(getattr(options,"dsn",None))) |
self.logger.debug("dsn: "+repr(getattr(options,"dsn",None))) |
self.logger.debug("table: "+repr(self.table)) |
self.logger.debug("table: "+repr(self.table)) |
Line 172 class xml_handler:
|
Line 188 class xml_handler:
|
self.logger.debug("replace_table: "+repr(self.replace_table)) |
self.logger.debug("replace_table: "+repr(self.replace_table)) |
self.logger.debug("backup_table: "+repr(self.backup_table)) |
self.logger.debug("backup_table: "+repr(self.backup_table)) |
self.logger.debug("read_before_update: "+repr(self.read_before_update)) |
self.logger.debug("read_before_update: "+repr(self.read_before_update)) |
|
self.logger.debug("debug_data: "+repr(self.debug_data)) |
|
|
self.dbIDs = {} |
self.dbIDs = {} |
self.rowcnt = 0 |
self.rowcnt = 0 |
Line 203 class xml_handler:
|
Line 220 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 |
self.logger.info("reading metadata...") |
self.logger.info("reading metadata...") |
|
if self.debug_data: |
self.logger.debug("START METADATA") |
self.logger.debug("START METADATA") |
yield None |
yield None |
|
|
Line 215 class xml_handler:
|
Line 233 class xml_handler:
|
yield None |
yield None |
|
|
#Element closed. Wrap up |
#Element closed. Wrap up |
|
if self.debug_data: |
self.logger.debug("END METADATA") |
self.logger.debug("END METADATA") |
|
|
# rename table for backup |
# rename table for backup |
Line 347 class xml_handler:
|
Line 366 class xml_handler:
|
#Element closed. Wrap up |
#Element closed. Wrap up |
if self.lc_names: |
if self.lc_names: |
# clean name |
# clean name |
sqlname = name.replace(" ","_").lower() |
sqlname = sqlName(name) |
else: |
else: |
sqlname = name |
sqlname = name |
self.xml_field_names.append(name) |
self.xml_field_names.append(name) |
Line 365 class xml_handler:
|
Line 384 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 |
self.logger.info("reading data...") |
self.logger.info("reading data...") |
|
if self.debug_data: |
self.logger.debug("START RESULTSET") |
self.logger.debug("START RESULTSET") |
self.rowcnt = 0 |
self.rowcnt = 0 |
yield None |
yield None |
Line 378 class xml_handler:
|
Line 398 class xml_handler:
|
yield None |
yield None |
|
|
#Element closed. Wrap up |
#Element closed. Wrap up |
|
if self.debug_data: |
self.logger.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 |
|
if self.rowcnt > 0: |
self.logger.info("deleting unmatched rows from db") |
self.logger.info("deleting unmatched rows from db") |
delQuery = "DELETE FROM %s WHERE \"%s\" = %%s"%(self.table,self.id_field) |
delQuery = "DELETE FROM %s WHERE \"%s\" = %%s"%(self.table,self.id_field) |
for id in self.dbIDs.keys(): |
for id in self.dbIDs.keys(): |
Line 396 class xml_handler:
|
Line 418 class xml_handler:
|
|
|
self.dbCon.commit() |
self.dbCon.commit() |
|
|
|
else: |
|
# safety in case we had an empty file |
|
self.logger.warning("no rows read! not deleting unmatched rows!") |
|
|
# reinstate backup tables |
# reinstate backup tables |
if self.backup_table and not self.id_field: |
if self.backup_table and not self.id_field: |
backup_name = "%s_%s"%(self.orig_table,time.strftime('%Y_%m_%d_%H_%M_%S')) |
backup_name = "%s_%s"%(self.orig_table,time.strftime('%Y_%m_%d_%H_%M_%S')) |
Line 407 class xml_handler:
|
Line 433 class xml_handler:
|
self.db.execute(qstr) |
self.db.execute(qstr) |
self.dbCon.commit() |
self.dbCon.commit() |
|
|
|
self.logger.info("Done (%s rows)"%self.rowcnt) |
return |
return |
|
|
def handle_row(self, end_condition): |
def handle_row(self, end_condition): |
Line 414 class xml_handler:
|
Line 441 class xml_handler:
|
(saxtools.START_ELEMENT, fm_ns, u'COL'): |
(saxtools.START_ELEMENT, fm_ns, u'COL'): |
self.handle_col, |
self.handle_col, |
} |
} |
|
if self.debug_data: |
self.logger.debug("START ROW") |
self.logger.debug("START ROW") |
self.xml_data = {} |
self.xml_data = {} |
self.colIdx = 0 |
self.colIdx = 0 |
Line 428 class xml_handler:
|
Line 456 class xml_handler:
|
yield None |
yield None |
|
|
#Element closed. Wrap up |
#Element closed. Wrap up |
|
if self.debug_data: |
self.logger.debug("END ROW") |
self.logger.debug("END ROW") |
self.rowcnt += 1 |
self.rowcnt += 1 |
# process collected row data |
# process collected row data |
Line 436 class xml_handler:
|
Line 465 class xml_handler:
|
# synchronize by id_field |
# synchronize by id_field |
if self.id_field: |
if self.id_field: |
if self.id_type == 'integer': |
if self.id_type == 'integer': |
|
try: |
id_val = int(self.xml_data[self.xml_id]) |
id_val = int(self.xml_data[self.xml_id]) |
|
except: |
|
pass |
else: |
else: |
id_val = self.xml_data[self.xml_id] |
id_val = self.xml_data[self.xml_id] |
|
|
|
if not id_val: |
|
# abort update |
|
self.logger.error("ERROR: unable to sync! emtpy id in row %s"%self.rowcnt) |
|
return |
|
|
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 |
Line 454 class xml_handler:
|
Line 491 class xml_handler:
|
f = self.xml_field_map[fn] |
f = self.xml_field_map[fn] |
val = self.xml_data[fn] |
val = self.xml_data[fn] |
type = self.sql_fields[f.getName()].getType() |
type = self.sql_fields[f.getName()].getType() |
if type == "date" and len(val) == 0: |
if type == "date" and len(val.strip()) == 0: |
# empty date field |
# empty date field |
val = None |
val = None |
|
|
Line 468 class xml_handler:
|
Line 505 class xml_handler:
|
# update existing row (by id_field) |
# update existing row (by id_field) |
if self.read_before_update: |
if self.read_before_update: |
# read data |
# read data |
|
if self.debug_data: |
self.logger.debug("update check: %s = %s"%(id_val, args)) |
self.logger.debug("update check: %s = %s"%(id_val, args)) |
oldrow = SimpleSearch(self.db, self.selQuery, [id_val], ascii=self.ascii_db) |
oldrow = SimpleSearch(self.db, self.selQuery, [id_val], ascii=self.ascii_db) |
#i = 0 |
#i = 0 |
Line 476 class xml_handler:
|
Line 514 class xml_handler:
|
# i += 1 |
# i += 1 |
if tuple(oldrow[0]) != tuple(args): |
if tuple(oldrow[0]) != tuple(args): |
# data has changed -- update |
# data has changed -- update |
|
if self.debug_data: |
self.logger.debug("really update: %s = %s"%(id_val, args)) |
self.logger.debug("really update: %s = %s"%(id_val, args)) |
args.append(id_val) # last arg is id |
args.append(id_val) # last arg is id |
SimpleSearch(self.db, self.updQuery, args, ascii=self.ascii_db) |
SimpleSearch(self.db, self.updQuery, args, ascii=self.ascii_db) |
|
|
else: |
else: |
# always update |
# always update |
|
if self.debug_data: |
self.logger.debug("update: %s = %s"%(id_val, args)) |
self.logger.debug("update: %s = %s"%(id_val, args)) |
args.append(id_val) # last arg is id |
args.append(id_val) # last arg is id |
SimpleSearch(self.db, self.updQuery, args, ascii=self.ascii_db) |
SimpleSearch(self.db, self.updQuery, args, ascii=self.ascii_db) |
|
|
else: |
else: |
# create new row |
# create new row |
|
if self.debug_data: |
self.logger.debug("insert: %s"%args) |
self.logger.debug("insert: %s"%args) |
SimpleSearch(self.db, self.addQuery, args, ascii=self.ascii_db) |
SimpleSearch(self.db, self.addQuery, args, ascii=self.ascii_db) |
|
|
Line 618 if __name__ == "__main__":
|
Line 659 if __name__ == "__main__":
|
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)") |
|
opars.add_option("--debug-data", default=False, action="store_true", |
|
dest="debug_data", |
|
help="debug mode for data (even more output)") |
|
|
(options, args) = opars.parse_args() |
(options, args) = opars.parse_args() |
|
|