Add a basic script to compare the db_schema to the fs_schema.
authorPierre-Yves David <pierre-yves.david@logilab.fr>
Thu, 22 Apr 2010 15:50:45 +0200
changeset 5372 b74eed7e8b37
parent 5364 e0feefb87de5
child 5373 24a873060692
Add a basic script to compare the db_schema to the fs_schema. Allow SchemaViewer to be used without a request object Most use of self.req have been hidden in method that fallback on a simple behaviour when no req are provided. Several element have been sorted to ease comparison.
doc/book/en/development/devweb/views/index.rst
misc/cmp_schema.py
schema.py
web/schemaviewer.py
--- a/doc/book/en/development/devweb/views/index.rst	Tue Apr 20 18:34:37 2010 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,31 +0,0 @@
-The View system
-===============
-
-This chapter aims to describe the concept of a `view` used all along
-the development of a web application and how it has been implemented
-in |cubicweb|.
-
-
-.. toctree::
-   :maxdepth: 3
-
-   views
-   basetemplates
-   primary
-   baseviews
-   startup
-   boxes
-   table
-   xmlrss
-   autoform
-..   editforms
-
-.. toctree::
-   :maxdepth: 3
-
-   urlpublish
-   breadcrumbs
-..   wdoc
-..   embedding
-..   idownloadable
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/misc/cmp_schema.py	Thu Apr 22 15:50:45 2010 +0200
@@ -0,0 +1,24 @@
+"""This module compare the Schema on the file system to the one in the database"""
+
+from cStringIO import StringIO
+from cubicweb.web.schemaviewer import SchemaViewer
+from logilab.common.ureports import TextWriter
+import difflib
+
+viewer = SchemaViewer()
+layout_db = viewer.visit_schema(schema, display_relations=True)
+layout_fs = viewer.visit_schema(fsschema, display_relations=True)
+writer = TextWriter()
+stream_db = StringIO()
+stream_fs = StringIO()
+writer.format(layout_db, stream=stream_db)
+writer.format(layout_fs, stream=stream_fs)
+
+stream_db.seek(0)
+stream_fs.seek(0)
+db = stream_db.getvalue().splitlines()
+fs = stream_fs.getvalue().splitlines()
+open('db_schema.txt', 'w').write(stream_db.getvalue())
+open('fs_schema.txt', 'w').write(stream_fs.getvalue())
+#for diff in difflib.ndiff(fs, db):
+#    print diff
--- a/schema.py	Tue Apr 20 18:34:37 2010 +0200
+++ b/schema.py	Thu Apr 22 15:50:45 2010 +0200
@@ -161,7 +161,7 @@
         mainvars.append('U')
     if not mainvars:
         raise Exception('unable to guess selection variables')
-    return ','.join(mainvars)
+    return ','.join(sorted(mainvars))
 
 def split_expression(rqlstring):
     for expr in rqlstring.split(','):
--- a/web/schemaviewer.py	Tue Apr 20 18:34:37 2010 +0200
+++ b/web/schemaviewer.py	Thu Apr 22 15:50:45 2010 +0200
@@ -12,6 +12,9 @@
 
 from yams.schema2dot import CARD_MAP
 from yams.schema import RelationDefinitionSchema
+from operator import attrgetter
+
+TYPE_GETTER = attrgetter('type')
 
 I18NSTRINGS = [_('read'), _('add'), _('delete'), _('update'), _('order')]
 
@@ -21,41 +24,71 @@
     def __init__(self, req=None, encoding=None):
         self.req = req
         if req is not None:
-            self.req.add_css('cubicweb.schema.css')
-            if not encoding:
+            req.add_css('cubicweb.schema.css')
+            if encoding is None:
                 encoding = req.encoding
+            self._ = req._
+        else:
+            encoding = 'ascii'
+            self._ = unicode
         self.encoding = encoding
 
+    # no self.req managements
+
+    def may_read(self, rdef, action):
+        """Return true if request user may read the given schema.
+        Always return True when no request is provided.
+        """
+        if self.req is None:
+            return True
+        return sch.may_have_permission('read', self.req)
+
+    def format_eschema(self, eschema):
+        text = eschema.type
+        if self.req is None:
+            return Text(text)
+        return Link(self.req.build_url('cwetype/%s' % eschema), text)
+
+    def format_rschema(self, rschema, label=None):
+        if label is None:
+            label = rschema.type
+        if self.req is None:
+            return Text(label)
+        return Link(self.req.build_url('cwrtype/%s' % rschema), label)
+
+    # end of no self.req managements
+
     def visit_schema(self, schema, display_relations=0, skiptypes=()):
         """get a layout for a whole schema"""
