[Formed-commits] r330 - in trunk: . formed/formed formed/formed/plugins/export

scm-commit@wald.intevation.org scm-commit at wald.intevation.org
Thu Apr 30 17:56:39 CEST 2009


Author: teichmann
Date: 2009-04-30 17:56:38 +0200 (Thu, 30 Apr 2009)
New Revision: 330

Added:
   trunk/formed/formed/plugins/export/new_sql.py
Modified:
   trunk/ChangeLog
   trunk/formed/formed/config.py
   trunk/formed/formed/plugins/export/rg_sql.py
Log:
New SQL schema generator plug-in.


Modified: trunk/ChangeLog
===================================================================
--- trunk/ChangeLog	2009-04-28 10:06:31 UTC (rev 329)
+++ trunk/ChangeLog	2009-04-30 15:56:38 UTC (rev 330)
@@ -1,5 +1,16 @@
 2009-04-28	Sascha L. Teichmann <teichmann at intevation.de>
 
+	* formed/formed/plugins/export/new_sql.py: New SQL schema generator to
+	  replace the old one. The new one faciliates the new RepeatNode instead
+	  of the Group.isRepeat() method to tell if something is to be repeated.
+
+	* formed/formed/config.py: Use new SQL schema generator plug-in by default.
+	  The old one is deactivated.
+
+	* formed/formed/plugins/export/rg_sql.py: Fix 'rg_' naming of repeat groups.
+
+2009-04-28	Sascha L. Teichmann <teichmann at intevation.de>
+
 	* formed/formed/plugins/export/rg_sql.py: Added delete functions.
 	  TODO: Adjust execution rights.
 

Modified: trunk/formed/formed/config.py
===================================================================
--- trunk/formed/formed/config.py	2009-04-28 10:06:31 UTC (rev 329)
+++ trunk/formed/formed/config.py	2009-04-30 15:56:38 UTC (rev 330)
@@ -33,8 +33,9 @@
 #    ExportStoredProcedureFilter, \
 #    ExportViewsFilter
 
-from formed.plugins.export.sql    import ExportAsSQLFilter
-from formed.plugins.export.rg_sql import ExportAsRepeatGroupSQLFilter
+#from formed.plugins.export.sql    import ExportAsSQLFilter
+from formed.plugins.export.new_sql import ExportAsSQLFilter
+from formed.plugins.export.rg_sql  import ExportAsRepeatGroupSQLFilter
 
 from formed.plugins.export.data import DataImportFilter, DataExportFilter
 

