diff -r a99dc223c583 -r b5e253c0dd13 web/views/schema.py --- /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'') + super(EETypeOneLineView, self).cell_call(row, col, **kwargs) + if final: + self.w(u'') + + +# 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'%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'%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'%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))