-        title = Title(self.req._('Schema %s') % schema.name,
+        title = Title(self._('Schema %s') % schema.name,
                       klass='titleUnderline')
         layout = Section(children=(title,))
-        esection = Section(children=(Title(self.req._('Entities'),
+        esection = Section(children=(Title(self._('Entities'),
                                            klass='titleUnderline'),))
         layout.append(esection)
         eschemas = [eschema for eschema in schema.entities()
                     if not (eschema.final or eschema in skiptypes)]
-        for eschema in sorted(eschemas):
+        for eschema in sorted(eschemas, key=TYPE_GETTER):
             esection.append(self.visit_entityschema(eschema, skiptypes))
         if display_relations:
-            title = Title(self.req._('Relations'), klass='titleUnderline')
+            title = Title(self._('Relations'), klass='titleUnderline')
             rsection = Section(children=(title,))
             layout.append(rsection)
-            relations = [rschema for rschema in schema.relations()
+            relations = [rschema for rschema in sorted(schema.relations(), key=TYPE_GETTER)
                          if not (rschema.final or rschema.type in skiptypes)]
             keys = [(rschema.type, rschema) for rschema in relations]
-            for key, rschema in sorted(keys):
+            for key, rschema in sorted(keys, cmp=(lambda x, y: cmp(x[1], y[1]))):
                 relstr = self.visit_relationschema(rschema)
                 rsection.append(relstr)
         return layout
 
     def _entity_attributes_data(self, eschema):
-        _ = self.req._
+        _ = self._
         data = [_('attribute'), _('type'), _('default'), _('constraints')]
-        for rschema, aschema in eschema.attribute_definitions():
+        attributes = sorted(eschema.attribute_definitions(), cmp=(lambda x, y: cmp(x[0].type, y[0].type)))
+        for rschema, aschema in attributes:
             rdef = eschema.rdef(rschema)
-            if not rdef.may_have_permission('read', self.req):
+            if not self.may_read(rdef):
                 continue
             aname = rschema.type
             if aname == 'eid':
@@ -75,11 +108,6 @@
             data.append(', '.join(str(constr) for constr in constraints))
         return data
 
-    def eschema_link_url(self, eschema):
-        return self.req.build_url('cwetype/%s' % eschema)
-
-    def rschema_link_url(self, rschema):
-        return self.req.build_url('cwrtype/%s' % rschema)
 
     def stereotype(self, name):
         return Span((' <<%s>>' % name,), klass='stereotype')
@@ -89,7 +117,7 @@
         etype = eschema.type
         layout = Section(children=' ', klass='clear')
         layout.append(Link(etype,'&#160;' , id=etype)) # anchor
-        title = Link(self.eschema_link_url(eschema), etype)
+        title = self.format_eschema(eschema)
         boxchild = [Section(children=(title,), klass='title')]
         data = []
         data.append(Section(children=boxchild, klass='box'))
@@ -98,13 +126,16 @@
         t_vars = []
         rels = []
         first = True
-        for rschema, targetschemas, role in eschema.relation_definitions():
+
+        rel_defs = sorted(eschema.relation_definitions(),
+                          cmp=(lambda x, y: cmp((x[0].type, x[0].cardinality),
+                          (y[0].type, y[0].cardinality))))
+        for rschema, targetschemas, role in rel_defs:
             if rschema.type in skiptypes:
                 continue
-            rschemaurl = self.rschema_link_url(rschema)
-            for oeschema in targetschemas:
+            for oeschema in sorted(targetschemas, key=TYPE_GETTER):
                 rdef = rschema.role_rdef(eschema, oeschema, role)
-                if not rdef.may_have_permission('read', self.req):
+                if not self.may_read(rdef):
                     continue
                 label = rschema.type
                 if role == 'subject':
@@ -114,8 +145,8 @@
                     cards = cards[::-1]
                 label = '%s %s %s' % (CARD_MAP[cards[1]], label,
                                       CARD_MAP[cards[0]])
-                rlink = Link(rschemaurl, label)
-                elink = Link(self.eschema_link_url(oeschema), oeschema.type)
+                rlink = self.format_rschema(rschema, label)
+                elink = self.format_eschema(oeschema)
                 if first:
                     t_vars.append(Section(children=(elink,), klass='firstvar'))
                     rels.append(Section(children=(rlink,), klass='firstrel'))
@@ -130,9 +161,9 @@
 
     def visit_relationschema(self, rschema, title=True):
         """get a layout for a relation schema"""
-        _ = self.req._
+        _ = self._
         if title:
-            title = Link(self.rschema_link_url(rschema), rschema.type)
+            title = self.format_rschema(rschema)
             stereotypes = []
             if rschema.meta:
                 stereotypes.append('meta')
@@ -140,7 +171,7 @@
                 stereotypes.append('symmetric')
             if rschema.inlined:
                 stereotypes.append('inlined')
-            title = Section(children=(title, ' (%s)'%rschema.display_name(self.req)), klass='title')
+            title = Section(children=(title,), klass='title')
             if stereotypes:
                 title.append(self.stereotype(','.join(stereotypes)))
             layout = Section(children=(title,), klass='schema')
@@ -158,15 +189,15 @@
         data += [_(prop) for prop in properties]
         cols = len(data)
         done = set()
-        for subjtype, objtypes in rschema.associations():
+        for subjtype, objtypes in sorted(rschema.associations()):
             for objtype in objtypes:
                 if (subjtype, objtype) in done:
                     continue
                 done.add((subjtype, objtype))
                 if rschema.symmetric:
                     done.add((objtype, subjtype))
-                data.append(Link(self.eschema_link_url(schema[subjtype]), subjtype))
-                data.append(Link(self.eschema_link_url(schema[objtype]), objtype))
+                data.append(self.format_eschema(schema[subjtype]))
+                data.append(self.format_eschema(schema[objtype]))
                 rdef = rschema.rdef(subjtype, objtype)
                 for prop in properties:
                     val = getattr(rdef, prop)
@@ -174,7 +205,14 @@
                         val = ''
                     elif prop == 'constraints':
                         val = ', '.join([c.restriction for c in val])
+                    elif isinstance(val, dict):
+                        for key, value in val.iteritems():
+                            if isinstance(value, (list, tuple)):
+                                val[key] = ', '.join(sorted( str(v) for v in value))
+                        val = str(val)
+
                     elif isinstance(val, (list, tuple)):
+                        val = sorted(val)
                         val = ', '.join(str(v) for v in val)
                     elif val and isinstance(val, basestring):
                         val = _(val)