[Formed-commits] r327 - in trunk: . formed/formed formed/formed/plugins/export
scm-commit@wald.intevation.org
scm-commit at wald.intevation.org
Fri Apr 24 14:29:33 CEST 2009
Author: teichmann
Date: 2009-04-24 14:29:32 +0200 (Fri, 24 Apr 2009)
New Revision: 327
Added:
trunk/formed/formed/plugins/export/rg_sql.py
Modified:
trunk/ChangeLog
trunk/formed/formed/config.py
Log:
New plug-in to generate tree structures in database.
Modified: trunk/ChangeLog
===================================================================
--- trunk/ChangeLog 2009-04-16 15:18:22 UTC (rev 326)
+++ trunk/ChangeLog 2009-04-24 12:29:32 UTC (rev 327)
@@ -1,3 +1,10 @@
+2009-04-24 Sascha L. Teichmann <teichmann at intevation.de>
+
+ * formed/formed/plugins/export/rg_sql.py: new plug-in to generate
+ tree structure functions in database.
+
+ * formed/formed/config.py: load plug-in.
+
2009-04-16 Sascha L. Teichmann <teichmann at intevation.de>
* formed/formed/model/data.py: Added 'digest' attribute to
Modified: trunk/formed/formed/config.py
===================================================================
--- trunk/formed/formed/config.py 2009-04-16 15:18:22 UTC (rev 326)
+++ trunk/formed/formed/config.py 2009-04-24 12:29:32 UTC (rev 327)
@@ -33,7 +33,8 @@
# ExportStoredProcedureFilter, \
# ExportViewsFilter
-from formed.plugins.export.sql import ExportAsSQLFilter
+from formed.plugins.export.sql import ExportAsSQLFilter
+from formed.plugins.export.rg_sql import ExportAsRepeatGroupSQLFilter
from formed.plugins.export.data import DataImportFilter, DataExportFilter
@@ -57,6 +58,7 @@
ExportDiffAsHTMLFilter(),
ExportAsTypeDict(),
ExportAsSQLFilter(),
+ ExportAsRepeatGroupSQLFilter(),
# ExportStoredProcedureFilter(),
# ExportViewsFilter(),
ExportAsXMLSchema(),
Added: trunk/formed/formed/plugins/export/rg_sql.py
===================================================================
--- trunk/formed/formed/plugins/export/rg_sql.py 2009-04-16 15:18:22 UTC (rev 326)
+++ trunk/formed/formed/plugins/export/rg_sql.py 2009-04-24 12:29:32 UTC (rev 327)
@@ -0,0 +1,283 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2007 by Intevation GmbH
+#
+# FormEd is free software under the terms of the
+# GNU General Public License Version 3. See file
+# LICENSE coming with the source of FormEd for details.
+#
+# Authors:
+# Sascha L. Teichmann <teichmann at intevation.de>
+#
+
+from formed.plugins.ui.controls import FileDialogFilter
+
+from formed.model.misc import checkMode, ModeChecker
+
+import formed.model.data as data
+from formed.model.nodecomponents import Node, Leaf
+
+from string import Template
+
+import codecs
+
+SQL_TEMPLATE = Template(
+'''
+--
+-- THIS IS MACHINE GENERATED BY FormEd. BE CAREFUL WITH MANUAL EDITING
+--
+BEGIN;
+
+-- need to be postgres because of untrusted language
+
+-- DROP FUNCTION get_case_structure(int4);
+CREATE OR REPLACE FUNCTION get_case_structure(case_id int4) RETURNS TEXT AS $$
+
+class Node(object):
+
+ def __init__(self, name, id = None, children = None):
+ self.name = name
+ self.id = id
+ self.children = children
+
+ def to_json(self, out):
+ out.append('{"name":"%s","id":%d' % (self.name, self.id))
+ if self.children:
+ out.append(',"children":[')
+ first = True
+ for child in self.children:
+ if first: first = False
+ else: out.append(",")
+ child.to_json(out)
+ out.append("]")
+ out.append("}")
+
+ def recursive_build(self, mid, children):
+ select = "SELECT id FROM %s WHERE master_id = %d" % (
+ self.name, mid)
+ ids = [r['id'] for r in plpy.execute(select)]
+ for id in ids:
+ sub_children = []
+ node = Node(self.name, id, sub_children)
+ for child in self.children:
+ child.recursive_build(id, sub_children)
+ children.append(node)
+
+TREE_STRUCTURE = \
+$TREE_STRUCTURE
+
+r = plpy.execute(
+ "SELECT modified, cache FROM case_structure "
+ "WHERE master_id = %d" % case_id, 1)
+
+if not r: return ""
+r = r[0]
+
+if r['modified']: # need refresh
+ children = []
+ tree = Node(TREE_STRUCTURE.name, case_id, children)
+ for child in TREE_STRUCTURE.children:
+ child.recursive_build(case_id, children)
+ out = []
+ tree.to_json(out)
+ cache = "".join(out)
+ plan = plpy.prepare(
+ "UPDATE case_structure SET modified = false, "
+ "cache = $1 WHERE master_id = $2", ["text", "int4"])
+ plpy.execute(plan, [cache, case_id])
+ return cache
+
+return r['cache']
+
+$$ LANGUAGE plpythonu;
+
+--
+-- These triggers are used to keep case_structure table in sync.
+-- Each time a repeat group is created or deleted the case_structure
+-- modified field has to be set to 'true'.
+--
+
+$TRIGGERS
+
+COMMIT;
+''')
+
+TRIGGER_TMPL = Template(
+'''
+-- DROP TRIGGER ${RELATION}_insert_trigger ON master_tbl CASCADE;
+-- DROP FUNCTION ${RELATION}_insert_func();
+-- DROP TRIGGER ${RELATION}_delete_trigger ON master_tbl CASCADE;
+-- DROP FUNCTION ${RELATION}_delete_func();
+
+CREATE OR REPLACE FUNCTION ${RELATION}_insert_func() RETURNS TRIGGER AS $$
+BEGIN
+ UPDATE case_structure SET modified = true WHERE master_id
+ ${SUBSELECT_INSERT}
+ RETURN NEW;
+END;
+$$ LANGUAGE plpgsql;
+
+CREATE TRIGGER ${RELATION}_insert_trigger AFTER INSERT ON rg1_rg
+ FOR EACH ROW EXECUTE PROCEDURE ${RELATION}_insert_func();
+
+CREATE OR REPLACE FUNCTION ${RELATION}_delete_func() RETURNS TRIGGER AS $$
+BEGIN
+ UPDATE case_structure SET modified = true WHERE master_id
+ ${SUBSELECT_DELETE}
+ RETURN OLD;
+END;
+$$ LANGUAGE plpgsql;
+
+CREATE TRIGGER ${RELATION}_delete_trigger AFTER DELETE ON rg1_rg
+ FOR EACH ROW EXECUTE PROCEDURE ${RELATION}_delete_func();
+''')
+
+SUBSELECT_TMPL = Template(
+'''IN (
+ SELECT master_tbl.id FROM $RELATION
+ $INNER_JOINS
+ WHERE $RELATION.id = $NEW_OLD.id
+ )''')
+
+class RGNode(object):
+
+ def __init__(self, name, min = 1, children = None, parent = None):
+ if name == "master": self.name = "master_tbl"
+ else: self.name = "rg_%s_tbl" % name.replace('-', '_')
+ self.min = min
+ if children is None: children = []
+ self.children = children
+ self.parent = parent
+
+ def to_tree_structure(self, out, indent = 0):
+ ind = " " * indent
+
+ if not self.children:
+ out.append("%sNode('%s')" % (ind, self.name))
+ else:
+ out.append("%sNode('%s', children = [\n" % (ind, self.name))
+ for idx, child in enumerate(self.children):
+ child.to_tree_structure(out, indent + 1)
+ out.append((idx < len(self.children) - 1) and ",\n" or "\n")
+ out.append("%s])" % ind)
+
+ def create_triggers(self, out):
+ if self.parent: # only when not master_tbl
+ current = self
+ inner_joins = []
+ while current.parent:
+ inner_joins.append(
+ "INNER JOIN %(parent)s ON %(current)s.master_id = %(parent)s.id" % {
+ 'current': current.name,
+ 'parent': current.parent.name
+ })
+ current = current.parent
+ inner_joins = "\n ".join(inner_joins)
+
+ out.append(TRIGGER_TMPL.safe_substitute({
+ "RELATION": self.name,
+ "SUBSELECT_INSERT": SUBSELECT_TMPL.safe_substitute({
+ "RELATION" : self.name,
+ "INNER_JOINS": inner_joins,
+ "NEW_OLD" : "NEW"
+ }),
+ "SUBSELECT_DELETE": SUBSELECT_TMPL.safe_substitute({
+ "RELATION" : self.name,
+ "INNER_JOINS": inner_joins,
+ "NEW_OLD" : "OLD"
+ })
+ }))
+ for child in self.children:
+ child.create_triggers(out)
+
+
+class RGCreator(ModeChecker):
+
+ def __init__(self, mode, allModes):
+ ModeChecker.__init__(self, mode, allModes, None)
+
+ def createRGs(self, root):
+ self.prepareDescent()
+ self.stack = [RGNode("master", 1, [])]
+ self._createRecursive(root)
+ return self.stack.pop()
+
+ @checkMode
+ def _createRecursive(self, node):
+ for child in node.children:
+ self._createChild(child)
+
+ @checkMode
+ def _createRepeatGroup(self, child):
+ min = child.getMin()
+ if not min: min = 1
+ min = int(min)
+ rg = RGNode(child.getName(), min, [], self.stack[-1])
+ self.stack.append(rg)
+ self._createRecursive(child)
+ self.stack.pop()
+ self.stack[-1].children.append(rg)
+
+ @checkMode
+ def _createChild(self, child):
+ if isinstance(child, Leaf):
+ return
+ elif isinstance(child, Node):
+ if isinstance(child, data.RepeatNode):
+ self._createRepeatGroup(child)
+ else:
+ self._createRecursive(child)
+
+class ExportAsRepeatGroupSQLFilter(FileDialogFilter):
+
+ def __init__(self):
+ FileDialogFilter.__init__(self)
+
+ def getMenuName(self):
+ return _("Export As &Repeat Group SQL...")
+
+ def getDescription(self):
+ return _("Exports document as repeat group SQL statements")
+
+ def wildcards(self):
+ return _("SQL files (*.sql)|*.sql")
+
+ def dialogMessage(self):
+ return _("Export as RG SQL statements")
+
+ def errorTitle(self):
+ return _("Error occured while exporting RG SQL")
+
+ def beforeFileDialog(self, main):
+ pass
+
+ def doExport(self, path, main, tup):
+ root = main.getDocument().root
+ mode = main.getSelectedMode()
+ allModes = main.getAllModes()
+ creator = RGCreator(mode, allModes)
+ rg_root = creator.createRGs(root)
+
+ out = []
+ rg_root.to_tree_structure(out)
+ tree_structure = "".join(out)
+
+ out = []
+ rg_root.create_triggers(out)
+ triggers = "".join(out)
+
+ f = None
+ try:
+ out = SQL_TEMPLATE.safe_substitute({
+ "TREE_STRUCTURE": tree_structure,
+ "TRIGGERS" : triggers
+ })
+ f = codecs.open(path, "w", "UTF-8")
+ print >> f, out
+ finally:
+ if f:
+ try: f.close()
+ except: pass
+
+
+# vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 enc=utf-8:
More information about the Formed-commits
mailing list