def build_xibless(edition, dest='cocoa/autogen'): import xibless ensure_folder(dest) FNPAIRS = [ ('ignore_list_dialog.py', 'IgnoreListDialog_UI'), ('deletion_options.py', 'DeletionOptions_UI'), ('problem_dialog.py', 'ProblemDialog_UI'), ('directory_panel.py', 'DirectoryPanel_UI'), ('prioritize_dialog.py', 'PrioritizeDialog_UI'), ('result_window.py', 'ResultWindow_UI'), ('main_menu.py', 'MainMenu_UI'), ('preferences_panel.py', 'PreferencesPanel_UI'), ] for srcname, dstname in FNPAIRS: xibless.generate(op.join('cocoa', 'base', 'ui', srcname), op.join(dest, dstname), localizationTable='Localizable', args={'edition': edition}) if edition == 'pe': xibless.generate('cocoa/pe/ui/details_panel.py', op.join(dest, 'DetailsPanel_UI'), localizationTable='Localizable') else: xibless.generate('cocoa/base/ui/details_panel.py', op.join(dest, 'DetailsPanel_UI'), localizationTable='Localizable')
def build_xibless(edition, dest='cocoa/autogen'): import xibless ensure_folder(dest) FNPAIRS = [ ('ignore_list_dialog.py', 'IgnoreListDialog_UI'), ('deletion_options.py', 'DeletionOptions_UI'), ('problem_dialog.py', 'ProblemDialog_UI'), ('directory_panel.py', 'DirectoryPanel_UI'), ('prioritize_dialog.py', 'PrioritizeDialog_UI'), ('result_window.py', 'ResultWindow_UI'), ('main_menu.py', 'MainMenu_UI'), ('preferences_panel.py', 'PreferencesPanel_UI'), ] for srcname, dstname in FNPAIRS: xibless.generate( op.join('cocoa', 'base', 'ui', srcname), op.join(dest, dstname), localizationTable='Localizable', args={'edition': edition} ) if edition == 'pe': xibless.generate( 'cocoa/pe/ui/details_panel.py', op.join(dest, 'DetailsPanel_UI'), localizationTable='Localizable' ) else: xibless.generate( 'cocoa/base/ui/details_panel.py', op.join(dest, 'DetailsPanel_UI'), localizationTable='Localizable' )
def build_xibless(dest='cocoa/autogen'): import xibless ensure_folder(dest) FNPAIRS = [ ('ignore_list_dialog.py', 'IgnoreListDialog_UI'), ('deletion_options.py', 'DeletionOptions_UI'), ('problem_dialog.py', 'ProblemDialog_UI'), ('directory_panel.py', 'DirectoryPanel_UI'), ('prioritize_dialog.py', 'PrioritizeDialog_UI'), ('result_window.py', 'ResultWindow_UI'), ('main_menu.py', 'MainMenu_UI'), ('details_panel.py', 'DetailsPanel_UI'), ('details_panel_picture.py', 'DetailsPanelPicture_UI'), ] for srcname, dstname in FNPAIRS: xibless.generate( op.join('cocoa', 'ui', srcname), op.join(dest, dstname), localizationTable='Localizable' ) for appmode in ('standard', 'music', 'picture'): xibless.generate( op.join('cocoa', 'ui', 'preferences_panel.py'), op.join(dest, 'PreferencesPanel%s_UI' % appmode.capitalize()), localizationTable='Localizable', args={'appmode': appmode}, )
def build_cocoa(dev): print("Creating OS X app structure") app = cocoa_app() # We import this here because we don't want opened module to prevent us replacing .pyd files. from core.app import Application as MoneyGuruApp app_version = MoneyGuruApp.VERSION filereplace('cocoa/InfoTemplate.plist', 'build/Info.plist', version=app_version) app.create('build/Info.plist') print("Building localizations") build_localizations('cocoa') print("Building xibless UIs") build_cocoalib_xibless() build_xibless() print("Building Python extensions") build_cocoa_proxy_module() build_cocoa_bridging_interfaces() print("Building the cocoa layer") copy_embeddable_python_dylib('build') pydep_folder = op.join(app.resources, 'py') ensure_folder(pydep_folder) if dev: hardlink('cocoa/mg_cocoa.py', 'build/mg_cocoa.py') else: copy('cocoa/mg_cocoa.py', 'build/mg_cocoa.py') tocopy = ['core', 'hscommon', 'cocoalib/cocoa', 'objp'] copy_packages(tocopy, pydep_folder, create_links=dev) sys.path.insert(0, 'build') collect_stdlib_dependencies('build/mg_cocoa.py', pydep_folder) del sys.path[0] copy_sysconfig_files_for_embed(pydep_folder) if not dev: # Important: Don't ever run delete_files_with_pattern('*.py') on dev builds because you'll # be deleting all py files in symlinked folders. compileall.compile_dir(pydep_folder, force=True, legacy=True) delete_files_with_pattern(pydep_folder, '*.py') delete_files_with_pattern(pydep_folder, '__pycache__') print("Compiling with WAF") os.chdir('cocoa') print_and_do(cocoa_compile_command()) os.chdir('..') app.copy_executable('cocoa/build/moneyGuru') build_help() print("Copying resources and frameworks") resources = [ 'cocoa/dsa_pub.pem', 'build/mg_cocoa.py', 'build/help', 'data/example.moneyguru', ] + glob.glob('images/*') app.copy_resources(*resources, use_symlinks=dev) app.copy_frameworks( 'build/Python', 'cocoalib/Sparkle.framework', ) print("Creating the run.py file") tmpl = open('run_template_cocoa.py', 'rt').read() run_contents = tmpl.replace('{{app_path}}', app.dest) open('run.py', 'wt').write(run_contents)
def build_xibless(dest='cocoa/autogen'): import xibless ensure_folder(dest) FNPAIRS = [ ('lookup.py', 'MGLookup_UI'), ('schedule_scope_dialog.py', 'MGRecurrenceScopeDialog_UI'), ('custom_date_range_dialog.py', 'MGCustomDateRangePanel_UI'), ('account_reassign_panel.py', 'MGAccountReassignPanel_UI'), ('csv_layout_name.py', 'MGCSVLayoutNameDialog_UI'), ('csv_import_options.py', 'MGCSVImportOptions_UI'), ('import_window.py', 'MGImportWindow_UI'), ('export_panel.py', 'MGExportPanel_UI'), ('budget_panel.py', 'MGBudgetPanel_UI'), ('schedule_panel.py', 'MGSchedulePanel_UI'), ('mass_editing_panel.py', 'MGMassEditionPanel_UI'), ('transaction_panel.py', 'MGTransactionInspector_UI'), ('account_panel.py', 'MGAccountProperties_UI'), ('newtab_view.py', 'MGEmptyView_UI'), ('docprops_view.py', 'MGDocPropsView_UI'), ('cashculator_view.py', 'MGCashculatorView_UI'), ('transaction_view.py', 'MGTransactionView_UI'), ('account_view.py', 'MGAccountView_UI'), ('account_sheet_view.py', 'MGAccountSheetView_UI'), ('date_range_selector.py', 'MGDateRangeSelector_UI'), ('main_window.py', 'MGMainWindowController_UI'), ('preferences_panel.py', 'MGPreferencesPanel_UI'), ('main_menu.py', 'MGMainMenu_UI'), ] for srcname, dstname in FNPAIRS: srcpath = op.join('cocoa', 'ui', srcname) dstpath = op.join(dest, dstname) if modified_after(srcpath, dstpath + '.h'): print("Generating xibless UI %s" % srcpath) xibless.generate(srcpath, dstpath, localizationTable='Localizable')
def build_xibless(dest='cocoa/autogen'): import xibless ensure_folder(dest) FNPAIRS = [ ('lookup.py', 'MGLookup_UI'), ('schedule_scope_dialog.py', 'MGRecurrenceScopeDialog_UI'), ('custom_date_range_dialog.py', 'MGCustomDateRangePanel_UI'), ('account_reassign_panel.py', 'MGAccountReassignPanel_UI'), ('csv_layout_name.py', 'MGCSVLayoutNameDialog_UI'), ('csv_import_options.py', 'MGCSVImportOptions_UI'), ('import_window.py', 'MGImportWindow_UI'), ('export_panel.py', 'MGExportPanel_UI'), ('budget_panel.py', 'MGBudgetPanel_UI'), ('schedule_panel.py', 'MGSchedulePanel_UI'), ('mass_editing_panel.py', 'MGMassEditionPanel_UI'), ('transaction_panel.py', 'MGTransactionInspector_UI'), ('account_panel.py', 'MGAccountProperties_UI'), ('newtab_view.py', 'MGEmptyView_UI'), ('docprops_view.py', 'MGDocPropsView_UI'), ('transaction_view.py', 'MGTransactionView_UI'), ('account_view.py', 'MGAccountView_UI'), ('account_sheet_view.py', 'MGAccountSheetView_UI'), ('date_range_selector.py', 'MGDateRangeSelector_UI'), ('main_window.py', 'MGMainWindowController_UI'), ('preferences_panel.py', 'MGPreferencesPanel_UI'), ('main_menu.py', 'MGMainMenu_UI'), ] for srcname, dstname in FNPAIRS: srcpath = op.join('cocoa', 'ui', srcname) dstpath = op.join(dest, dstname) if modified_after(srcpath, dstpath + '.h'): print("Generating xibless UI %s" % srcpath) xibless.generate(srcpath, dstpath, localizationTable='Localizable')
def build_xibless(dest="cocoa/autogen"): import xibless ensure_folder(dest) FNPAIRS = [ ("lookup.py", "MGLookup_UI"), ("schedule_scope_dialog.py", "MGRecurrenceScopeDialog_UI"), ("custom_date_range_dialog.py", "MGCustomDateRangePanel_UI"), ("account_reassign_panel.py", "MGAccountReassignPanel_UI"), ("csv_layout_name.py", "MGCSVLayoutNameDialog_UI"), ("csv_import_options.py", "MGCSVImportOptions_UI"), ("import_window.py", "MGImportWindow_UI"), ("export_panel.py", "MGExportPanel_UI"), ("budget_panel.py", "MGBudgetPanel_UI"), ("schedule_panel.py", "MGSchedulePanel_UI"), ("mass_editing_panel.py", "MGMassEditionPanel_UI"), ("transaction_panel.py", "MGTransactionInspector_UI"), ("account_panel.py", "MGAccountProperties_UI"), ("newtab_view.py", "MGEmptyView_UI"), ("docprops_view.py", "MGDocPropsView_UI"), ("transaction_view.py", "MGTransactionView_UI"), ("account_view.py", "MGAccountView_UI"), ("account_sheet_view.py", "MGAccountSheetView_UI"), ("date_range_selector.py", "MGDateRangeSelector_UI"), ("main_window.py", "MGMainWindowController_UI"), ("preferences_panel.py", "MGPreferencesPanel_UI"), ("main_menu.py", "MGMainMenu_UI"), ] for srcname, dstname in FNPAIRS: srcpath = op.join("cocoa", "ui", srcname) dstpath = op.join(dest, dstname) if modified_after(srcpath, dstpath + ".h"): print("Generating xibless UI %s" % srcpath) xibless.generate(srcpath, dstpath, localizationTable="Localizable")
def build_cocoa(dev): print("Creating OS X app structure") app = cocoa_app() # We import this here because we don't want opened module to prevent us replacing .pyd files. from core.app import Application as MoneyGuruApp app_version = MoneyGuruApp.VERSION filereplace('cocoa/InfoTemplate.plist', 'build/Info.plist', version=app_version) app.create('build/Info.plist') print("Building localizations") build_localizations('cocoa') print("Building xibless UIs") build_cocoalib_xibless() build_xibless() print("Building Python extensions") build_cocoa_proxy_module() build_cocoa_bridging_interfaces() print("Building the cocoa layer") copy_embeddable_python_dylib('build') pydep_folder = op.join(app.resources, 'py') ensure_folder(pydep_folder) if dev: hardlink('cocoa/mg_cocoa.py', 'build/mg_cocoa.py') else: copy('cocoa/mg_cocoa.py', 'build/mg_cocoa.py') tocopy = ['core', 'hscommon', 'cocoalib/cocoa', 'objp', 'sgmllib'] copy_packages(tocopy, pydep_folder, create_links=dev) sys.path.insert(0, 'build') collect_stdlib_dependencies('build/mg_cocoa.py', pydep_folder) del sys.path[0] copy_sysconfig_files_for_embed(pydep_folder) if not dev: # Important: Don't ever run delete_files_with_pattern('*.py') on dev builds because you'll # be deleting all py files in symlinked folders. compileall.compile_dir(pydep_folder, force=True, legacy=True) delete_files_with_pattern(pydep_folder, '*.py') delete_files_with_pattern(pydep_folder, '__pycache__') print("Compiling PSMTabBarControl framework") os.chdir('psmtabbarcontrol') print_and_do('{0} waf configure && {0} waf && {0} waf build_framework'.format(sys.executable)) os.chdir('..') print("Compiling with WAF") os.chdir('cocoa') print_and_do(cocoa_compile_command()) os.chdir('..') app.copy_executable('cocoa/build/moneyGuru') build_help() print("Copying resources and frameworks") resources = [ 'cocoa/dsa_pub.pem', 'build/mg_cocoa.py', 'build/help', 'data/example.moneyguru', 'plugin_examples' ] + glob.glob('images/*') app.copy_resources(*resources, use_symlinks=dev) app.copy_frameworks( 'build/Python', 'cocoalib/Sparkle.framework', 'psmtabbarcontrol/PSMTabBarControl.framework' ) print("Creating the run.py file") tmpl = open('run_template_cocoa.py', 'rt').read() run_contents = tmpl.replace('{{app_path}}', app.dest) open('run.py', 'wt').write(run_contents)
def build_xibless(): import xibless ensure_folder('cocoa/autogen') xibless.generate('cocoa/ui/edit_pane.py', 'cocoa/autogen/PMEditPane_UI') xibless.generate('cocoa/ui/build_pane.py', 'cocoa/autogen/PMBuildPane_UI') xibless.generate('cocoa/ui/page_pane.py', 'cocoa/autogen/PMPageController_UI') xibless.generate('cocoa/ui/main_window.py', 'cocoa/autogen/PMMainWindow_UI') xibless.generate('cocoa/ui/main_menu.py', 'cocoa/autogen/PMMainMenu_UI')
def build_cocoa_bridging_interfaces(): print("Building Cocoa Bridging Interfaces") import objp.o2p import objp.p2o import objp.const add_to_pythonpath('cocoa') add_to_pythonpath('cocoalib') from cocoa.inter import (PyGUIObject, GUIObjectView, PyTextField, PyTable, TableView, PyColumns, ColumnsView, PyOutline, PySelectableList, SelectableListView, PyBaseApp, BaseAppView) # This createPool() business is a bit hacky, but upon importing mg_cocoa, we call # install_gettext_trans_under_cocoa() which uses proxy functions (and thus need an active # autorelease pool). If we don't do that, we get leak warnings. from cocoa import proxy proxy.createPool() from mg_cocoa import (PyPanel, PanelView, PyBaseView, PyTableWithDate, PyCompletableEdit, PyDateWidget, PyCSVImportOptions, CSVImportOptionsView, PyImportTable, PySplitTable, PyLookup, LookupView, PyDateRangeSelector, DateRangeSelectorView, PyImportWindow, ImportWindowView, PyFilterBar, FilterBarView, PyReport, ReportView, PyScheduleTable, PyBudgetTable, PyEntryTable, PyTransactionTable, PyGeneralLedgerTable, PyChart, ChartView, PyAccountPanel, PyMassEditionPanel, PyBudgetPanel, BudgetPanelView, PyCustomDateRangePanel, PyAccountReassignPanel, PyExportPanel, ExportPanelView, PyPanelWithTransaction, PanelWithTransactionView, PyTransactionPanel, PySchedulePanel, SchedulePanelView, BaseViewView, PyAccountSheetView, PyTransactionView, PyAccountView, AccountViewView, PyScheduleView, PyBudgetView, PyCashculatorView, PyGeneralLedgerView, PyDocPropsView, PyEmptyView, PyReadOnlyPluginView, PyMainWindow, MainWindowView, PyDocument, DocumentView, PyMoneyGuruApp) from mg_cocoa import PyPrintView, PySplitPrint, PyTransactionPrint, PyEntryPrint allclasses = [PyGUIObject, PyTextField, PyTable, PyColumns, PyOutline, PySelectableList, PyBaseApp, PyPanel, PyBaseView, PyTableWithDate, PyCompletableEdit, PyDateWidget, PyCSVImportOptions, PyImportTable, PySplitTable, PyLookup, PyDateRangeSelector, PyImportWindow, PyFilterBar, PyReport, PyScheduleTable, PyBudgetTable, PyEntryTable, PyTransactionTable, PyGeneralLedgerTable, PyChart, PyAccountPanel, PyMassEditionPanel, PyBudgetPanel, PyCustomDateRangePanel, PyAccountReassignPanel, PyExportPanel, PyPanelWithTransaction, PyTransactionPanel, PySchedulePanel, PyAccountSheetView, PyTransactionView, PyAccountView, PyScheduleView, PyBudgetView, PyCashculatorView, PyGeneralLedgerView, PyDocPropsView, PyEmptyView, PyReadOnlyPluginView, PyMainWindow, PyDocument, PyMoneyGuruApp] proxy.destroyPool() allclasses += [PyPrintView, PySplitPrint, PyTransactionPrint, PyEntryPrint] for class_ in allclasses: objp.o2p.generate_objc_code(class_, 'cocoa/autogen', inherit=True) allclasses = [GUIObjectView, TableView, ColumnsView, SelectableListView, BaseAppView, PanelView, CSVImportOptionsView, LookupView, DateRangeSelectorView, ImportWindowView, FilterBarView, ReportView, BudgetPanelView, ExportPanelView, PanelWithTransactionView, SchedulePanelView, BaseViewView, AccountViewView, MainWindowView, DocumentView, ChartView] clsspecs = [objp.o2p.spec_from_python_class(class_) for class_ in allclasses] objp.p2o.generate_python_proxy_code_from_clsspec(clsspecs, 'build/CocoaViews.m') py_folder = op.join(cocoa_app().resources, 'py') ensure_folder(py_folder) build_cocoa_ext('CocoaViews', py_folder, ['build/CocoaViews.m', 'build/ObjP.m']) import mg_const objp.const.generate_objc_code(mg_const, 'cocoa/autogen/PyConst.h')
def build_cocoa(dev): print("Creating OS X app structure") app = cocoa_app() # We import this here because we don't want opened module to prevent us replacing .pyd files. from core.app import Application as MoneyGuruApp app_version = MoneyGuruApp.VERSION filereplace("cocoa/InfoTemplate.plist", "build/Info.plist", version=app_version) app.create("build/Info.plist") print("Building localizations") build_localizations("cocoa") print("Building xibless UIs") build_cocoalib_xibless() build_xibless() print("Building Python extensions") build_cocoa_proxy_module() build_cocoa_bridging_interfaces() print("Building the cocoa layer") copy_embeddable_python_dylib("build") pydep_folder = op.join(app.resources, "py") ensure_folder(pydep_folder) if dev: hardlink("cocoa/mg_cocoa.py", "build/mg_cocoa.py") else: copy("cocoa/mg_cocoa.py", "build/mg_cocoa.py") tocopy = ["core", "hscommon", "cocoalib/cocoa", "objp"] copy_packages(tocopy, pydep_folder, create_links=dev) sys.path.insert(0, "build") collect_stdlib_dependencies("build/mg_cocoa.py", pydep_folder) del sys.path[0] copy_sysconfig_files_for_embed(pydep_folder) if not dev: # Important: Don't ever run delete_files_with_pattern('*.py') on dev builds because you'll # be deleting all py files in symlinked folders. compileall.compile_dir(pydep_folder, force=True, legacy=True) delete_files_with_pattern(pydep_folder, "*.py") delete_files_with_pattern(pydep_folder, "__pycache__") print("Compiling with WAF") os.chdir("cocoa") print_and_do(cocoa_compile_command()) os.chdir("..") app.copy_executable("cocoa/build/moneyGuru") build_help() print("Copying resources and frameworks") resources = ["cocoa/dsa_pub.pem", "build/mg_cocoa.py", "build/help", "data/example.moneyguru"] + glob.glob( "images/*" ) app.copy_resources(*resources, use_symlinks=dev) app.copy_frameworks("build/Python", "cocoalib/Sparkle.framework") print("Creating the run.py file") tmpl = open("run_template_cocoa.py", "rt").read() run_contents = tmpl.replace("{{app_path}}", app.dest) open("run.py", "wt").write(run_contents)
def build_cocoa_bridging_interfaces(): print("Building Cocoa Bridging Interfaces") import objp.o2p import objp.p2o import objp.const add_to_pythonpath('cocoa') add_to_pythonpath('cocoalib') from cocoa.inter import (PyGUIObject, GUIObjectView, PyTextField, PyTable, TableView, PyColumns, ColumnsView, PyOutline, PySelectableList, SelectableListView, PyBaseApp, BaseAppView) # This createPool() business is a bit hacky, but upon importing mg_cocoa, we call # install_gettext_trans_under_cocoa() which uses proxy functions (and thus need an active # autorelease pool). If we don't do that, we get leak warnings. from cocoa import proxy proxy.createPool() from mg_cocoa import ( PyPanel, PanelView, PyBaseView, PyTableWithDate, PyCompletableEdit, PyDateWidget, PyCSVImportOptions, CSVImportOptionsView, PyImportTable, PySplitTable, PyLookup, LookupView, PyDateRangeSelector, DateRangeSelectorView, PyImportWindow, ImportWindowView, PyFilterBar, FilterBarView, PyReport, ReportView, PyScheduleTable, PyBudgetTable, PyEntryTable, PyTransactionTable, PyGeneralLedgerTable, PyChart, ChartView, PyAccountPanel, PyMassEditionPanel, PyBudgetPanel, BudgetPanelView, PyCustomDateRangePanel, PyAccountReassignPanel, PyExportPanel, ExportPanelView, PyPanelWithTransaction, PanelWithTransactionView, PyTransactionPanel, PySchedulePanel, SchedulePanelView, BaseViewView, PyAccountSheetView, PyTransactionView, PyAccountView, AccountViewView, PyScheduleView, PyBudgetView, PyGeneralLedgerView, PyDocPropsView, PyPluginListView, PyEmptyView, PyReadOnlyPluginView, PyMainWindow, MainWindowView, PyDocument, DocumentView, PyMoneyGuruApp) from mg_cocoa import PyPrintView, PySplitPrint, PyTransactionPrint, PyEntryPrint allclasses = [ PyGUIObject, PyTextField, PyTable, PyColumns, PyOutline, PySelectableList, PyBaseApp, PyPanel, PyBaseView, PyTableWithDate, PyCompletableEdit, PyDateWidget, PyCSVImportOptions, PyImportTable, PySplitTable, PyLookup, PyDateRangeSelector, PyImportWindow, PyFilterBar, PyReport, PyScheduleTable, PyBudgetTable, PyEntryTable, PyTransactionTable, PyGeneralLedgerTable, PyChart, PyAccountPanel, PyMassEditionPanel, PyBudgetPanel, PyCustomDateRangePanel, PyAccountReassignPanel, PyExportPanel, PyPanelWithTransaction, PyTransactionPanel, PySchedulePanel, PyAccountSheetView, PyTransactionView, PyAccountView, PyScheduleView, PyBudgetView, PyGeneralLedgerView, PyDocPropsView, PyPluginListView, PyEmptyView, PyReadOnlyPluginView, PyMainWindow, PyDocument, PyMoneyGuruApp ] proxy.destroyPool() allclasses += [PyPrintView, PySplitPrint, PyTransactionPrint, PyEntryPrint] for class_ in allclasses: objp.o2p.generate_objc_code(class_, 'cocoa/autogen', inherit=True) allclasses = [ GUIObjectView, TableView, ColumnsView, SelectableListView, BaseAppView, PanelView, CSVImportOptionsView, LookupView, DateRangeSelectorView, ImportWindowView, FilterBarView, ReportView, BudgetPanelView, ExportPanelView, PanelWithTransactionView, SchedulePanelView, BaseViewView, AccountViewView, MainWindowView, DocumentView, ChartView ] clsspecs = [ objp.o2p.spec_from_python_class(class_) for class_ in allclasses ] objp.p2o.generate_python_proxy_code_from_clsspec(clsspecs, 'build/CocoaViews.m') py_folder = op.join(cocoa_app().resources, 'py') ensure_folder(py_folder) build_cocoa_ext('CocoaViews', py_folder, ['build/CocoaViews.m', 'build/ObjP.m']) import mg_const objp.const.generate_objc_code(mg_const, 'cocoa/autogen/PyConst.h')
def save(filename, document_id, properties, accounts, groups, transactions, schedules, budgets): def date2str(date): return date.strftime('%Y-%m-%d') def handle_newlines(s): # etree doesn't correctly save newlines. In fields that allow it, we have to escape them so # that we can restore them during load. # XXX It seems like newer version of etree do escape newlines. When we use Python 3.2, we # can probably remove this. if not s: return s return s.replace('\n', '\\n') def setattrib(attribs, attribname, value): if value: attribs[attribname] = value def write_transaction_element(parent_element, transaction): transaction_element = ET.SubElement(parent_element, 'transaction') attrib = transaction_element.attrib attrib['date'] = date2str(transaction.date) setattrib(attrib, 'description', transaction.description) setattrib(attrib, 'payee', transaction.payee) setattrib(attrib, 'checkno', transaction.checkno) setattrib(attrib, 'notes', handle_newlines(transaction.notes)) attrib['mtime'] = str(int(transaction.mtime)) for split in transaction.splits: split_element = ET.SubElement(transaction_element, 'split') attrib = split_element.attrib attrib['account'] = split.account_name attrib['amount'] = format_amount(split.amount) setattrib(attrib, 'memo', split.memo) setattrib(attrib, 'reference', split.reference) if split.reconciliation_date is not None: attrib['reconciliation_date'] = date2str( split.reconciliation_date) root = ET.Element('moneyguru-file') root.attrib['document_id'] = document_id props_element = ET.SubElement(root, 'properties') for name, value in properties.items(): if name == 'default_currency': value = value.code else: value = str(value) props_element.attrib[name] = value for group in groups: group_element = ET.SubElement(root, 'group') attrib = group_element.attrib attrib['name'] = group.name attrib['type'] = group.type for account in accounts: account_element = ET.SubElement(root, 'account') attrib = account_element.attrib attrib['name'] = account.name attrib['currency'] = account.currency.code attrib['type'] = account.type if account.group: attrib['group'] = account.group.name if account.reference is not None: attrib['reference'] = account.reference if account.account_number: attrib['account_number'] = account.account_number if account.inactive: attrib['inactive'] = 'y' if account.notes: attrib['notes'] = handle_newlines(account.notes) for transaction in transactions: write_transaction_element(root, transaction) # the functionality of the line below is untested because it's an optimisation scheduled = [s for s in schedules if s.is_alive] for recurrence in scheduled: recurrence_element = ET.SubElement(root, 'recurrence') attrib = recurrence_element.attrib attrib['type'] = recurrence.repeat_type attrib['every'] = str(recurrence.repeat_every) if recurrence.stop_date is not None: attrib['stop_date'] = date2str(recurrence.stop_date) for date, change in recurrence.date2globalchange.items(): change_element = ET.SubElement(recurrence_element, 'change') change_element.attrib['date'] = date2str(date) if change is not None: write_transaction_element(change_element, change) for date, exception in recurrence.date2exception.items(): exception_element = ET.SubElement(recurrence_element, 'exception') exception_element.attrib['date'] = date2str(date) if exception is not None: write_transaction_element(exception_element, exception) write_transaction_element(recurrence_element, recurrence.ref) for budget in budgets: budget_element = ET.SubElement(root, 'budget') attrib = budget_element.attrib attrib['account'] = budget.account.name attrib['type'] = budget.repeat_type attrib['every'] = str(budget.repeat_every) attrib['amount'] = format_amount(budget.amount) attrib['notes'] = budget.notes if budget.target is not None: attrib['target'] = budget.target.name attrib['start_date'] = date2str(budget.start_date) if budget.stop_date is not None: attrib['stop_date'] = date2str(budget.stop_date) for elem in root.getiterator(): attrib = elem.attrib for key, value in attrib.items(): attrib[key] = remove_invalid_xml(value) tree = ET.ElementTree(root) ensure_folder(op.dirname(filename)) fp = open(filename, 'wt', encoding='utf-8') fp.write('<?xml version="1.0" encoding="utf-8"?>\n') # This 'unicode' encoding thing is only available (and necessary) from Python 3.2 if sys.version_info[1] >= 2: tree.write(fp, encoding='unicode') else: tree.write(fp)
def save(filename, document_id, properties, accounts, groups, transactions, schedules, budgets): def date2str(date): return date.strftime('%Y-%m-%d') def handle_newlines(s): # etree doesn't correctly save newlines. In fields that allow it, we have to escape them so # that we can restore them during load. # XXX It seems like newer version of etree do escape newlines. When we use Python 3.2, we # can probably remove this. if not s: return s return s.replace('\n', '\\n') def setattrib(attribs, attribname, value): if value: attribs[attribname] = value def write_transaction_element(parent_element, transaction): transaction_element = ET.SubElement(parent_element, 'transaction') attrib = transaction_element.attrib attrib['date'] = date2str(transaction.date) setattrib(attrib, 'description', transaction.description) setattrib(attrib, 'payee', transaction.payee) setattrib(attrib, 'checkno', transaction.checkno) setattrib(attrib, 'notes', handle_newlines(transaction.notes)) attrib['mtime'] = str(int(transaction.mtime)) for split in transaction.splits: split_element = ET.SubElement(transaction_element, 'split') attrib = split_element.attrib attrib['account'] = split.account_name attrib['amount'] = format_amount(split.amount) setattrib(attrib, 'memo', split.memo) setattrib(attrib, 'reference', split.reference) if split.reconciliation_date is not None: attrib['reconciliation_date'] = date2str(split.reconciliation_date) root = ET.Element('moneyguru-file') root.attrib['document_id'] = document_id props_element = ET.SubElement(root, 'properties') for name, value in properties.items(): if name == 'default_currency': value = value.code else: value = str(value) props_element.attrib[name] = value for group in groups: group_element = ET.SubElement(root, 'group') attrib = group_element.attrib attrib['name'] = group.name attrib['type'] = group.type for account in accounts: account_element = ET.SubElement(root, 'account') attrib = account_element.attrib attrib['name'] = account.name attrib['currency'] = account.currency.code attrib['type'] = account.type if account.group: attrib['group'] = account.group.name if account.reference is not None: attrib['reference'] = account.reference if account.account_number: attrib['account_number'] = account.account_number if account.inactive: attrib['inactive'] = 'y' if account.notes: attrib['notes'] = handle_newlines(account.notes) for transaction in transactions: write_transaction_element(root, transaction) # the functionality of the line below is untested because it's an optimisation scheduled = [s for s in schedules if s.is_alive] for recurrence in scheduled: recurrence_element = ET.SubElement(root, 'recurrence') attrib = recurrence_element.attrib attrib['type'] = recurrence.repeat_type attrib['every'] = str(recurrence.repeat_every) if recurrence.stop_date is not None: attrib['stop_date'] = date2str(recurrence.stop_date) for date, change in recurrence.date2globalchange.items(): change_element = ET.SubElement(recurrence_element, 'change') change_element.attrib['date'] = date2str(date) if change is not None: write_transaction_element(change_element, change) for date, exception in recurrence.date2exception.items(): exception_element = ET.SubElement(recurrence_element, 'exception') exception_element.attrib['date'] = date2str(date) if exception is not None: write_transaction_element(exception_element, exception) write_transaction_element(recurrence_element, recurrence.ref) for budget in budgets: budget_element = ET.SubElement(root, 'budget') attrib = budget_element.attrib attrib['account'] = budget.account.name attrib['type'] = budget.repeat_type attrib['every'] = str(budget.repeat_every) attrib['amount'] = format_amount(budget.amount) attrib['notes'] = budget.notes if budget.target is not None: attrib['target'] = budget.target.name attrib['start_date'] = date2str(budget.start_date) if budget.stop_date is not None: attrib['stop_date'] = date2str(budget.stop_date) for elem in root.getiterator(): attrib = elem.attrib for key, value in attrib.items(): attrib[key] = remove_invalid_xml(value) tree = ET.ElementTree(root) ensure_folder(op.dirname(filename)) fp = open(filename, 'wt', encoding='utf-8') fp.write('<?xml version="1.0" encoding="utf-8"?>\n') # This 'unicode' encoding thing is only available (and necessary) from Python 3.2 if sys.version_info[1] >= 2: tree.write(fp, encoding='unicode') else: tree.write(fp)