schemaviewer.py
changeset 5239 471554b842d2
parent 5238 31c12863fd9d
child 5241 1460d69672bc
equal deleted inserted replaced
5238:31c12863fd9d 5239:471554b842d2
     1 """an helper class to display CubicWeb schema using ureports
       
     2 
       
     3 :organization: Logilab
       
     4 :copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
       
     5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
       
     6 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
       
     7 """
       
     8 __docformat__ = "restructuredtext en"
       
     9 _ = unicode
       
    10 
       
    11 from logilab.common.ureports import Section, Title, Table, Link, Span, Text
       
    12 
       
    13 from yams.schema2dot import CARD_MAP
       
    14 from yams.schema import RelationDefinitionSchema
       
    15 
       
    16 I18NSTRINGS = [_('read'), _('add'), _('delete'), _('update'), _('order')]
       
    17 
       
    18 
       
    19 class SchemaViewer(object):
       
    20     """return an ureport layout for some part of a schema"""
       
    21     def __init__(self, req=None, encoding=None):
       
    22         self.req = req
       
    23         if req is not None:
       
    24             self.req.add_css('cubicweb.schema.css')
       
    25             if not encoding:
       
    26                 encoding = req.encoding
       
    27         self.encoding = encoding
       
    28 
       
    29     def format_acls(self, schema, access_types):
       
    30         """return a layout displaying access control lists"""
       
    31         data = [self.req._('access type'), self.req._('groups')]
       
    32         for access_type in access_types:
       
    33             data.append(self.req._(access_type))
       
    34             acls = [Link(self.req.build_url('cwgroup/%s' % group), self.req._(group))
       
    35                     for group in schema.get_groups(access_type)]
       
    36             acls += (Text(rqlexp.expression) for rqlexp in schema.get_rqlexprs(access_type))
       
    37             acls = [n for _n in acls for n in (_n, Text(', '))][:-1]
       
    38             data.append(Span(children=acls))
       
    39         return Section(children=(Table(cols=2, cheaders=1, rheaders=1, children=data),),
       
    40                        klass='acl')
       
    41 
       
    42     def visit_schema(self, schema, display_relations=0, skiptypes=()):
       
    43         """get a layout for a whole schema"""
       
    44         title = Title(self.req._('Schema %s') % schema.name,
       
    45                       klass='titleUnderline')
       
    46         layout = Section(children=(title,))
       
    47         esection = Section(children=(Title(self.req._('Entities'),
       
    48                                            klass='titleUnderline'),))
       
    49         layout.append(esection)
       
    50         eschemas = [eschema for eschema in schema.entities()
       
    51                     if not (eschema.final or eschema in skiptypes)]
       
    52         for eschema in sorted(eschemas):
       
    53             esection.append(self.visit_entityschema(eschema, skiptypes))
       
    54         if display_relations:
       
    55             title = Title(self.req._('Relations'), klass='titleUnderline')
       
    56             rsection = Section(children=(title,))
       
    57             layout.append(rsection)
       
    58             relations = [rschema for rschema in schema.relations()
       
    59                          if not (rschema.final or rschema.type in skiptypes)]
       
    60             keys = [(rschema.type, rschema) for rschema in relations]
       
    61             for key, rschema in sorted(keys):
       
    62                 relstr = self.visit_relationschema(rschema)
       
    63                 rsection.append(relstr)
       
    64         return layout
       
    65 
       
    66     def _entity_attributes_data(self, eschema):
       
    67         _ = self.req._
       
    68         data = [_('attribute'), _('type'), _('default'), _('constraints')]
       
    69         for rschema, aschema in eschema.attribute_definitions():
       
    70             rdef = eschema.rdef(rschema)
       
    71             if not rdef.may_have_permission('read', self.req):
       
    72                 continue
       
    73             aname = rschema.type
       
    74             if aname == 'eid':
       
    75                 continue
       
    76             data.append('%s (%s)' % (aname, _(aname)))
       
    77             data.append(_(aschema.type))
       
    78             defaultval = eschema.default(aname)
       
    79             if defaultval is not None:
       
    80                 default = self.to_string(defaultval)
       
    81             elif rdef.cardinality[0] == '1':
       
    82                 default = _('required field')
       
    83             else:
       
    84                 default = ''
       
    85             data.append(default)
       
    86             constraints = rschema.rproperty(eschema.type, aschema.type,
       
    87                                             'constraints')
       
    88             data.append(', '.join(str(constr) for constr in constraints))
       
    89         return data
       
    90 
       
    91     def eschema_link_url(self, eschema):
       
    92         return self.req.build_url('cwetype/%s' % eschema)
       
    93 
       
    94     def rschema_link_url(self, rschema):
       
    95         return self.req.build_url('cwrtype/%s' % rschema)
       
    96 
       
    97     def stereotype(self, name):
       
    98         return Span((' <<%s>>' % name,), klass='stereotype')
       
    99 
       
   100     def visit_entityschema(self, eschema, skiptypes=()):
       
   101         """get a layout for an entity schema"""
       
   102         etype = eschema.type
       
   103         layout = Section(children=' ', klass='clear')
       
   104         layout.append(Link(etype,'&#160;' , id=etype)) # anchor
       
   105         title = Link(self.eschema_link_url(eschema), etype)
       
   106         boxchild = [Section(children=(title, ' (%s)'% eschema.display_name(self.req)), klass='title')]
       
   107         data = []
       
   108         data.append(Section(children=boxchild, klass='box'))
       
   109         data.append(Section(children='', klass='vl'))
       
   110         data.append(Section(children='', klass='hl'))
       
   111         t_vars = []
       
   112         rels = []
       
   113         first = True
       
   114         for rschema, targetschemas, role in eschema.relation_definitions():
       
   115             if rschema.type in skiptypes:
       
   116                 continue
       
   117             rschemaurl = self.rschema_link_url(rschema)
       
   118             for oeschema in targetschemas:
       
   119                 rdef = rschema.role_rdef(eschema, oeschema, role)
       
   120                 if not rdef.may_have_permission('read', self.req):
       
   121                     continue
       
   122                 label = rschema.type
       
   123                 if role == 'subject':
       
   124                     cards = rschema.rproperty(eschema, oeschema, 'cardinality')
       
   125                 else:
       
   126                     cards = rschema.rproperty(oeschema, eschema, 'cardinality')
       
   127                     cards = cards[::-1]
       
   128                 label = '%s %s (%s) %s' % (CARD_MAP[cards[1]], label,
       
   129                                            display_name(self.req, label, role),
       
   130                                            CARD_MAP[cards[0]])
       
   131                 rlink = Link(rschemaurl, label)
       
   132                 elink = Link(self.eschema_link_url(oeschema), oeschema.type)
       
   133                 if first:
       
   134                     t_vars.append(Section(children=(elink,), klass='firstvar'))
       
   135                     rels.append(Section(children=(rlink,), klass='firstrel'))
       
   136                     first = False
       
   137                 else:
       
   138                     t_vars.append(Section(children=(elink,), klass='var'))
       
   139                     rels.append(Section(children=(rlink,), klass='rel'))
       
   140         data.append(Section(children=rels, klass='rels'))
       
   141         data.append(Section(children=t_vars, klass='vars'))
       
   142         layout.append(Section(children=data, klass='entityAttributes'))
       
   143         return layout
       
   144 
       
   145     def visit_relationschema(self, rschema, title=True):
       
   146         """get a layout for a relation schema"""
       
   147         _ = self.req._
       
   148         if title:
       
   149             title = Link(self.rschema_link_url(rschema), rschema.type)
       
   150             stereotypes = []
       
   151             if rschema.meta:
       
   152                 stereotypes.append('meta')
       
   153             if rschema.symmetric:
       
   154                 stereotypes.append('symmetric')
       
   155             if rschema.inlined:
       
   156                 stereotypes.append('inlined')
       
   157             title = Section(children=(title, ' (%s)'%rschema.display_name(self.req)), klass='title')
       
   158             if stereotypes:
       
   159                 title.append(self.stereotype(','.join(stereotypes)))
       
   160             layout = Section(children=(title,), klass='schema')
       
   161         else:
       
   162             layout = Section(klass='schema')
       
   163         data = [_('from'), _('to')]
       
   164         schema = rschema.schema
       
   165         rschema_objects = rschema.objects()
       
   166         if rschema_objects:
       
   167             # might be empty
       
   168             properties = [p for p in RelationDefinitionSchema.rproperty_defs(rschema_objects[0])
       
   169                           if not p in ('cardinality', 'composite', 'eid')]
       
   170         else:
       
   171             properties = []
       
   172         data += [_(prop) for prop in properties]
       
   173         cols = len(data)
       
   174         done = set()
       
   175         for subjtype, objtypes in rschema.associations():
       
   176             for objtype in objtypes:
       
   177                 if (subjtype, objtype) in done:
       
   178                     continue
       
   179                 done.add((subjtype, objtype))
       
   180                 if rschema.symmetric:
       
   181                     done.add((objtype, subjtype))
       
   182                 data.append(Link(self.eschema_link_url(schema[subjtype]), subjtype))
       
   183                 data.append(Link(self.eschema_link_url(schema[objtype]), objtype))
       
   184                 rdef = rschema.rdef(subjtype, objtype)
       
   185                 for prop in properties:
       
   186                     val = getattr(rdef, prop)
       
   187                     if val is None:
       
   188                         val = ''
       
   189                     elif prop == 'constraints':
       
   190                         val = ', '.join([c.restriction for c in val])
       
   191                     elif isinstance(val, (list, tuple)):
       
   192                         val = ', '.join(str(v) for v in val)
       
   193                     elif val and isinstance(val, basestring):
       
   194                         val = _(val)
       
   195                     else:
       
   196                         val = str(val)
       
   197                     data.append(Text(val))
       
   198         table = Table(cols=cols, rheaders=1, children=data, klass='listing')
       
   199         layout.append(Section(children=(table,), klass='relationDefinition'))
       
   200         #if self.req.user.matching_groups('managers'):
       
   201         #    layout.append(self.format_acls(rschema, ('read', 'add', 'delete')))
       
   202         layout.append(Section(children='', klass='clear'))
       
   203         return layout
       
   204 
       
   205     def to_string(self, value):
       
   206         """used to converte arbitrary values to encoded string"""
       
   207         if isinstance(value, unicode):
       
   208             return value.encode(self.encoding, 'replace')
       
   209         return str(value)