web/schemaviewer.py
changeset 5239 471554b842d2
parent 5234 e2476d78b060
child 5372 b74eed7e8b37
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 visit_schema(self, schema, display_relations=0, skiptypes=()):
       
    30         """get a layout for a whole schema"""
       
    31         title = Title(self.req._('Schema %s') % schema.name,
       
    32                       klass='titleUnderline')
       
    33         layout = Section(children=(title,))
       
    34         esection = Section(children=(Title(self.req._('Entities'),
       
    35                                            klass='titleUnderline'),))
       
    36         layout.append(esection)
       
    37         eschemas = [eschema for eschema in schema.entities()
       
    38                     if not (eschema.final or eschema in skiptypes)]
       
    39         for eschema in sorted(eschemas):
       
    40             esection.append(self.visit_entityschema(eschema, skiptypes))
       
    41         if display_relations:
       
    42             title = Title(self.req._('Relations'), klass='titleUnderline')
       
    43             rsection = Section(children=(title,))
       
    44             layout.append(rsection)
       
    45             relations = [rschema for rschema in schema.relations()
       
    46                          if not (rschema.final or rschema.type in skiptypes)]
       
    47             keys = [(rschema.type, rschema) for rschema in relations]
       
    48             for key, rschema in sorted(keys):
       
    49                 relstr = self.visit_relationschema(rschema)
       
    50                 rsection.append(relstr)
       
    51         return layout
       
    52 
       
    53     def _entity_attributes_data(self, eschema):
       
    54         _ = self.req._
       
    55         data = [_('attribute'), _('type'), _('default'), _('constraints')]
       
    56         for rschema, aschema in eschema.attribute_definitions():
       
    57             rdef = eschema.rdef(rschema)
       
    58             if not rdef.may_have_permission('read', self.req):
       
    59                 continue
       
    60             aname = rschema.type
       
    61             if aname == 'eid':
       
    62                 continue
       
    63             data.append('%s (%s)' % (aname, _(aname)))
       
    64             data.append(_(aschema.type))
       
    65             defaultval = eschema.default(aname)
       
    66             if defaultval is not None:
       
    67                 default = self.to_string(defaultval)
       
    68             elif rdef.cardinality[0] == '1':
       
    69                 default = _('required field')
       
    70             else:
       
    71                 default = ''
       
    72             data.append(default)
       
    73             constraints = rschema.rproperty(eschema.type, aschema.type,
       
    74                                             'constraints')
       
    75             data.append(', '.join(str(constr) for constr in constraints))
       
    76         return data
       
    77 
       
    78     def eschema_link_url(self, eschema):
       
    79         return self.req.build_url('cwetype/%s' % eschema)
       
    80 
       
    81     def rschema_link_url(self, rschema):
       
    82         return self.req.build_url('cwrtype/%s' % rschema)
       
    83 
       
    84     def stereotype(self, name):
       
    85         return Span((' <<%s>>' % name,), klass='stereotype')
       
    86 
       
    87     def visit_entityschema(self, eschema, skiptypes=()):
       
    88         """get a layout for an entity schema"""
       
    89         etype = eschema.type
       
    90         layout = Section(children=' ', klass='clear')
       
    91         layout.append(Link(etype,'&#160;' , id=etype)) # anchor
       
    92         title = Link(self.eschema_link_url(eschema), etype)
       
    93         boxchild = [Section(children=(title,), klass='title')]
       
    94         data = []
       
    95         data.append(Section(children=boxchild, klass='box'))
       
    96         data.append(Section(children='', klass='vl'))
       
    97         data.append(Section(children='', klass='hl'))
       
    98         t_vars = []
       
    99         rels = []
       
   100         first = True
       
   101         for rschema, targetschemas, role in eschema.relation_definitions():
       
   102             if rschema.type in skiptypes:
       
   103                 continue
       
   104             rschemaurl = self.rschema_link_url(rschema)
       
   105             for oeschema in targetschemas:
       
   106                 rdef = rschema.role_rdef(eschema, oeschema, role)
       
   107                 if not rdef.may_have_permission('read', self.req):
       
   108                     continue
       
   109                 label = rschema.type
       
   110                 if role == 'subject':
       
   111                     cards = rschema.rproperty(eschema, oeschema, 'cardinality')
       
   112                 else:
       
   113                     cards = rschema.rproperty(oeschema, eschema, 'cardinality')
       
   114                     cards = cards[::-1]
       
   115                 label = '%s %s %s' % (CARD_MAP[cards[1]], label,
       
   116                                       CARD_MAP[cards[0]])
       
   117                 rlink = Link(rschemaurl, label)
       
   118                 elink = Link(self.eschema_link_url(oeschema), oeschema.type)
       
   119                 if first:
       
   120                     t_vars.append(Section(children=(elink,), klass='firstvar'))
       
   121                     rels.append(Section(children=(rlink,), klass='firstrel'))
       
   122                     first = False
       
   123                 else:
       
   124                     t_vars.append(Section(children=(elink,), klass='var'))
       
   125                     rels.append(Section(children=(rlink,), klass='rel'))
       
   126         data.append(Section(children=rels, klass='rels'))
       
   127         data.append(Section(children=t_vars, klass='vars'))
       
   128         layout.append(Section(children=data, klass='entityAttributes'))
       
   129         return layout
       
   130 
       
   131     def visit_relationschema(self, rschema, title=True):
       
   132         """get a layout for a relation schema"""
       
   133         _ = self.req._
       
   134         if title:
       
   135             title = Link(self.rschema_link_url(rschema), rschema.type)
       
   136             stereotypes = []
       
   137             if rschema.meta:
       
   138                 stereotypes.append('meta')
       
   139             if rschema.symmetric:
       
   140                 stereotypes.append('symmetric')
       
   141             if rschema.inlined:
       
   142                 stereotypes.append('inlined')
       
   143             title = Section(children=(title, ' (%s)'%rschema.display_name(self.req)), klass='title')
       
   144             if stereotypes:
       
   145                 title.append(self.stereotype(','.join(stereotypes)))
       
   146             layout = Section(children=(title,), klass='schema')
       
   147         else:
       
   148             layout = Section(klass='schema')
       
   149         data = [_('from'), _('to')]
       
   150         schema = rschema.schema
       
   151         rschema_objects = rschema.objects()
       
   152         if rschema_objects:
       
   153             # might be empty
       
   154             properties = [p for p in RelationDefinitionSchema.rproperty_defs(rschema_objects[0])
       
   155                           if not p in ('cardinality', 'composite', 'eid')]
       
   156         else:
       
   157             properties = []
       
   158         data += [_(prop) for prop in properties]
       
   159         cols = len(data)
       
   160         done = set()
       
   161         for subjtype, objtypes in rschema.associations():
       
   162             for objtype in objtypes:
       
   163                 if (subjtype, objtype) in done:
       
   164                     continue
       
   165                 done.add((subjtype, objtype))
       
   166                 if rschema.symmetric:
       
   167                     done.add((objtype, subjtype))
       
   168                 data.append(Link(self.eschema_link_url(schema[subjtype]), subjtype))
       
   169                 data.append(Link(self.eschema_link_url(schema[objtype]), objtype))
       
   170                 rdef = rschema.rdef(subjtype, objtype)
       
   171                 for prop in properties:
       
   172                     val = getattr(rdef, prop)
       
   173                     if val is None:
       
   174                         val = ''
       
   175                     elif prop == 'constraints':
       
   176                         val = ', '.join([c.restriction for c in val])
       
   177                     elif isinstance(val, (list, tuple)):
       
   178                         val = ', '.join(str(v) for v in val)
       
   179                     elif val and isinstance(val, basestring):
       
   180                         val = _(val)
       
   181                     else:
       
   182                         val = str(val)
       
   183                     data.append(Text(val))
       
   184         table = Table(cols=cols, rheaders=1, children=data, klass='listing')
       
   185         layout.append(Section(children=(table,), klass='relationDefinition'))
       
   186         layout.append(Section(children='', klass='clear'))
       
   187         return layout
       
   188 
       
   189     def to_string(self, value):
       
   190         """used to converte arbitrary values to encoded string"""
       
   191         if isinstance(value, unicode):
       
   192             return value.encode(self.encoding, 'replace')
       
   193         return str(value)