web/views/schema.py
branchtls-sprint
changeset 1091 b5e253c0dd13
parent 1012 0de202b907d6
child 1132 96752791c2b6
equal deleted inserted replaced
1090:a99dc223c583 1091:b5e253c0dd13
       
     1 """Specific views for schema related entities
       
     2 
       
     3 :organization: Logilab
       
     4 :copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
       
     5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
       
     6 """
       
     7 __docformat__ = "restructuredtext en"
       
     8 
       
     9 from itertools import cycle
       
    10 
       
    11 from logilab.mtconverter import html_escape
       
    12 from logilab.common.graph import escape, GraphGenerator, DotBackend
       
    13 from yams import schema2dot as s2d
       
    14 
       
    15 from cubicweb.selectors import implements, rql_condition, yes
       
    16 from cubicweb.schemaviewer import SchemaViewer
       
    17 from cubicweb.view import EntityView, StartupView
       
    18 from cubicweb.common.uilib import ureport_as_html
       
    19 from cubicweb.web.action import Action
       
    20 from cubicweb.web.views import baseviews
       
    21 from cubicweb.web.views import TmpFileViewMixin
       
    22 
       
    23 
       
    24 class ViewSchemaAction(Action):
       
    25     id = 'schema'
       
    26     __select__ = yes()
       
    27     
       
    28     title = _("site schema")
       
    29     category = 'siteactions'
       
    30     order = 30
       
    31     
       
    32     def url(self):
       
    33         return self.build_url(self.id)
       
    34 
       
    35         
       
    36 # schema entity types views ###################################################
       
    37 
       
    38 class _SchemaEntityPrimaryView(baseviews.PrimaryView):
       
    39     show_attr_label = False
       
    40     cache_max_age = 60*60*2 # stay in http cache for 2 hours by default 
       
    41     
       
    42     def content_title(self, entity):
       
    43         return html_escape(entity.dc_long_title())
       
    44     
       
    45 class EETypePrimaryView(_SchemaEntityPrimaryView):
       
    46     __select__ = implements('EEType')
       
    47     skip_attrs = _SchemaEntityPrimaryView.skip_attrs + ('name', 'meta', 'final')
       
    48 
       
    49 class ERTypePrimaryView(_SchemaEntityPrimaryView):
       
    50     __select__ = implements('ERType')
       
    51     skip_attrs = _SchemaEntityPrimaryView.skip_attrs + ('name', 'meta', 'final',
       
    52                                                         'symetric', 'inlined')
       
    53 
       
    54 class ErdefPrimaryView(_SchemaEntityPrimaryView):
       
    55     __select__ = implements('EFRDef', 'ENFRDef')
       
    56     show_attr_label = True
       
    57 
       
    58 class EETypeOneLineView(baseviews.OneLineView):
       
    59     __select__ = implements('EEType')
       
    60     
       
    61     def cell_call(self, row, col, **kwargs):
       
    62         entity = self.entity(row, col)
       
    63         final = entity.final
       
    64         if final:
       
    65             self.w(u'<em class="finalentity">')
       
    66         super(EETypeOneLineView, self).cell_call(row, col, **kwargs)
       
    67         if final:
       
    68             self.w(u'</em>')
       
    69 
       
    70 
       
    71 # in memory schema views (yams class instances) ###############################
       
    72 
       
    73 class EETypeSchemaView(EETypePrimaryView):
       
    74     id = 'eschema'
       
    75     title = _('in memory entity schema')
       
    76     main_related_section = False
       
    77     skip_rels = ('is', 'is_instance_of', 'identity', 'created_by', 'owned_by',
       
    78                  'has_text',)
       
    79     
       
    80     def render_entity_attributes(self, entity, siderelations):
       
    81         super(EETypeSchemaView, self).render_entity_attributes(entity, siderelations)
       
    82         eschema = self.vreg.schema.eschema(entity.name)
       
    83         viewer = SchemaViewer(self.req)
       
    84         layout = viewer.visit_entityschema(eschema, skiprels=self.skip_rels)
       
    85         self.w(ureport_as_html(layout))
       
    86         if not eschema.is_final():
       
    87             self.w(u'<img src="%s" alt="%s"/>' % (
       
    88                 html_escape(entity.absolute_url(vid='eschemagraph')),
       
    89                 html_escape(self.req._('graphical schema for %s') % entity.name)))
       
    90 
       
    91 
       
    92 class ERTypeSchemaView(ERTypePrimaryView):
       
    93     id = 'eschema'
       
    94     title = _('in memory relation schema')
       
    95     main_related_section = False
       
    96 
       
    97     def render_entity_attributes(self, entity, siderelations):
       
    98         super(ERTypeSchemaView, self).render_entity_attributes(entity, siderelations)
       
    99         rschema = self.vreg.schema.rschema(entity.name)
       
   100         viewer = SchemaViewer(self.req)
       
   101         layout = viewer.visit_relationschema(rschema)
       
   102         self.w(ureport_as_html(layout))
       
   103         if not rschema.is_final():
       
   104             self.w(u'<img src="%s" alt="%s"/>' % (
       
   105                 html_escape(entity.absolute_url(vid='eschemagraph')),
       
   106                 html_escape(self.req._('graphical schema for %s') % entity.name)))
       
   107         
       
   108 
       
   109 # schema images ###############################################################
       
   110 
       
   111 class ImageView(EntityView):
       
   112     __select__ = implements('EEType')
       
   113     id = 'image'
       
   114     title = _('image')
       
   115 
       
   116     def cell_call(self, row, col):
       
   117         entity = self.entity(row, col)
       
   118         url = entity.absolute_url(vid='eschemagraph')
       
   119         self.w(u'<img src="%s" alt="%s"/>' % (
       
   120             html_escape(url),
       
   121             html_escape(self.req._('graphical schema for %s') % entity.name)))
       
   122 
       
   123 
       
   124 class RestrictedSchemaDotPropsHandler(s2d.SchemaDotPropsHandler):
       
   125     def __init__(self, req):
       
   126         # FIXME: colors are arbitrary
       
   127         self.nextcolor = cycle( ('#aa0000', '#00aa00', '#0000aa',
       
   128                                  '#000000', '#888888') ).next
       
   129         self.req = req
       
   130         
       
   131     def display_attr(self, rschema):
       
   132         return not rschema.meta and (rschema.has_local_role('read')
       
   133                                      or rschema.has_perm(self.req, 'read'))
       
   134     
       
   135     # XXX remove this method once yams > 0.20 is out
       
   136     def node_properties(self, eschema):
       
   137         """return default DOT drawing options for an entity schema"""
       
   138         label = ['{',eschema.type,'|']
       
   139         label.append(r'\l'.join(rel.type for rel in eschema.subject_relations()
       
   140                                 if rel.final and self.display_attr(rel)))
       
   141         label.append(r'\l}') # trailing \l ensure alignement of the last one
       
   142         return {'label' : ''.join(label), 'shape' : "record",
       
   143                 'fontname' : "Courier", 'style' : "filled"}
       
   144 
       
   145     def edge_properties(self, rschema, subjnode, objnode):
       
   146         kwargs = super(RestrictedSchemaDotPropsHandler, self).edge_properties(rschema, subjnode, objnode)
       
   147         # symetric rels are handled differently, let yams decide what's best
       
   148         if not rschema.symetric:
       
   149             kwargs['color'] = self.nextcolor()
       
   150         kwargs['fontcolor'] = kwargs['color']
       
   151         # dot label decoration is just awful (1 line underlining the label
       
   152         # + 1 line going to the closest edge spline point)
       
   153         kwargs['decorate'] = 'false'
       
   154         return kwargs
       
   155     
       
   156 
       
   157 class RestrictedSchemaVisitorMiIn:
       
   158     def __init__(self, req, *args, **kwargs):
       
   159         # hack hack hack
       
   160         assert len(self.__class__.__bases__) == 2
       
   161         self.__parent = self.__class__.__bases__[1]
       
   162         self.__parent.__init__(self, *args, **kwargs)
       
   163         self.req = req
       
   164         
       
   165     def nodes(self):
       
   166         for etype, eschema in self.__parent.nodes(self):
       
   167             if eschema.has_local_role('read') or eschema.has_perm(self.req, 'read'):
       
   168                 yield eschema.type, eschema
       
   169             
       
   170     def edges(self):
       
   171         for setype, oetype, rschema in self.__parent.edges(self):
       
   172             if rschema.has_local_role('read') or rschema.has_perm(self.req, 'read'):
       
   173                 yield setype, oetype, rschema
       
   174 
       
   175 
       
   176 class FullSchemaVisitor(RestrictedSchemaVisitorMiIn, s2d.FullSchemaVisitor):
       
   177     pass
       
   178 
       
   179 class OneHopESchemaVisitor(RestrictedSchemaVisitorMiIn, s2d.OneHopESchemaVisitor):
       
   180     pass
       
   181 
       
   182 class OneHopRSchemaVisitor(RestrictedSchemaVisitorMiIn, s2d.OneHopRSchemaVisitor):
       
   183     pass
       
   184 
       
   185 
       
   186 class SchemaImageView(TmpFileViewMixin, StartupView):
       
   187     id = 'schemagraph'
       
   188     content_type = 'image/png'
       
   189     skip_rels = ('owned_by', 'created_by', 'identity', 'is', 'is_instance_of')
       
   190     def _generate(self, tmpfile):
       
   191         """display global schema information"""
       
   192         skipmeta = not int(self.req.form.get('withmeta', 0))
       
   193         visitor = FullSchemaVisitor(self.req, self.schema, skiprels=self.skip_rels, skipmeta=skipmeta)
       
   194         s2d.schema2dot(outputfile=tmpfile, visitor=visitor,
       
   195                        prophdlr=RestrictedSchemaDotPropsHandler(self.req))
       
   196 
       
   197 class EETypeSchemaImageView(TmpFileViewMixin, EntityView):
       
   198     id = 'eschemagraph'
       
   199     content_type = 'image/png'
       
   200     __select__ = implements('EEType')
       
   201     skip_rels = ('owned_by', 'created_by', 'identity', 'is', 'is_instance_of')
       
   202     
       
   203     def _generate(self, tmpfile):
       
   204         """display schema information for an entity"""
       
   205         entity = self.entity(self.row, self.col)
       
   206         eschema = self.vreg.schema.eschema(entity.name)
       
   207         visitor = OneHopESchemaVisitor(self.req, eschema, skiprels=self.skip_rels)
       
   208         s2d.schema2dot(outputfile=tmpfile, visitor=visitor,
       
   209                        prophdlr=RestrictedSchemaDotPropsHandler(self.req))
       
   210 
       
   211 class ERTypeSchemaImageView(EETypeSchemaImageView):
       
   212     __select__ = implements('ERType')
       
   213     
       
   214     def _generate(self, tmpfile):
       
   215         """display schema information for an entity"""
       
   216         entity = self.entity(self.row, self.col)
       
   217         rschema = self.vreg.schema.rschema(entity.name)
       
   218         visitor = OneHopRSchemaVisitor(self.req, rschema)
       
   219         s2d.schema2dot(outputfile=tmpfile, visitor=visitor,
       
   220                        prophdlr=RestrictedSchemaDotPropsHandler(self.req))