web/views/dynimages.py
changeset 0 b97547f5f1fa
child 55 5ff3ca010290
equal deleted inserted replaced
-1:000000000000 0:b97547f5f1fa
       
     1 """dynamically generated image views
       
     2 
       
     3 :organization: Logilab
       
     4 :copyright: 2001-2008 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 import os
       
    10 from tempfile import mktemp
       
    11 
       
    12 from logilab.common.graph import escape, GraphGenerator, DotBackend
       
    13 from yams import schema2dot as s2d
       
    14 
       
    15 from cubicweb.common.view import EntityView, StartupView
       
    16 
       
    17 
       
    18 class RestrictedSchemaDotPropsHandler(s2d.SchemaDotPropsHandler):
       
    19     def __init__(self, req):
       
    20         self.req = req
       
    21         
       
    22     def display_attr(self, rschema):
       
    23         return not rschema.meta and (rschema.has_local_role('read')
       
    24                                      or rschema.has_perm(self.req, 'read'))
       
    25     
       
    26     # XXX remove this method once yams > 0.20 is out
       
    27     def node_properties(self, eschema):
       
    28         """return default DOT drawing options for an entity schema"""
       
    29         label = ['{',eschema.type,'|']
       
    30         label.append(r'\l'.join(rel.type for rel in eschema.subject_relations()
       
    31                                 if rel.final and self.display_attr(rel)))
       
    32         label.append(r'\l}') # trailing \l ensure alignement of the last one
       
    33         return {'label' : ''.join(label), 'shape' : "record",
       
    34                 'fontname' : "Courier", 'style' : "filled"}
       
    35     
       
    36 class RestrictedSchemaVisitorMiIn:
       
    37     def __init__(self, req, *args, **kwargs):
       
    38         # hack hack hack
       
    39         assert len(self.__class__.__bases__) == 2
       
    40         self.__parent = self.__class__.__bases__[1]
       
    41         self.__parent.__init__(self, *args, **kwargs)
       
    42         self.req = req
       
    43         
       
    44     def nodes(self):
       
    45         for etype, eschema in self.__parent.nodes(self):
       
    46             if eschema.has_local_role('read') or eschema.has_perm(self.req, 'read'):
       
    47                 yield eschema.type, eschema
       
    48             
       
    49     def edges(self):
       
    50         for setype, oetype, rschema in self.__parent.edges(self):
       
    51             if rschema.has_local_role('read') or rschema.has_perm(self.req, 'read'):
       
    52                 yield setype, oetype, rschema
       
    53 
       
    54 class FullSchemaVisitor(RestrictedSchemaVisitorMiIn, s2d.FullSchemaVisitor):
       
    55     pass
       
    56 
       
    57 class OneHopESchemaVisitor(RestrictedSchemaVisitorMiIn, s2d.OneHopESchemaVisitor):
       
    58     pass
       
    59 
       
    60 class OneHopRSchemaVisitor(RestrictedSchemaVisitorMiIn, s2d.OneHopRSchemaVisitor):
       
    61     pass
       
    62                 
       
    63         
       
    64 class TmpFileViewMixin(object):
       
    65     binary = True
       
    66     content_type = 'application/octet-stream'
       
    67     cache_max_age = 60*60*2 # stay in http cache for 2 hours by default 
       
    68     
       
    69     def call(self):
       
    70         self.cell_call()
       
    71         
       
    72     def cell_call(self, row=0, col=0):
       
    73         self.row, self.col = row, col # in case one need it
       
    74         tmpfile = mktemp('.png')
       
    75         try:
       
    76             self._generate(tmpfile)
       
    77             self.w(open(tmpfile).read())
       
    78         finally:
       
    79             os.unlink(tmpfile)
       
    80     
       
    81 class SchemaImageView(TmpFileViewMixin, StartupView):
       
    82     id = 'schemagraph'
       
    83     content_type = 'image/png'
       
    84     skip_rels = ('owned_by', 'created_by', 'identity', 'is', 'is_instance_of')
       
    85     def _generate(self, tmpfile):
       
    86         """display global schema information"""
       
    87         skipmeta = not int(self.req.form.get('withmeta', 0))
       
    88         visitor = FullSchemaVisitor(self.req, self.schema, skiprels=self.skip_rels, skipmeta=skipmeta)
       
    89         s2d.schema2dot(outputfile=tmpfile, visitor=visitor,
       
    90                        prophdlr=RestrictedSchemaDotPropsHandler(self.req))
       
    91 
       
    92 class EETypeSchemaImageView(TmpFileViewMixin, EntityView):
       
    93     id = 'eschemagraph'
       
    94     content_type = 'image/png'
       
    95     accepts = ('EEType',)
       
    96     skip_rels = ('owned_by', 'created_by', 'identity', 'is', 'is_instance_of')
       
    97     
       
    98     def _generate(self, tmpfile):
       
    99         """display schema information for an entity"""
       
   100         entity = self.entity(self.row, self.col)
       
   101         eschema = self.vreg.schema.eschema(entity.name)
       
   102         visitor = OneHopESchemaVisitor(self.req, eschema, skiprels=self.skip_rels)
       
   103         s2d.schema2dot(outputfile=tmpfile, visitor=visitor,
       
   104                        prophdlr=RestrictedSchemaDotPropsHandler(self.req))
       
   105 
       
   106 class ERTypeSchemaImageView(EETypeSchemaImageView):
       
   107     accepts = ('ERType',)
       
   108     
       
   109     def _generate(self, tmpfile):
       
   110         """display schema information for an entity"""
       
   111         entity = self.entity(self.row, self.col)
       
   112         rschema = self.vreg.schema.rschema(entity.name)
       
   113         visitor = OneHopRSchemaVisitor(self.req, rschema)
       
   114         s2d.schema2dot(outputfile=tmpfile, visitor=visitor,
       
   115                        prophdlr=RestrictedSchemaDotPropsHandler(self.req))
       
   116 
       
   117 
       
   118 
       
   119 class WorkflowDotPropsHandler(object):
       
   120     def __init__(self, req):
       
   121         self._ = req._
       
   122         
       
   123     def node_properties(self, stateortransition):
       
   124         """return default DOT drawing options for a state or transition"""
       
   125         props = {'label': stateortransition.name, 
       
   126                  'fontname': 'Courier'}
       
   127         if hasattr(stateortransition, 'state_of'):
       
   128             props['shape'] = 'box'
       
   129             props['style'] = 'filled'
       
   130             if stateortransition.reverse_initial_state:
       
   131                 props['color'] = '#88CC88'
       
   132         else:
       
   133             props['shape'] = 'ellipse'
       
   134             descr = []
       
   135             tr = stateortransition
       
   136             if tr.require_group:
       
   137                 descr.append('%s %s'% (
       
   138                     self._('groups:'),
       
   139                     ','.join(g.name for g in tr.require_group)))
       
   140             if tr.condition:
       
   141                 descr.append('%s %s'% (self._('condition:'), tr.condition))
       
   142             if descr:
       
   143                 props['label'] += escape('\n'.join(descr))
       
   144         return props
       
   145     
       
   146     def edge_properties(self, transition, fromstate, tostate):
       
   147         return {'label': '', 'dir': 'forward',
       
   148                 'color': 'black', 'style': 'filled'}
       
   149 
       
   150 class WorkflowVisitor:
       
   151     def __init__(self, entity):
       
   152         self.entity = entity
       
   153 
       
   154     def nodes(self):
       
   155         for state in self.entity.reverse_state_of:
       
   156             state.complete()
       
   157             yield state.eid, state
       
   158             
       
   159         for transition in self.entity.reverse_transition_of:
       
   160             transition.complete()
       
   161             yield transition.eid, transition
       
   162             
       
   163     def edges(self):
       
   164         for transition in self.entity.reverse_transition_of:
       
   165             for incomingstate in transition.reverse_allowed_transition:
       
   166                 yield incomingstate.eid, transition.eid, transition
       
   167             yield transition.eid, transition.destination().eid, transition
       
   168 
       
   169 
       
   170 class EETypeWorkflowImageView(TmpFileViewMixin, EntityView):
       
   171     id = 'ewfgraph'
       
   172     content_type = 'image/png'
       
   173     accepts = ('EEType',)
       
   174     
       
   175     def _generate(self, tmpfile):
       
   176         """display schema information for an entity"""
       
   177         entity = self.entity(self.row, self.col)
       
   178         visitor = WorkflowVisitor(entity)
       
   179         prophdlr = WorkflowDotPropsHandler(self.req)
       
   180         generator = GraphGenerator(DotBackend('workflow', 'LR',
       
   181                                               ratio='compress', size='30,12'))
       
   182         return generator.generate(visitor, prophdlr, tmpfile)