# Export as XML # Based on 'Save as Script' Copyright 2005 by Alexander V. Christensen # Changes Copyright 2008 by Brian C. Christensen """ Save the database as an XML (.xml) file. """ # This file is part of GanttPV. # # GanttPV is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # GanttPV is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with GanttPV; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # 050720 - first version, based on a debugging script by Brian Christensen # 050801 - added support for database aliases # 050901 - placed generated code in a function; added save prompt; included the current date in the signature # 060722 - added IgnoreColumns, FirstColumns, and LastColumns # 080213 - Brian - first version of XML export based on Save as Script by Alexander V. Christensen # 080214 - Brian - added 'rows', 'columns', and 'column' (column would be a good place to add type info) # 080225 - Brian - added export date and time to database element import os import datetime # import xml.sax.saxutils IgnoreTables = ['TableAlias'] # these tables will be omitted IgnoreColumns = ["ServerKey", 'OwnerKey', 'EditKey', 'ViewKey', 'WebEditKey', 'WebViewKey'] # these columns will be omitted FirstColumns = ['ID', 'Name', 'Label'] # these columns will be listed before the rest LastColumns = ['zzStatus'] # these columns will be listed after the rest signature = 'GanttPV XML Database Format v0.1; generated by "' + os.path.basename(thisfile) + '" on ' + Data.GetToday() preface = """""" dToday = datetime.datetime.today() # returns datetime object for now # date = dToday.strftime("%y-%m-%d %H:%M") # convert to display format date = dToday.strftime("%y-%m-%d") # convert to display format time = dToday.strftime("%H:%M") # convert to display format header = """ """ % (date, time) footer = """ """ def WriteXML(output): print >> output, preface.encode( "utf-8" ) def out(s): print >> output, s.encode( "utf-8" ) def xml_escape(s): if isinstance(s, basestring): s = s.replace('&', '&') s = s.replace('<', '<') s = s.replace('>', '>') s = s.replace('"', '"') # needed for attributes # s = xml.sax.saxutils.escape(s) return s out(header) db = Data.Database # add sequence numbers to report rows and report columns col_xref = {} row_xref = {} reports = db['Report'] for reportid, reportrecord in reports.iteritems(): columns = Data.GetColumnList(reportid) for i, cid in enumerate(columns): col_xref[(reportid, cid)] = i + 1 rows = Data.GetRowList(reportid) for i, rid in enumerate(rows): row_xref[(reportid, rid)] = i + 1 out('') tables = [ x for x in db['NextID'].keys() if not x.startswith('_') and x not in IgnoreTables] tables.sort() for tid in tables: t = db.get(tid) or {} # should never default records = t.items() records.sort() cmap = {} try: for rid, r in records: for cid in r: if cid not in cmap: cmap[cid] = None cmap['ID'] except: # skip empty or non-standard tables continue for cid in IgnoreColumns: if cid in cmap: del cmap[cid] first = [] for cid in FirstColumns: if cid in cmap: del cmap[cid] first.append(cid) last = [] for cid in LastColumns: if cid in cmap: del cmap[cid] last.append(cid) others = [ x for x in cmap.keys() if not x.startswith('_')] others.sort() columns = first + others + last out('\n' % xml_escape(tid)) out('') for c in columns: if tid == 'Other' and c == 'WeekHours': for i in range(7): name = 'Day%dHours' % (i + 1) out('%s' % name) else: out('%s' % xml_escape(c)) out('\n') out('') for rid, r in records: if r.get('zzStatus') == 'deleted': continue if tid == 'ReportRow': report_id = r.get('ReportID') or 0 seq = row_xref.get((report_id, rid)) or 0 if not seq: # ganttpv leaves orphaned columns in data (should be flagged as deleted) continue out('' % (rid, report_id, seq)) elif tid == 'ReportColumn': report_id = r.get('ReportID') or 0 seq = col_xref.get((report_id, rid)) or 0 if not seq: # ganttpv leaves orphaned rows in data (should be flagged as deleted) continue out('' % (rid, report_id, seq)) else: out('' % rid) for cid in columns: v = r.get(cid) if v is None: continue elif tid == 'Other' and cid == 'WeekHours': # per discussion w/ alex # monday is day 1; Using the name we plan to use in future GanttPV release for i, dayh in enumerate(v): # out('%s' % (i + 1, dayh)) # possible alternative? name = 'Day%dHours' % (i + 1) out('<%s>%s' % (name, dayh, name)) elif tid == 'ReportColumn' and cid == 'Periods': # normal output out('<%s>%s' % (xml_escape(cid), xml_escape(v), xml_escape(cid))) # period list - useful in processing export in xml first_period = r.get('FirstDate') or Data.TodayString() period = Data.StringToDate(first_period) ctid = r.get('ColumnTypeID') ct = db['ColumnType'].get(ctid) or {} size = 1 # should get the real size (# of years) unit = (ct.get('Name') or 'Day/x').split('/')[0] period = Data.PeriodStart(period, unit, 1) dates = [] for i in range(v): dates.append(Data.DateToString(period)) period = Data.AddPeriod(period, unit, size) out('%s' % (','.join(dates))) else: out('<%s>%s' % (xml_escape(cid), xml_escape(v), xml_escape(cid))) out('\n') out('') out('
\n') out('
\n') out('') for k, v in db['TableAlias'][1].iteritems(): if k == 'ID': continue out('%s' % (xml_escape(k), xml_escape(v))) out('\n') out(footer) if Data.FileName: name = os.path.basename(Data.FileName) suffix = '.ganttpv' if name.endswith(suffix): name = name[:-len(suffix)] + '.xml' else: name = 'Untitled.xml' path = wx.FileSelector('Save database', '', name, '', 'XML file (*.xml)|*.xml', wx.SAVE | wx.OVERWRITE_PROMPT) if path: output = open(path, 'w') WriteXML(output) output.close()