# 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%s>' % (name, dayh, name))
elif tid == 'ReportColumn' and cid == 'Periods':
# normal output
out('<%s>%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%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()