|
1 # copyright 2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. |
|
2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr |
|
3 # |
|
4 # This file is part of CubicWeb. |
|
5 # |
|
6 # CubicWeb is free software: you can redistribute it and/or modify it under the |
|
7 # terms of the GNU Lesser General Public License as published by the Free |
|
8 # Software Foundation, either version 2.1 of the License, or (at your option) |
|
9 # any later version. |
|
10 # |
|
11 # CubicWeb is distributed in the hope that it will be useful, but WITHOUT |
|
12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
|
13 # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more |
|
14 # details. |
|
15 # |
|
16 # You should have received a copy of the GNU Lesser General Public License along |
|
17 # with CubicWeb. If not, see <http://www.gnu.org/licenses/>. |
|
18 """Specific views for data sources""" |
|
19 |
|
20 __docformat__ = "restructuredtext en" |
|
21 _ = unicode |
|
22 |
|
23 from itertools import repeat, chain |
|
24 |
|
25 from cubicweb.selectors import is_instance, score_entity |
|
26 from cubicweb.view import EntityView |
|
27 from cubicweb.schema import META_RTYPES, VIRTUAL_RTYPES, display_name |
|
28 from cubicweb.web import uicfg |
|
29 from cubicweb.web.views import tabs |
|
30 |
|
31 for rtype in ('cw_support', 'cw_may_cross', 'cw_dont_cross'): |
|
32 uicfg.primaryview_section.tag_subject_of(('CWSource', rtype, '*'), |
|
33 'hidden') |
|
34 |
|
35 class CWSourcePrimaryView(tabs.TabbedPrimaryView): |
|
36 __select__ = is_instance('CWSource') |
|
37 tabs = [_('cwsource-main'), _('cwsource-mapping')] |
|
38 default_tab = 'cwsource-main' |
|
39 |
|
40 |
|
41 class CWSourceMainTab(tabs.PrimaryTab): |
|
42 __regid__ = 'cwsource-main' |
|
43 __select__ = tabs.PrimaryTab.__select__ & is_instance('CWSource') |
|
44 |
|
45 |
|
46 class CWSourceMappingTab(EntityView): |
|
47 __regid__ = 'cwsource-mapping' |
|
48 __select__ = (tabs.PrimaryTab.__select__ & is_instance('CWSource') |
|
49 & score_entity(lambda x:x.type == 'pyrorql')) |
|
50 |
|
51 def entity_call(self, entity): |
|
52 _ = self._cw._ |
|
53 self.w('<h3>%s</h3>' % _('Entity and relation types supported by this source')) |
|
54 self.wview('list', entity.related('cw_support'), 'noresult') |
|
55 self.w('<h3>%s</h3>' % _('Relations that should not be crossed')) |
|
56 self.w('<p>%s</p>' % _( |
|
57 'By default, when a relation is not supported by a source, it is ' |
|
58 'supposed that a local relation may point to an entity from the ' |
|
59 'external source. Relations listed here won\'t have this ' |
|
60 '"crossing" behaviour.')) |
|
61 self.wview('list', entity.related('cw_dont_cross'), 'noresult') |
|
62 self.w('<h3>%s</h3>' % _('Relations that can be crossed')) |
|
63 self.w('<p>%s</p>' % _( |
|
64 'By default, when a relation is supported by a source, it is ' |
|
65 'supposed that a local relation can\'t point to an entity from the ' |
|
66 'external source. Relations listed here may have this ' |
|
67 '"crossing" behaviour anyway.')) |
|
68 self.wview('list', entity.related('cw_may_cross'), 'noresult') |
|
69 if self._cw.user.is_in_group('managers'): |
|
70 errors, warnings, infos = check_mapping(entity) |
|
71 if (errors or warnings or infos): |
|
72 self.w('<h2>%s</h2>' % _('Detected problems')) |
|
73 errors = zip(repeat(_('error'), errors)) |
|
74 warnings = zip(repeat(_('warning'), warnings)) |
|
75 infos = zip(repeat(_('warning'), infos)) |
|
76 self.wview('pyvaltable', pyvalue=chain(errors, warnings, infos)) |
|
77 |
|
78 def check_mapping(cwsource): |
|
79 req = cwsource._cw |
|
80 _ = req._ |
|
81 errors = [] |
|
82 error = errors.append |
|
83 warnings = [] |
|
84 warning = warnings.append |
|
85 infos = [] |
|
86 info = infos.append |
|
87 srelations = set() |
|
88 sentities = set() |
|
89 maycross = set() |
|
90 dontcross = set() |
|
91 # first check supported stuff / meta & virtual types and get mapping as sets |
|
92 for cwertype in cwsource.cw_support: |
|
93 if cwertype.name in META_RTYPES: |
|
94 error(_('meta relation %s can not be supported') % cwertype.name) |
|
95 else: |
|
96 if cwertype.__regid__ == 'CWEType': |
|
97 sentities.add(cwertype.name) |
|
98 else: |
|
99 srelations.add(cwertype.name) |
|
100 for attr, attrset in (('cw_may_cross', maycross), |
|
101 ('cw_dont_cross', dontcross)): |
|
102 for cwrtype in getattr(cwsource, attr): |
|
103 if cwrtype.name in VIRTUAL_RTYPES: |
|
104 error(_('virtual relation %(rtype)s can not be referenced by ' |
|
105 'the "%(srel)s" relation') % |
|
106 {'rtype': cwrtype.name, |
|
107 'srel': display_name(req, attr, context='CWSource')}) |
|
108 else: |
|
109 attrset.add(cwrtype.name) |
|
110 # check relation in dont_cross_relations aren't in support_relations |
|
111 for rtype in dontcross & maycross: |
|
112 info(_('relation %(rtype)s is supported but in %(dontcross)s') % |
|
113 {'rtype': rtype, |
|
114 'dontcross': display_name(req, 'cw_dont_cross', |
|
115 context='CWSource')}) |
|
116 # check relation in cross_relations are in support_relations |
|
117 for rtype in maycross & srelations: |
|
118 info(_('relation %(rtype)s isn\'t supported but in %(maycross)s') % |
|
119 {'rtype': rtype, |
|
120 'dontcross': display_name(req, 'cw_may_cross', |
|
121 context='CWSource')}) |
|
122 # now check for more handy things |
|
123 seen = set() |
|
124 for etype in sentities: |
|
125 eschema = req.vreg.schema[etype] |
|
126 for rschema, ttypes, role in eschema.relation_definitions(): |
|
127 if rschema in META_RTYPES: |
|
128 continue |
|
129 ttypes = [ttype for ttype in ttypes if ttype in sentities] |
|
130 if not rschema in srelations: |
|
131 somethingprinted = False |
|
132 for ttype in ttypes: |
|
133 rdef = rschema.role_rdef(etype, ttype, role) |
|
134 seen.add(rdef) |
|
135 if rdef.role_cardinality(role) in '1+': |
|
136 error(_('relation %(type)s with %(etype)s as %(role)s ' |
|
137 'and target type %(target)s is mandatory but ' |
|
138 'not supported') % |
|
139 {'rtype': rschema, 'etype': etype, 'role': role, |
|
140 'target': ttype}) |
|
141 somethingprinted = True |
|
142 elif ttype in sentities: |
|
143 if rdef not in seen: |
|
144 warning(_('%s could be supported') % rdef) |
|
145 somethingprinted = True |
|
146 if rschema not in dontcross: |
|
147 if role == 'subject' and rschema.inlined: |
|
148 error(_('inlined relation %(rtype)s of %(etype)s ' |
|
149 'should be supported') % |
|
150 {'rtype': rschema, 'etype': etype}) |
|
151 elif (not somethingprinted and rschema not in seen |
|
152 and rschema not in maycross): |
|
153 info(_('you may want to specify something for %s') % |
|
154 rschema) |
|
155 seen.add(rschema) |
|
156 else: |
|
157 if not ttypes: |
|
158 warning(_('relation %(rtype)s with %(etype)s as %(role)s ' |
|
159 'is supported but no target type supported') % |
|
160 {'rtype': rschema, 'role': role, 'etype': etype}) |
|
161 if rschema in maycross and rschema.inlined: |
|
162 error(_('you should un-inline relation %s which is ' |
|
163 'supported and may be crossed ') % rschema) |
|
164 for rschema in srelations: |
|
165 for subj, obj in rschema.rdefs: |
|
166 if subj in sentities and obj in sentities: |
|
167 break |
|
168 else: |
|
169 error(_('relation %s is supported but none if its definitions ' |
|
170 'matches supported entities') % rschema) |
|
171 return errors, warnings, infos |