web/views/schema.py
branchtls-sprint
changeset 1091 b5e253c0dd13
parent 1012 0de202b907d6
child 1132 96752791c2b6
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/web/views/schema.py	Thu Mar 12 16:29:00 2009 +0100
@@ -0,0 +1,220 @@
+"""Specific views for schema related entities
+
+:organization: Logilab
+:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
+"""
+__docformat__ = "restructuredtext en"
+
+from itertools import cycle
+
+from logilab.mtconverter import html_escape
+from logilab.common.graph import escape, GraphGenerator, DotBackend
+from yams import schema2dot as s2d
+
+from cubicweb.selectors import implements, rql_condition, yes
+from cubicweb.schemaviewer import SchemaViewer
+from cubicweb.view import EntityView, StartupView
+from cubicweb.common.uilib import ureport_as_html
+from cubicweb.web.action import Action
+from cubicweb.web.views import baseviews
+from cubicweb.web.views import TmpFileViewMixin
+
+
+class ViewSchemaAction(Action):
+    id = 'schema'
+    __select__ = yes()
+    
+    title = _("site schema")
+    category = 'siteactions'
+    order = 30
+    
+    def url(self):
+        return self.build_url(self.id)
+
+        
+# schema entity types views ###################################################
+
+class _SchemaEntityPrimaryView(baseviews.PrimaryView):
+    show_attr_label = False
+    cache_max_age = 60*60*2 # stay in http cache for 2 hours by default 
+    
+    def content_title(self, entity):
+        return html_escape(entity.dc_long_title())
+    
+class EETypePrimaryView(_SchemaEntityPrimaryView):
+    __select__ = implements('EEType')
+    skip_attrs = _SchemaEntityPrimaryView.skip_attrs + ('name', 'meta', 'final')
+
+class ERTypePrimaryView(_SchemaEntityPrimaryView):
+    __select__ = implements('ERType')
+    skip_attrs = _SchemaEntityPrimaryView.skip_attrs + ('name', 'meta', 'final',
+                                                        'symetric', 'inlined')
+
+class ErdefPrimaryView(_SchemaEntityPrimaryView):
+    __select__ = implements('EFRDef', 'ENFRDef')
+    show_attr_label = True
+
+class EETypeOneLineView(baseviews.OneLineView):
+    __select__ = implements('EEType')
+    
+    def cell_call(self, row, col, **kwargs):
+        entity = self.entity(row, col)
+        final = entity.final
+        if final:
+            self.w(u'<em class="finalentity">')
+        super(EETypeOneLineView, self).cell_call(row, col, **kwargs)
+        if final:
+            self.w(u'</em>')
+
+
+# in memory schema views (yams class instances) ###############################
+
+class EETypeSchemaView(EETypePrimaryView):
+    id = 'eschema'
+    title = _('in memory entity schema')
+    main_related_section = False
+    skip_rels = ('is', 'is_instance_of', 'identity', 'created_by', 'owned_by',
+                 'has_text',)
+    
+    def render_entity_attributes(self, entity, siderelations):
+        super(EETypeSchemaView, self).render_entity_attributes(entity, siderelations)
+        eschema = self.vreg.schema.eschema(entity.name)
+        viewer = SchemaViewer(self.req)
+        layout = viewer.visit_entityschema(eschema, skiprels=self.skip_rels)
+        self.w(ureport_as_html(layout))
+        if not eschema.is_final():
+            self.w(u'<img src="%s" alt="%s"/>' % (
+                html_escape(entity.absolute_url(vid='eschemagraph')),
+                html_escape(self.req._('graphical schema for %s') % entity.name)))
+
+
+class ERTypeSchemaView(ERTypePrimaryView):
+    id = 'eschema'
+    title = _('in memory relation schema')
+    main_related_section = False
+
+    def render_entity_attributes(self, entity, siderelations):
+        super(ERTypeSchemaView, self).render_entity_attributes(entity, siderelations)
+        rschema = self.vreg.schema.rschema(entity.name)
+        viewer = SchemaViewer(self.req)
+        layout = viewer.visit_relationschema(rschema)
+        self.w(ureport_as_html(layout))
+        if not rschema.is_final():
+            self.w(u'<img src="%s" alt="%s"/>' % (
+                html_escape(entity.absolute_url(vid='eschemagraph')),
+                html_escape(self.req._('graphical schema for %s') % entity.name)))
+        
+
+# schema images ###############################################################
+
+class ImageView(EntityView):
+    __select__ = implements('EEType')
+    id = 'image'
+    title = _('image')
+
+    def cell_call(self, row, col):
+        entity = self.entity(row, col)
+        url = entity.absolute_url(vid='eschemagraph')
+        self.w(u'<img src="%s" alt="%s"/>' % (
+            html_escape(url),
+            html_escape(self.req._('graphical schema for %s') % entity.name)))
+
+
+class RestrictedSchemaDotPropsHandler(s2d.SchemaDotPropsHandler):
+    def __init__(self, req):
+        # FIXME: colors are arbitrary
+        self.nextcolor = cycle( ('#aa0000', '#00aa00', '#0000aa',
+                                 '#000000', '#888888') ).next
+        self.req = req
+        
+    def display_attr(self, rschema):
+        return not rschema.meta and (rschema.has_local_role('read')
+                                     or rschema.has_perm(self.req, 'read'))
+    
+    # XXX remove this method once yams > 0.20 is out
+    def node_properties(self, eschema):
+        """return default DOT drawing options for an entity schema"""
+        label = ['{',eschema.type,'|']
+        label.append(r'\l'.join(rel.type for rel in eschema.subject_relations()
+                                if rel.final and self.display_attr(rel)))
+        label.append(r'\l}') # trailing \l ensure alignement of the last one
+        return {'label' : ''.join(label), 'shape' : "record",
+                'fontname' : "Courier", 'style' : "filled"}
+
+    def edge_properties(self, rschema, subjnode, objnode):
+        kwargs = super(RestrictedSchemaDotPropsHandler, self).edge_properties(rschema, subjnode, objnode)
+        # symetric rels are handled differently, let yams decide what's best
+        if not rschema.symetric:
+            kwargs['color'] = self.nextcolor()
+        kwargs['fontcolor'] = kwargs['color']
+        # dot label decoration is just awful (1 line underlining the label
+        # + 1 line going to the closest edge spline point)
+        kwargs['decorate'] = 'false'
+        return kwargs
+    
+
+class RestrictedSchemaVisitorMiIn:
+    def __init__(self, req, *args, **kwargs):
+        # hack hack hack
+        assert len(self.__class__.__bases__) == 2
+        self.__parent = self.__class__.__bases__[1]
+        self.__parent.__init__(self, *args, **kwargs)
+        self.req = req
+        
+    def nodes(self):
+        for etype, eschema in self.__parent.nodes(self):
+            if eschema.has_local_role('read') or eschema.has_perm(self.req, 'read'):
+                yield eschema.type, eschema
+            
+    def edges(self):
+        for setype, oetype, rschema in self.__parent.edges(self):
+            if rschema.has_local_role('read') or rschema.has_perm(self.req, 'read'):
+                yield setype, oetype, rschema
+
+
+class FullSchemaVisitor(RestrictedSchemaVisitorMiIn, s2d.FullSchemaVisitor):
+    pass
+
+class OneHopESchemaVisitor(RestrictedSchemaVisitorMiIn, s2d.OneHopESchemaVisitor):
+    pass
+
+class OneHopRSchemaVisitor(RestrictedSchemaVisitorMiIn, s2d.OneHopRSchemaVisitor):
+    pass
+
+
+class SchemaImageView(TmpFileViewMixin, StartupView):
+    id = 'schemagraph'
+    content_type = 'image/png'
+    skip_rels = ('owned_by', 'created_by', 'identity', 'is', 'is_instance_of')
+    def _generate(self, tmpfile):
+        """display global schema information"""
+        skipmeta = not int(self.req.form.get('withmeta', 0))
+        visitor = FullSchemaVisitor(self.req, self.schema, skiprels=self.skip_rels, skipmeta=skipmeta)
+        s2d.schema2dot(outputfile=tmpfile, visitor=visitor,
+                       prophdlr=RestrictedSchemaDotPropsHandler(self.req))
+
+class EETypeSchemaImageView(TmpFileViewMixin, EntityView):
+    id = 'eschemagraph'
+    content_type = 'image/png'
+    __select__ = implements('EEType')
+    skip_rels = ('owned_by', 'created_by', 'identity', 'is', 'is_instance_of')
+    
+    def _generate(self, tmpfile):
+        """display schema information for an entity"""
+        entity = self.entity(self.row, self.col)
+        eschema = self.vreg.schema.eschema(entity.name)
+        visitor = OneHopESchemaVisitor(self.req, eschema, skiprels=self.skip_rels)
+        s2d.schema2dot(outputfile=tmpfile, visitor=visitor,
+                       prophdlr=RestrictedSchemaDotPropsHandler(self.req))
+
+class ERTypeSchemaImageView(EETypeSchemaImageView):
+    __select__ = implements('ERType')
+    
+    def _generate(self, tmpfile):
+        """display schema information for an entity"""
+        entity = self.entity(self.row, self.col)
+        rschema = self.vreg.schema.rschema(entity.name)
+        visitor = OneHopRSchemaVisitor(self.req, rschema)
+        s2d.schema2dot(outputfile=tmpfile, visitor=visitor,
+                       prophdlr=RestrictedSchemaDotPropsHandler(self.req))