author | Sylvain Thénault <sylvain.thenault@logilab.fr> |
Sun, 01 Feb 2015 22:17:25 +0100 | |
changeset 10295 | 080ac14df6fa |
parent 9704 | 43c9896d0e9f |
child 10612 | 84468b90e9c1 |
permissions | -rw-r--r-- |
# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This file is part of CubicWeb. # # CubicWeb is free software: you can redistribute it and/or modify it under the # terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 2.1 of the License, or (at your option) # any later version. # # CubicWeb is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License along # with CubicWeb. If not, see <http://www.gnu.org/licenses/>. """an helper class to display CubicWeb schema using ureports""" __docformat__ = "restructuredtext en" _ = unicode from logilab.common.ureports import Section, Title, Table, Link, Span, Text from yams.schema2dot import CARD_MAP from yams.schema import RelationDefinitionSchema from operator import attrgetter TYPE_GETTER = attrgetter('type') I18NSTRINGS = [_('read'), _('add'), _('delete'), _('update'), _('order')] class SchemaViewer(object): """return a ureport layout for some part of a schema""" def __init__(self, req=None, encoding=None): self.req = req if req is not None: req.add_css('cubicweb.schema.css') if encoding is None: encoding = req.encoding self._ = req._ else: encoding = 'ascii' self._ = unicode self.encoding = encoding # no self.req managements def may_read(self, rdef, action='read'): """Return true if request user may read the given schema. Always return True when no request is provided. """ if self.req is None: return True return rdef.may_have_permission(action, self.req) def format_eschema(self, eschema): text = eschema.type if self.req is None: return Text(text) return Link(self.req.build_url('cwetype/%s' % eschema), text) def format_rschema(self, rschema, label=None): if label is None: label = rschema.type if self.req is None: return Text(label) return Link(self.req.build_url('cwrtype/%s' % rschema), label) # end of no self.req managements def visit_schema(self, schema, display_relations=0, skiptypes=()): """get a layout for a whole schema""" title = Title(self._('Schema %s') % schema.name, klass='titleUnderline') layout = Section(children=(title,)) esection = Section(children=(Title(self._('Entities'), klass='titleUnderline'),)) layout.append(esection) eschemas = [eschema for eschema in schema.entities() if not (eschema.final or eschema in skiptypes)] for eschema in sorted(eschemas, key=TYPE_GETTER): esection.append(self.visit_entityschema(eschema, skiptypes)) if display_relations: title = Title(self._('Relations'), klass='titleUnderline') rsection = Section(children=(title,)) layout.append(rsection) relations = [rschema for rschema in sorted(schema.relations(), key=TYPE_GETTER) if not (rschema.final or rschema.type in skiptypes)] keys = [(rschema.type, rschema) for rschema in relations] for key, rschema in sorted(keys, cmp=(lambda x, y: cmp(x[1], y[1]))): relstr = self.visit_relationschema(rschema) rsection.append(relstr) return layout def _entity_attributes_data(self, eschema): _ = self._ data = [_('attribute'), _('type'), _('default'), _('constraints')] attributes = sorted(eschema.attribute_definitions(), cmp=(lambda x, y: cmp(x[0].type, y[0].type))) for rschema, aschema in attributes: rdef = eschema.rdef(rschema) if not self.may_read(rdef): continue aname = rschema.type if aname == 'eid': continue data.append('%s (%s)' % (aname, _(aname))) data.append(_(aschema.type)) defaultval = eschema.default(aname) if defaultval is not None: default = self.to_string(defaultval) elif rdef.cardinality[0] == '1': default = _('required field') else: default = '' data.append(default) constraints = rschema.rproperty(eschema.type, aschema.type, 'constraints') data.append(', '.join(str(constr) for constr in constraints)) return data def stereotype(self, name): return Span((' <<%s>>' % name,), klass='stereotype') def visit_entityschema(self, eschema, skiptypes=()): """get a layout for an entity schema""" etype = eschema.type layout = Section(children=' ', klass='clear') layout.append(Link(etype,' ' , id=etype)) # anchor title = self.format_eschema(eschema) boxchild = [Section(children=(title,), klass='title')] data = [] data.append(Section(children=boxchild, klass='box')) data.append(Section(children='', klass='vl')) data.append(Section(children='', klass='hl')) t_vars = [] rels = [] first = True rel_defs = sorted(eschema.relation_definitions(), cmp=(lambda x, y: cmp((x[0].type, x[0].cardinality), (y[0].type, y[0].cardinality)))) for rschema, targetschemas, role in rel_defs: if rschema.type in skiptypes: continue for oeschema in sorted(targetschemas, key=TYPE_GETTER): rdef = rschema.role_rdef(eschema, oeschema, role) if not self.may_read(rdef): continue label = rschema.type if role == 'subject': cards = rschema.rdef(eschema, oeschema).cardinality else: cards = rschema.rdef(oeschema, eschema).cardinality cards = cards[::-1] label = '%s %s %s' % (CARD_MAP[cards[1]], label, CARD_MAP[cards[0]]) rlink = self.format_rschema(rschema, label) elink = self.format_eschema(oeschema) if first: t_vars.append(Section(children=(elink,), klass='firstvar')) rels.append(Section(children=(rlink,), klass='firstrel')) first = False else: t_vars.append(Section(children=(elink,), klass='var')) rels.append(Section(children=(rlink,), klass='rel')) data.append(Section(children=rels, klass='rels')) data.append(Section(children=t_vars, klass='vars')) layout.append(Section(children=data, klass='entityAttributes')) return layout def visit_relationschema(self, rschema, title=True): """get a layout for a relation schema""" _ = self._ if title: title = self.format_rschema(rschema) stereotypes = [] if rschema.meta: stereotypes.append('meta') if rschema.symmetric: stereotypes.append('symmetric') if rschema.inlined: stereotypes.append('inlined') title = Section(children=(title,), klass='title') if stereotypes: title.append(self.stereotype(','.join(stereotypes))) layout = Section(children=(title,), klass='schema') else: layout = Section(klass='schema') data = [_('from'), _('to')] schema = rschema.schema rschema_objects = rschema.objects() if rschema_objects: # might be empty properties = [p for p in RelationDefinitionSchema.rproperty_defs(rschema_objects[0]) if not p in ('cardinality', 'composite', 'eid')] else: properties = [] data += [_(prop) for prop in properties] cols = len(data) done = set() for subjtype, objtypes in sorted(rschema.associations()): for objtype in objtypes: if (subjtype, objtype) in done: continue done.add((subjtype, objtype)) if rschema.symmetric: done.add((objtype, subjtype)) data.append(self.format_eschema(schema[subjtype])) data.append(self.format_eschema(schema[objtype])) rdef = rschema.rdef(subjtype, objtype) for prop in properties: val = getattr(rdef, prop) if val is None: val = '' elif prop == 'constraints': val = ', '.join([c.expression for c in val]) elif isinstance(val, dict): for key, value in val.iteritems(): if isinstance(value, (list, tuple)): val[key] = ', '.join(sorted( str(v) for v in value)) val = str(val) elif isinstance(val, (list, tuple)): val = sorted(val) val = ', '.join(str(v) for v in val) elif val and isinstance(val, basestring): val = _(val) else: val = str(val) data.append(Text(val)) table = Table(cols=cols, rheaders=1, children=data, klass='listing') layout.append(Section(children=(table,), klass='relationDefinition')) layout.append(Section(children='', klass='clear')) return layout def to_string(self, value): """used to converte arbitrary values to encoded string""" if isinstance(value, unicode): return value.encode(self.encoding, 'replace') return str(value)