web/schemaviewer.py
changeset 5372 b74eed7e8b37
parent 5239 471554b842d2
child 5423 e15abfdcce38
equal deleted inserted replaced
5364:e0feefb87de5 5372:b74eed7e8b37
    10 
    10 
    11 from logilab.common.ureports import Section, Title, Table, Link, Span, Text
    11 from logilab.common.ureports import Section, Title, Table, Link, Span, Text
    12 
    12 
    13 from yams.schema2dot import CARD_MAP
    13 from yams.schema2dot import CARD_MAP
    14 from yams.schema import RelationDefinitionSchema
    14 from yams.schema import RelationDefinitionSchema
       
    15 from operator import attrgetter
       
    16 
       
    17 TYPE_GETTER = attrgetter('type')
    15 
    18 
    16 I18NSTRINGS = [_('read'), _('add'), _('delete'), _('update'), _('order')]
    19 I18NSTRINGS = [_('read'), _('add'), _('delete'), _('update'), _('order')]
    17 
    20 
    18 
    21 
    19 class SchemaViewer(object):
    22 class SchemaViewer(object):
    20     """return an ureport layout for some part of a schema"""
    23     """return an ureport layout for some part of a schema"""
    21     def __init__(self, req=None, encoding=None):
    24     def __init__(self, req=None, encoding=None):
    22         self.req = req
    25         self.req = req
    23         if req is not None:
    26         if req is not None:
    24             self.req.add_css('cubicweb.schema.css')
    27             req.add_css('cubicweb.schema.css')
    25             if not encoding:
    28             if encoding is None:
    26                 encoding = req.encoding
    29                 encoding = req.encoding
       
    30             self._ = req._
       
    31         else:
       
    32             encoding = 'ascii'
       
    33             self._ = unicode
    27         self.encoding = encoding
    34         self.encoding = encoding
       
    35 
       
    36     # no self.req managements
       
    37 
       
    38     def may_read(self, rdef, action):
       
    39         """Return true if request user may read the given schema.
       
    40         Always return True when no request is provided.
       
    41         """
       
    42         if self.req is None:
       
    43             return True
       
    44         return sch.may_have_permission('read', self.req)
       
    45 
       
    46     def format_eschema(self, eschema):
       
    47         text = eschema.type
       
    48         if self.req is None:
       
    49             return Text(text)
       
    50         return Link(self.req.build_url('cwetype/%s' % eschema), text)
       
    51 
       
    52     def format_rschema(self, rschema, label=None):
       
    53         if label is None:
       
    54             label = rschema.type
       
    55         if self.req is None:
       
    56             return Text(label)
       
    57         return Link(self.req.build_url('cwrtype/%s' % rschema), label)
       
    58 
       
    59     # end of no self.req managements
    28 
    60 
    29     def visit_schema(self, schema, display_relations=0, skiptypes=()):
    61     def visit_schema(self, schema, display_relations=0, skiptypes=()):
    30         """get a layout for a whole schema"""
    62         """get a layout for a whole schema"""
    31         title = Title(self.req._('Schema %s') % schema.name,
    63         title = Title(self._('Schema %s') % schema.name,
    32                       klass='titleUnderline')
    64                       klass='titleUnderline')
    33         layout = Section(children=(title,))
    65         layout = Section(children=(title,))
    34         esection = Section(children=(Title(self.req._('Entities'),
    66         esection = Section(children=(Title(self._('Entities'),
    35                                            klass='titleUnderline'),))
    67                                            klass='titleUnderline'),))
    36         layout.append(esection)
    68         layout.append(esection)
    37         eschemas = [eschema for eschema in schema.entities()
    69         eschemas = [eschema for eschema in schema.entities()
    38                     if not (eschema.final or eschema in skiptypes)]
    70                     if not (eschema.final or eschema in skiptypes)]
    39         for eschema in sorted(eschemas):
    71         for eschema in sorted(eschemas, key=TYPE_GETTER):
    40             esection.append(self.visit_entityschema(eschema, skiptypes))
    72             esection.append(self.visit_entityschema(eschema, skiptypes))
    41         if display_relations:
    73         if display_relations:
    42             title = Title(self.req._('Relations'), klass='titleUnderline')
    74             title = Title(self._('Relations'), klass='titleUnderline')
    43             rsection = Section(children=(title,))
    75             rsection = Section(children=(title,))
    44             layout.append(rsection)
    76             layout.append(rsection)
    45             relations = [rschema for rschema in schema.relations()
    77             relations = [rschema for rschema in sorted(schema.relations(), key=TYPE_GETTER)
    46                          if not (rschema.final or rschema.type in skiptypes)]
    78                          if not (rschema.final or rschema.type in skiptypes)]
    47             keys = [(rschema.type, rschema) for rschema in relations]
    79             keys = [(rschema.type, rschema) for rschema in relations]
    48             for key, rschema in sorted(keys):
    80             for key, rschema in sorted(keys, cmp=(lambda x, y: cmp(x[1], y[1]))):
    49                 relstr = self.visit_relationschema(rschema)
    81                 relstr = self.visit_relationschema(rschema)
    50                 rsection.append(relstr)
    82                 rsection.append(relstr)
    51         return layout
    83         return layout
    52 
    84 
    53     def _entity_attributes_data(self, eschema):
    85     def _entity_attributes_data(self, eschema):
    54         _ = self.req._
    86         _ = self._
    55         data = [_('attribute'), _('type'), _('default'), _('constraints')]
    87         data = [_('attribute'), _('type'), _('default'), _('constraints')]
    56         for rschema, aschema in eschema.attribute_definitions():
    88         attributes = sorted(eschema.attribute_definitions(), cmp=(lambda x, y: cmp(x[0].type, y[0].type)))
       
    89         for rschema, aschema in attributes:
    57             rdef = eschema.rdef(rschema)
    90             rdef = eschema.rdef(rschema)
    58             if not rdef.may_have_permission('read', self.req):
    91             if not self.may_read(rdef):
    59                 continue
    92                 continue
    60             aname = rschema.type
    93             aname = rschema.type
    61             if aname == 'eid':
    94             if aname == 'eid':
    62                 continue
    95                 continue
    63             data.append('%s (%s)' % (aname, _(aname)))
    96             data.append('%s (%s)' % (aname, _(aname)))
    73             constraints = rschema.rproperty(eschema.type, aschema.type,
   106             constraints = rschema.rproperty(eschema.type, aschema.type,
    74                                             'constraints')
   107                                             'constraints')
    75             data.append(', '.join(str(constr) for constr in constraints))
   108             data.append(', '.join(str(constr) for constr in constraints))
    76         return data
   109         return data
    77 
   110 
    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 
   111 
    84     def stereotype(self, name):
   112     def stereotype(self, name):
    85         return Span((' <<%s>>' % name,), klass='stereotype')
   113         return Span((' <<%s>>' % name,), klass='stereotype')
    86 
   114 
    87     def visit_entityschema(self, eschema, skiptypes=()):
   115     def visit_entityschema(self, eschema, skiptypes=()):
    88         """get a layout for an entity schema"""
   116         """get a layout for an entity schema"""
    89         etype = eschema.type
   117         etype = eschema.type
    90         layout = Section(children=' ', klass='clear')
   118         layout = Section(children=' ', klass='clear')
    91         layout.append(Link(etype,'&#160;' , id=etype)) # anchor
   119         layout.append(Link(etype,'&#160;' , id=etype)) # anchor
    92         title = Link(self.eschema_link_url(eschema), etype)
   120         title = self.format_eschema(eschema)
    93         boxchild = [Section(children=(title,), klass='title')]
   121         boxchild = [Section(children=(title,), klass='title')]
    94         data = []
   122         data = []
    95         data.append(Section(children=boxchild, klass='box'))
   123         data.append(Section(children=boxchild, klass='box'))
    96         data.append(Section(children='', klass='vl'))
   124         data.append(Section(children='', klass='vl'))
    97         data.append(Section(children='', klass='hl'))
   125         data.append(Section(children='', klass='hl'))
    98         t_vars = []
   126         t_vars = []
    99         rels = []
   127         rels = []
   100         first = True
   128         first = True
   101         for rschema, targetschemas, role in eschema.relation_definitions():
   129 
       
   130         rel_defs = sorted(eschema.relation_definitions(),
       
   131                           cmp=(lambda x, y: cmp((x[0].type, x[0].cardinality),
       
   132                           (y[0].type, y[0].cardinality))))
       
   133         for rschema, targetschemas, role in rel_defs:
   102             if rschema.type in skiptypes:
   134             if rschema.type in skiptypes:
   103                 continue
   135                 continue
   104             rschemaurl = self.rschema_link_url(rschema)
   136             for oeschema in sorted(targetschemas, key=TYPE_GETTER):
   105             for oeschema in targetschemas:
       
   106                 rdef = rschema.role_rdef(eschema, oeschema, role)
   137                 rdef = rschema.role_rdef(eschema, oeschema, role)
   107                 if not rdef.may_have_permission('read', self.req):
   138                 if not self.may_read(rdef):
   108                     continue
   139                     continue
   109                 label = rschema.type
   140                 label = rschema.type
   110                 if role == 'subject':
   141                 if role == 'subject':
   111                     cards = rschema.rproperty(eschema, oeschema, 'cardinality')
   142                     cards = rschema.rproperty(eschema, oeschema, 'cardinality')
   112                 else:
   143                 else:
   113                     cards = rschema.rproperty(oeschema, eschema, 'cardinality')
   144                     cards = rschema.rproperty(oeschema, eschema, 'cardinality')
   114                     cards = cards[::-1]
   145                     cards = cards[::-1]
   115                 label = '%s %s %s' % (CARD_MAP[cards[1]], label,
   146                 label = '%s %s %s' % (CARD_MAP[cards[1]], label,
   116                                       CARD_MAP[cards[0]])
   147                                       CARD_MAP[cards[0]])
   117                 rlink = Link(rschemaurl, label)
   148                 rlink = self.format_rschema(rschema, label)
   118                 elink = Link(self.eschema_link_url(oeschema), oeschema.type)
   149                 elink = self.format_eschema(oeschema)
   119                 if first:
   150                 if first:
   120                     t_vars.append(Section(children=(elink,), klass='firstvar'))
   151                     t_vars.append(Section(children=(elink,), klass='firstvar'))
   121                     rels.append(Section(children=(rlink,), klass='firstrel'))
   152                     rels.append(Section(children=(rlink,), klass='firstrel'))
   122                     first = False
   153                     first = False
   123                 else:
   154                 else:
   128         layout.append(Section(children=data, klass='entityAttributes'))
   159         layout.append(Section(children=data, klass='entityAttributes'))
   129         return layout
   160         return layout
   130 
   161 
   131     def visit_relationschema(self, rschema, title=True):
   162     def visit_relationschema(self, rschema, title=True):
   132         """get a layout for a relation schema"""
   163         """get a layout for a relation schema"""
   133         _ = self.req._
   164         _ = self._
   134         if title:
   165         if title:
   135             title = Link(self.rschema_link_url(rschema), rschema.type)
   166             title = self.format_rschema(rschema)
   136             stereotypes = []
   167             stereotypes = []
   137             if rschema.meta:
   168             if rschema.meta:
   138                 stereotypes.append('meta')
   169                 stereotypes.append('meta')
   139             if rschema.symmetric:
   170             if rschema.symmetric:
   140                 stereotypes.append('symmetric')
   171                 stereotypes.append('symmetric')
   141             if rschema.inlined:
   172             if rschema.inlined:
   142                 stereotypes.append('inlined')
   173                 stereotypes.append('inlined')
   143             title = Section(children=(title, ' (%s)'%rschema.display_name(self.req)), klass='title')
   174             title = Section(children=(title,), klass='title')
   144             if stereotypes:
   175             if stereotypes:
   145                 title.append(self.stereotype(','.join(stereotypes)))
   176                 title.append(self.stereotype(','.join(stereotypes)))
   146             layout = Section(children=(title,), klass='schema')
   177             layout = Section(children=(title,), klass='schema')
   147         else:
   178         else:
   148             layout = Section(klass='schema')
   179             layout = Section(klass='schema')
   156         else:
   187         else:
   157             properties = []
   188             properties = []
   158         data += [_(prop) for prop in properties]
   189         data += [_(prop) for prop in properties]
   159         cols = len(data)
   190         cols = len(data)
   160         done = set()
   191         done = set()
   161         for subjtype, objtypes in rschema.associations():
   192         for subjtype, objtypes in sorted(rschema.associations()):
   162             for objtype in objtypes:
   193             for objtype in objtypes:
   163                 if (subjtype, objtype) in done:
   194                 if (subjtype, objtype) in done:
   164                     continue
   195                     continue
   165                 done.add((subjtype, objtype))
   196                 done.add((subjtype, objtype))
   166                 if rschema.symmetric:
   197                 if rschema.symmetric:
   167                     done.add((objtype, subjtype))
   198                     done.add((objtype, subjtype))
   168                 data.append(Link(self.eschema_link_url(schema[subjtype]), subjtype))
   199                 data.append(self.format_eschema(schema[subjtype]))
   169                 data.append(Link(self.eschema_link_url(schema[objtype]), objtype))
   200                 data.append(self.format_eschema(schema[objtype]))
   170                 rdef = rschema.rdef(subjtype, objtype)
   201                 rdef = rschema.rdef(subjtype, objtype)
   171                 for prop in properties:
   202                 for prop in properties:
   172                     val = getattr(rdef, prop)
   203                     val = getattr(rdef, prop)
   173                     if val is None:
   204                     if val is None:
   174                         val = ''
   205                         val = ''
   175                     elif prop == 'constraints':
   206                     elif prop == 'constraints':
   176                         val = ', '.join([c.restriction for c in val])
   207                         val = ', '.join([c.restriction for c in val])
       
   208                     elif isinstance(val, dict):
       
   209                         for key, value in val.iteritems():
       
   210                             if isinstance(value, (list, tuple)):
       
   211                                 val[key] = ', '.join(sorted( str(v) for v in value))
       
   212                         val = str(val)
       
   213 
   177                     elif isinstance(val, (list, tuple)):
   214                     elif isinstance(val, (list, tuple)):
       
   215                         val = sorted(val)
   178                         val = ', '.join(str(v) for v in val)
   216                         val = ', '.join(str(v) for v in val)
   179                     elif val and isinstance(val, basestring):
   217                     elif val and isinstance(val, basestring):
   180                         val = _(val)
   218                         val = _(val)
   181                     else:
   219                     else:
   182                         val = str(val)
   220                         val = str(val)