Added: trunk/formed/formed/plugins/export/new_sql.py
===================================================================
--- trunk/formed/formed/plugins/export/new_sql.py	2009-04-28 10:06:31 UTC (rev 329)
+++ trunk/formed/formed/plugins/export/new_sql.py	2009-04-30 15:56:38 UTC (rev 330)
@@ -0,0 +1,877 @@
+# -*- 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:
+# Torsten Irlaender   <torsten.irlaender at intevation.de>
+# Sascha L. Teichmann <teichmann at intevation.de>
+# Frank Koormann      <frank.koormann at intevation.de>
+#
+
+from formed.plugins.ui.controls import FileDialogFilter
+
+import formed.model.data as data
+from   formed.model.nodecomponents import Node, Leaf
+
+import codecs
+import sys
+
+from   cgi  import escape
+from   time import gmtime, strftime
+
+VIEW_WHERE_CLAUSE = \
+"""WHERE
+    master_tbl.bearbeiter_id = getuserid()
+    OR master_tbl.id IN (
+        SELECT nm.master_id from nm_benutzer_master_tbl nm
+        WHERE nm.benutzer_id IN (
+            SELECT nm2.gruppe_id from nm_benutzer_gruppe_tbl nm2 
+            WHERE nm2.benutzer_id = getuserid()
+        )
+    )
+    OR pg_has_role('admin_ka','MEMBER');
+"""
+
+FORBID_DELETE_RULE = \
+"""CREATE OR REPLACE RULE %s_delete_rule AS ON DELETE TO %s_view
+    DO INSTEAD NOTHING;
+"""
+
+FORBID_INSERT_RULE = \
+"""CREATE OR REPLACE RULE %s_insert_rule AS ON INSERT TO %s_view
+    DO INSTEAD NOTHING;
+"""
+
+FORBID_UPDATE_RULE = \
+"""CREATE OR REPLACE RULE %s_update_rule AS ON UPDATE TO %s_view
+    DO INSTEAD NOTHING;
+"""
+
+UPDATE_RULE = \
+"""CREATE OR REPLACE RULE %s_update_rule AS ON UPDATE TO %s_view
+DO INSTEAD UPDATE %s SET
+"""
+
+GRANT_SELECT = \
+"""GRANT SELECT ON %s TO GROUP %s;
+"""
+
+GRANT_INSERT = \
+"""GRANT INSERT ON %s TO GROUP %s;
+"""
+
+GRANT_UPDATE = \
+"""GRANT UPDATE ON %s TO GROUP %s;
+"""
+
+ANON_FUNCTION_HEADER = \
+"""--
+-- Function to anonymize a case and all of its repeat groups
+--
+CREATE OR REPLACE FUNCTION anonymize_case(mid int)
+RETURNS void AS
+$$
+BEGIN
+    anonymize_uuid(mid);
+"""
+
+ANON_UPDATE = \
+"""    UPDATE %s_view SET
+        %s
+    WHERE %s;
+
+"""
+
+ANON_FUNCTION_FOOTER = \
+"""END;
+$$ LANGUAGE plpgsql VOLATILE SECURITY DEFINER;
+
+REVOKE ALL ON FUNCTION anonymize_case(int) FROM public;
+GRANT EXECUTE ON FUNCTION anonymize_case(int) TO GROUP :admin_ka_group;
+
+"""
+
+def removeTbl(s):
+    return s.replace("_tbl", "");
+
+
+def sqlQuote(name):
+    return name.replace("'", "''")
+
+def sqlName(name):
+    return name.replace("-", "_")
+
+class RelationAnonymizer:
+
+    def __init__(self, raw_table, table):
+        self.name = table
+        anonym = []
+
+        for c in raw_table.columns:
+            #if not c.accessible: continue
+            if not c.func:
+                # no set needed
+                continue
+            if c.func == "suppress_value" or c.func == "suppress_value2":
+                anonym.append('%s = NULL' % c.name)
+            else:
+                args = [x.strip() for x in c.func.split(':')]
+                if len(args) > 1:
+                    anonym.append("%s = %s(%s, %s)" % (
+                        c.name, args[0], c.name, ", ".join(args[1:])))
+                else:
+                    anonym.append("%s = %s(%s)" % (c.name, args[0], c.name))
+
+        if not anonym: # empty -> not sets
+            self.processed = True
+            self.set_columns = ""
+        else:
+            self.processed = False
+            self.set_columns = ",\n        ".join(anonym)
+
+    def writeUpdate(self, pfeil, where_clause="master_id = mid"):
+        if self.processed: return
+        self.processed = True
+        pfeil(ANON_UPDATE % (self.name, self.set_columns, where_clause))
+
+class CaseAnonymizer:
+
+    def __init__(self):
+        self.anonymizers = []
+
+    def addAnoymizer(self, anonymizer):
+        self.anonymizers.append(anonymizer)
+
+    def _findMaster(self):
+        for rel in self.anonymizers:
+            if rel.name == 'master_tbl':
+                return rel
+        return None
+
+    def writeFunction(self, pfeil):
+        master = self._findMaster()
+        if master is None: 
+            print >> sys.stderr, "master_tbl not found"
+            return
+        pfeil(ANON_FUNCTION_HEADER)
+        pfeil("    -- anonymize master table\n")
+        master.writeUpdate(pfeil, "id = mid")
+
+        pfeil("    -- anonymize repeat groups\n")
+        for anonymizer in self.anonymizers:
+            anonymizer.writeUpdate(pfeil)
+
+        pfeil(ANON_FUNCTION_FOOTER)
+
+class Column:
+    def __init__(self, name, datatype, accessible=True, func=None):
+        self.name       = name
+        self.datatype   = datatype
+        self.accessible = accessible
+        self.func       = func
+
+    def definition(self):
+        return "%s %s" % (self.name, self.datatype)
+
+class Table:
+    def __init__(self, name, parent = None, root = None):
+        self.name        = name
+        self.columns     = []
+        self.constraints = []
+        self.inserts     = []
+        self.depends     = set()
+        self.parent      = parent
+        self.root        = root
+
+    def addDependency(self, dependency):
+        self.depends.add(dependency)
+
+    def appendColumn(self, column):
+        self.columns.append(column)
+
+    def appendConstraint(self, constraint):
+        self.constraints.append(constraint)
+
+    def appendInsert(self, ins):
+        self.inserts.append(ins)
+
+    def getName(self):
+        return self.name
+
+    def getSchema(self):
+        out = [ "CREATE TABLE %s (\n    " % self.name ]
+        out.append(",\n    ".join(
+            [column.definition() for column in self.columns]))
+        if self.constraints:
+            out.append(",\n    ")
+            out.append(",\n    ".join(self.constraints))
+
+        out.append("\n);")
+        return "".join(out)
+
+    def getInserts(self):
+        if not self.inserts:
+            return ""
+        out = []
+        for ins in self.inserts:
+            out.append("INSERT INTO %s %s;" % (self.name, ins))
+        return "\n".join(out)
+
+    def equalContent(self, other):
+        # XXX: This is a bit to simple for general case
+        if len(self.inserts) != len(other.inserts):
+            return False
+        ains = [a.lower() for a in self.inserts]
+        bins = [b.lower() for b in other.inserts]
+        ains.sort()
+        bins.sort()
+        return ains == bins
+
+class SchemaCreator:
+
+    def __init__(self, root, mode, allModes):
+        self.root       = root
+        self.mode       = mode
+        self.allModes   = allModes
+        self.modesStack = []
+        self.tableStack = []
+        self.tables     = []
+
+    def modeCheck(visitor):
+        def _wrapper(*args):
+            self = args[0]
+            if not self.mode               \
+                or self.allModes is None   \
+                or len(self.modesStack) == 0:
+                    visitor(*args)
+            else:
+                modes = self.modesStack[-1].copy()
+                args[1].modifyModeSet(modes)
+                if self.mode in modes:
+                    self.modesStack.append(modes)
+                    visitor(*args)
+                    self.modesStack.pop()
+        return _wrapper
+
+    def findSameContent(self, table):
+        for t in self.tables:
+            if t.equalContent(table):
+                return t
+        return None
+
+    def createSchema(self):
+        self.modesStack = [ self.allModes ]
+        table = Table("master_tbl", root = self.root)
+        table.appendColumn(Column("id", "SERIAL PRIMARY KEY NOT NULL", False))
+        table.appendColumn(Column("uuid_id", "CHAR(36)", False))
+        table.appendColumn(Column("bearbeiter_id", "int NOT NULL", False))
+
+        table.appendConstraint("UNIQUE (uuid_id)")
+        # table.appendConstraint(
+        #     "-- FOREIGN KEY (bearbeiter_oid) REFERENCES pg_roles (OID)")
+        # table.appendConstraint(
+        #     "-- FOREIGN KEY (vertreter_oid) REFERENCES pg_roles (OID)")
+        self.tableStack = [table]
+        self._createRecursive(self.root)
+        self.tables.append(self.tableStack.pop())
+
+    def topological(self):
+        free, bound = [], []
+        for t in self.tables:
+            if not t.depends: free.append(t)
+            else:             bound.append(t)
+
+        out = []
+        while free:
+            f = free.pop()
+            out.append(f)
+            tmp = []
+            for i in range(len(bound)):
+                t = bound[i]
+                try:
+                    t.depends.remove(f.name)
+                    if not t.depends:
+                        tmp.insert(0, i)
+                except:
+                    pass
+            for i in tmp:
+                free.append(bound.pop(i))
+
+        if bound:
+            print >> sys.stderr, \
+                "WARNING: cyclic dependencies on %d tables." % len(bound)
+            for t in bound:
+                deps = ", ".join(sorted(t.depends))
+                print >> sys.stderr, "  %s: %s" % (t.name, deps)
+            # append them for debugging purposes
+            out.extend(bound)
+        return out
+
+    @modeCheck
+    def _createRecursive(self, node):
+        for child in node.children:
+            self._createChild(child)
+
+    @modeCheck
+    def _createDate(self, date):
+        name = date.getName()
+        if not name: return
+        name = sqlName(name)
+        table = self.tableStack[-1]
+        table.appendColumn(Column(name, "DATE", func=date.getFunction()))
+
+    @modeCheck
+    def _createBool(self, bool):
+        name = bool.getName()
+        if not name: return
+        name = sqlName(name)
+        table = self.tableStack[-1]
+        #table.appendColumn("%s BOOLEAN" % name)
+        kvs = {
+            -1: 'Keine Angabe',
+            0:  'Nein',
+            1:  'Ja'
+        }
+        max_length = max([len(v) for v in kvs.itervalues()]) + 1
+
+        choiceTable = Table(name + '_tbl')
+        choiceTable.appendColumn(Column('id', 'SERIAL PRIMARY KEY NOT NULL'))
+        choiceTable.appendColumn(Column('value', 'VARCHAR(%d) NOT NULL' % max_length))
+
+        for k in sorted(kvs.iterkeys()):
+            choiceTable.appendInsert("(id, value) VALUES (%d, '%s')" % (
+                k, sqlQuote(kvs[k])))
+
+        other = self.findSameContent(choiceTable)
+
+        if other:
+            into = other.name
+        else:
+            into = name + "_tbl"
+            self.tables.append(choiceTable)
+
+        table = self.tableStack[-1]
+
+        table.appendColumn(Column(name, 'INTEGER DEFAULT -1', func=bool.getFunction()))
+
+        table.appendConstraint(
+            "FOREIGN KEY (%s) REFERENCES %s (id)" % (name, into))
+        table.addDependency(into)
+
+    @modeCheck
+    def _createText(self, text):
+        name = text.getName()
+        if not name: return
+        name = sqlName(name)
+        table = self.tableStack[-1]
+        length = text.getMaxLength()
+        if length:
+            table.appendColumn(Column(name, "VARCHAR(%s)" % str(length), func=text.getFunction()))
+        else:
+            table.appendColumn(Column(name, "VARCHAR", func=text.getFunction()))
+
+    @modeCheck
+    def _createTextArea(self, text):
+        name = text.getName()
+        if not name: return
+        name = sqlName(name)
+        table = self.tableStack[-1]
+        table.appendColumn(Column(name, "TEXT", func=text.getFunction()))
+
+    @modeCheck
+    def _createInteger(self, integer):
+        name = integer.getName()
+        if not name: return
+        name = sqlName(name)
+        table = self.tableStack[-1]
+        table.appendColumn(Column(name, "INTEGER", func=integer.getFunction()))
+
+    @modeCheck
+    def _createChoice(self, choice):
+        name = choice.getName()
+        if not name: return
+        name = sqlName(name)
+
+        if choice.getMultiple():
+            print >> sys.stderr, "WARNING: No support for multiple choices."
+            return
+
+        #if len(choice.children) == 3:
+        #   yesNo = set(['-1', '0', '1'])
+        #   descYesNo = set(['ja', 'nein', 'keine angabe'])
+        #   for c in choice.children:
+        #       yesNo.discard(c.getValue())
+        #       desc = c.getDescription()
+        #       if not desc: break
+        #       descYesNo.discard(desc.strip().lower())
+        #   if not yesNo and not descYesNo:
+        #       table = self.tableStack[-1]
+        #       table.appendColumn("%s BOOLEAN" % name)
+        #       return   
+
+        kvs = {}
+        try:
+            for c in choice.children:
+                if isinstance(c, data.ExternalChoiceListLeaf):
+                    cc = c.getChildren()
+                    if cc:
+                        for ccc in cc:
+                            desc = ccc.getDescription()
+                            if not desc: desc = ""
+                            kvs[int(ccc.getValue())] = desc
+                elif isinstance(c, data.BoolLeaf):
+                    desc = c.getDescription()
+                    if not desc: desc = ""
+                    kvs[int(c.getValue())] = desc
+        except ValueError:
+            print >> sys.stderr, "WARNING choice '%s' contains none integer values" % name
+            return
+
+        if not kvs:
+            print >> sys.stderr, "WARNING choice '%s' contains no values" % name
+            return
+
+        max_length = max([len(v) for v in kvs.itervalues()]) + 1
+
+        choiceTable = Table(name + '_tbl')
+        choiceTable.appendColumn(Column('id', 'SERIAL PRIMARY KEY NOT NULL'))
+        choiceTable.appendColumn(Column('value', 'VARCHAR(%d) NOT NULL' % max_length))
+
+        for k in sorted(kvs.iterkeys()):
+            choiceTable.appendInsert("(id, value) VALUES (%d, '%s')" % (
+                k, sqlQuote(kvs[k])))
+
+        if choiceTable.inserts:
+            other = self.findSameContent(choiceTable)
+        else:
+            other = None
+
+        if other:
+            into = other.name
+        else:
+            into = name + "_tbl"
+            self.tables.append(choiceTable)
+
+        table = self.tableStack[-1]
+
+        table.appendColumn(Column(name, 'INTEGER DEFAULT -1', func=choice.getFunction()))
+
+        table.appendConstraint(
+            "FOREIGN KEY (%s) REFERENCES %s (id)" % (name, into))
+        table.addDependency(into)
+
+        
+    @modeCheck
+    def _createRadioGroup(self, radio):
+        name = radio.getName()
+        if not name: return
+        name = sqlName(name)
+        selected = radio.getSelected()
+        if selected:
+            found = False
+            for child in radio.children:
+                if child.getName() == selected:
+                    found = True
+                    break
+            if not found:
+                print >> sys.stderr, \
+                    "WARNING: Don't found selected in radio '%s'" % name
+                selected = None
+
+        max_length = max([len(c.getDescription()) for c in radio.children]) + 1
+
+        radioTable = Table(name + '_tbl')
+        radioTable.appendColumn(Column('id', 'INTEGER PRIMARY KEY NOT NULL'))
+        radioTable.appendColumn(Column('value', 'VARCHAR(%d) NOT NULL' % max_length))
+
+        default_idx = None
+
+        for i, child in enumerate(radio.children):
+            cname = child.getName()
+            try:
+                value = int(child.getValue())
+            except ValueError:
+                print >> sys, "WARNING: cannot convert radio '%s' value '%s' to integer" % (
+                    cname, child.getValue())
+                continue
+
+            if default_idx is None and (child.getChecked() or cname == selected):
+                default_idx = value
+
+            radioTable.appendInsert("(id, value) VALUES (%d, '%s')" % (
+                value, sqlQuote(child.getDescription())))
+
+        other = self.findSameContent(radioTable)
+
+        if other:
+            into = other.name
+            print >> sys.stderr, "Found radio '%s" % into
+        else:
+            into = name + "_tbl"
+            self.tables.append(radioTable)
+
+        table = self.tableStack[-1]
+
+        table.appendColumn(
+            Column(
+                name,
+                default_idx is not None and ('INTEGER DEFAULT %d' % default_idx) or 'INTEGER',
+                func=radio.getFunction()))
+
+        table.appendConstraint(
+            "FOREIGN KEY (%s) REFERENCES %s (id)" % (name, into))
+
+        table.addDependency(into)
+
+    @modeCheck
+    def _createRepeatNode(self, group):
+        name = group.getName()
+
+        if name:
+            name = sqlName(name)
+            parent = self.tableStack[-1]
+            table = Table("%s_tbl" % name, parent, group)
+            table.appendColumn(Column("id", "SERIAL PRIMARY KEY NOT NULL", False))
+            table.appendColumn(Column("%s_id" % removeTbl(parent.name), "INTEGER NOT NULL", False))
+            table.appendColumn(Column("uuid_id", "CHAR(36)", False))
+            table.appendConstraint(
+                "FOREIGN KEY (%s_id) REFERENCES %s (id)" % (
+                    removeTbl(parent.name), parent.name))
+            table.appendConstraint("UNIQUE (uuid_id)")
+            table.addDependency(parent.name)
+            self.tableStack.append(table)
+
+            for child in group.children:
+                self._createChild(child)
+
+            self.tables.append(self.tableStack.pop())
+        else:
+            for child in group.children:
+                self._createChild(child)
+
+    @modeCheck
+    def _createChild(self, child):
+        if isinstance(child, Leaf):
+            if isinstance(child, data.IntLeaf):
+                self._createInteger(child)
+            elif isinstance(child, data.DateLeaf):
+                self._createDate(child)
+            elif isinstance(child, data.TextLeaf):
+                self._createText(child)
+            elif isinstance(child, data.TextAreaLeaf):
+                self._createTextArea(child)
+            elif isinstance(child, data.BoolLeaf):
+                self._createBool(child)
+        elif isinstance(child, Node):
+            if isinstance(child, data.RepeatNode):
+                self._createRepeatNode(child)
+            elif isinstance(child, data.RadioNode):
+                self._createRadioGroup(child)
+            elif isinstance(child, data.ChoiceNode):
+                self._createChoice(child)
+            else:
+                self._createRecursive(child)
+        
+
+class ExportAsSQLFilter(FileDialogFilter):
+
+    def __init__(self):
+        FileDialogFilter.__init__(self)
+
+    def getMenuName(self):
+        return _("Export As &SQL...")
+
+    def getDescription(self):
+        return _("Exports document as SQL statements")
+
+    def wildcards(self):
+        return _("SQL files (*.sql)|*.sql")
+
+    def dialogMessage(self):
+        return _("Export as SQL statements")
+
+    def errorTitle(self):
+        return _("Error occured while exporting SQL")
+
+    def beforeFileDialog(self, main):
+        root     = main.getDocument().root
+        mode     = main.getSelectedMode()
+        allModes = main.getAllModes()
+        creator  = SchemaCreator(root, mode, allModes)
+        creator.createSchema()
+        tables = creator.topological()
+        arr = self.printRepeats(tables, mode, allModes)
+        return tables, arr
+
+    def printRepeats(self, tables, mode, allModes):
+        arr = []
+        for t in tables:
+            root = t.root
+            if not root: continue
+            parent = t.parent
+            joins = []
+            while parent:
+                joins.append(parent.name)
+                parent = parent.parent
+            linear = [ c for c in root.firstLevel(
+                lambda c: isinstance(c, data.RepeatNode) or isinstance(c, data.PageNode),
+                mode, allModes) ]
+            print "%s:" % t.name
+            arr.append((t, joins, linear))
+            if joins:
+                print "\tjoins: %s" % " -> ".join(joins)
+            if linear:
+                print "\tlinear: %s" % ", ".join([p.getName() for p in linear])
+        return arr
+                    
+
+
+    def doExport(self, path, main, tup):
+        root     = main.getDocument().root
+        mode     = main.getSelectedMode()
+        allModes = main.getAllModes()
+
+        tables, arr = tup
+        out = None
+        tableGrants = "" 
+
+        caseAnonymizer = CaseAnonymizer()
+
+        try:
+            out = codecs.open(path, "wb", "UTF-8")
+            pfeil = out.write
+            pfeil(u'--\n-- Schema generated by FormEd\n-- %s\n' % \
+                strftime("%a, %Y-%m-%d %H:%M:%S +0000", gmtime()))
+            pfeil('-- # tables: %d\n--\n' % len(tables))
+
+            pfeil('BEGIN;')
+
+            pfeil('\n--\n-- DROP TABLES\n--\n')
+            for t in reversed(tables):
+                pfeil('-- DROP TABLE %s CASCADE;\n' % t.name)
+
+            pfeil('\n--\n-- CREATE TABLES\n--\n')
+            for table in tables:
+                pfeil("%s\n" % table.getSchema())
+            
+            pfeil('\n--\n-- INSERT DATA\n--\n')
+            for table in tables:
+                inserts = table.getInserts()
+                if inserts: 
+                    pfeil("%s\n\n" % inserts)
+                    tableGrants = tableGrants \
+                        + (GRANT_SELECT % (table.getName(),':cm_ka_group')) \
+                        + (GRANT_SELECT % (table.getName(),':admin_ka_group'))
+
+            pfeil('\n--\n-- GRANTS ON TABLES\n--\n')
+            pfeil(tableGrants)
+
+            pfeil('\n--\n-- VIEWS\n--\n')
+            page_tables = [x[0].name for x in arr]
+            marked = set()
+            for tup in arr:
+                raw_table, joins, linear = tup
+                table = raw_table.name
+                for page in linear:
+                    table_name = "%s_tbl" % sqlName(page.getName())
+                    if table_name in page_tables:
+                        marked.add(table_name)
+                    else:
+                        pfeil("CREATE OR REPLACE VIEW %s_view AS SELECT\n" \
+                            % sqlName(page.getName()))
+
+                        ws = [
+                            w.getName() 
+                            for w in page.allWidgets() if w.getName()]
+
+                        widgets = [
+                            "    %s.id AS id" % table ]
+                        widgets.extend([
+                            "    %s.%s AS %s" % (table, n, n)
+                            for n in ws]);
+                        pfeil(",\n".join(widgets))
+                        pfeil("\nFROM %s\n" % table)
+
+                        njoins = joins
+                        oldTable = table
+
+                        while njoins:
+                            try:
+                                idx = page_tables.index(njoins[0])
+                            except ValueError:
+                                break
+                            ntup = arr[idx]
+                            njoins = ntup[1]
+                            nname = ntup[0].name
+
+                            pfeil("    INNER JOIN %s\n" % nname)
+                            pfeil("    ON         %s.%s_id = %s.id\n" % (
+                                oldTable, removeTbl(nname), nname))
+                            oldTable = nname
+                        pfeil(VIEW_WHERE_CLAUSE)
+                        pfeil("\n")
+
+                        pfeil(UPDATE_RULE % (sqlName(page.getName()), sqlName(page.getName()), table))
+                        pfeil(",\n".join(["    %s = NEW.%s" % (n, n) for n in ws]))
+                        pfeil("\n")
+                        pfeil("WHERE id = NEW.id;\n\n")
+
+                        pn = sqlName(page.getName())
+
+                        vn = "%s_view" % sqlName(pn)
+                        pfeil(GRANT_SELECT % (vn, ':cm_ka_group'))
+                        pfeil(GRANT_UPDATE % (vn, ':cm_ka_group'))
+                        pfeil(GRANT_SELECT % (vn, ':admin_ka_group'))
+                        pfeil("\n")
+
+                        pfeil(FORBID_DELETE_RULE % (pn, pn))
+                        pfeil("\n")
+                        pfeil(FORBID_INSERT_RULE % (pn, pn))
+                        pfeil("\n")
+
+            for pt in page_tables:
+                if pt not in marked \
+                and not arr[page_tables.index(pt)][2]:
+                    pfeil("CREATE OR REPLACE VIEW %s_page_view AS SELECT\n" % sqlName(pt))
+
+                    group = None
+                    for tup2 in arr:
+                        if tup2[0].root.getName()+"_tbl" == pt:
+                            group = tup2[0].root
+                            break
+
+                    ws = [w.getName() for w in group.allWidgets() if w.getName()]
+
+                    widgets = [
+                        "    %s.id AS id" % pt
+                    ]
+                    widgets.extend([
+                        "    %s.%s AS %s" % (pt, n, n)
+                        for n in ws])
+                    pfeil(",\n".join(widgets))
+                    pfeil("\nFROM %s\n" % pt)
+                    joins = arr[page_tables.index(pt)][1][:]
+                    oldTable = pt
+                    for join in joins:
+                        pfeil("    INNER JOIN %s\n" % join)
+                        pfeil("    ON         %s.%s_id = %s.id\n" % (
+                            oldTable, removeTbl(join), join))
+                        oldTable = join
+                    pfeil(VIEW_WHERE_CLAUSE)
+                    pfeil("\n")
+                    v = "%s_page" % pt
+
+                    pfeil(UPDATE_RULE % (v, v, pt))
+                    pfeil(",\n".join(["    %s = NEW.%s" % (n, n) for n in ws]))
+                    pfeil("\n")
+                    pfeil("WHERE id = NEW.id;\n\n")
+
+                    vn = "%s_page_view" % sqlName(pt)
+                    pfeil(GRANT_SELECT % (vn, ':cm_ka_group'))
+                    pfeil(GRANT_UPDATE % (vn, ':cm_ka_group'))
+                    pfeil(GRANT_SELECT % (vn, ':admin_ka_group'))
+                    pfeil("\n")
+
+                    pfeil(FORBID_DELETE_RULE % (v, v))
+                    pfeil("\n")
+                    pfeil(FORBID_INSERT_RULE % (v, v))
+                    pfeil("\n")
+
+            # generate ro-views for master table and repeat groups
+            pfeil("--\n--\n-- views for master table and repeat groups\n--\n")
+            for tup in arr:
+                raw_table, joins, linear = tup
+                table = raw_table.name
+                tab = sqlName(table)
+                pfeil("CREATE OR REPLACE VIEW %s_view\nAS SELECT %s.* FROM %s\n" % (
+                    tab, tab, tab))
+
+                njoins   = joins
+                oldTable = table
+
+                while njoins:
+                    found = False
+                    for t in arr:
+                        raw_table2, joins, linear2 = t
+                        nname = raw_table2.name
+                        if nname == njoins[0]:
+                            found = True; break
+                    if not found: break
+
+                    njoins = joins
+
+                    pfeil("    INNER JOIN %s\n" % nname)
+                    pfeil("    ON         %s.%s_id = %s.id\n" % (
+                        oldTable, removeTbl(nname), nname))
+                    oldTable = nname
+                pfeil(VIEW_WHERE_CLAUSE)
+                pfeil("\n")
+
+
+                pfeil(UPDATE_RULE % (tab, tab, tab))
+                pfeil(",\n".join(
+                    ["    %s = NEW.%s" % (c.name, c.name) 
+                        for c in raw_table.columns if c.accessible]))
+                pfeil("\n")
+                pfeil("WHERE id = NEW.id;\n\n")
+
+                pfeil(FORBID_DELETE_RULE % (tab, tab))
+                pfeil("\n")
+                pfeil(FORBID_INSERT_RULE % (tab, tab))
+                pfeil("\n")
+
+                vn = "%s_view" % tab
+                pfeil(GRANT_SELECT % (vn, ':cm_ka_group'))
+                pfeil(GRANT_UPDATE % (vn, ':cm_ka_group'))
+                pfeil(GRANT_SELECT % (vn, ':admin_ka_group'))
+                pfeil("\n")
+
+                # anonymization view
+                self._writeAnonymizationView(pfeil, raw_table, tab)
+
+                caseAnonymizer.addAnoymizer(RelationAnonymizer(raw_table, table))
+                
+            caseAnonymizer.writeFunction(pfeil)
+            pfeil('COMMIT;\n')
+        finally:
+            if out:
+                try: out.close()
+                except: pass
+
+    def _writeAnonymizationView(self, pfeil, raw_table, table):
+        anonym = []
+
+        for c in raw_table.columns:
+            #if not c.accessible: continue
+            if not c.func:
+                anonym.append(c.name)
+            else:
+                if c.func == "suppress_value" or c.func == "suppress_value2":
+                    datatype = c.datatype.split()[0] # a bit hackish
+                    anonym.append('NULL::%s AS %s' % (datatype, c.name))
+                else:
+                    args = [x.strip() for x in c.func.split(':')]
+                    if len(args) > 1:
+                        anonym.append("%s(%s, %s) AS %s" % (
+                            args[0], c.name, ", ".join(args[1:]), c.name))
+                    else:
+                        anonym.append("%s(%s) AS %s" % (args[0], c.name, c.name))
+        pfeil("CREATE OR REPLACE VIEW %s_anonym_view\nAS SELECT\n    %s FROM %s_view;\n\n" % (
+            table, ",\n    ".join(anonym), table))
+
+        vn = "%s_anonym" % table
+        pfeil(FORBID_DELETE_RULE % (vn, vn))
+        pfeil("\n")
+        pfeil(FORBID_INSERT_RULE % (vn, vn))
+        pfeil("\n")
+
+        vn = "%s_anonym_view" % table
+        pfeil(GRANT_SELECT % (vn, ':cm_ka_group'))
+        pfeil(GRANT_SELECT % (vn, ':admin_ka_group'))
+        pfeil("\n")
+
+# vim:set ts=4 sw=4 si et sta sts=4:

Modified: trunk/formed/formed/plugins/export/rg_sql.py
===================================================================
--- trunk/formed/formed/plugins/export/rg_sql.py	2009-04-28 10:06:31 UTC (rev 329)
+++ trunk/formed/formed/plugins/export/rg_sql.py	2009-04-30 15:56:38 UTC (rev 330)
@@ -275,9 +275,8 @@
 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
+        self.name = "%s_tbl" % name.replace('-', '_')
+        self.min  = min
         if children is None: children = []
         self.children = children
         self.parent   = parent
@@ -398,7 +397,7 @@
             self._createChild(child)
 
     @checkMode
-    def _createRepeatGroup(self, child):
+    def _createRepeatNode(self, child):
         min = child.getMin()
         if not min: min = 0
         min = int(min)



More information about the Formed-commits mailing list