|
1 # copyright 2003-2011 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 """produces some Ontology Web Language schema and views |
|
19 |
|
20 """ |
|
21 __docformat__ = "restructuredtext en" |
|
22 from cubicweb import _ |
|
23 |
|
24 from six.moves import range |
|
25 |
|
26 from logilab.mtconverter import TransformError, xml_escape |
|
27 |
|
28 from cubicweb.view import StartupView, EntityView |
|
29 from cubicweb.predicates import none_rset, match_view |
|
30 from cubicweb.web.action import Action |
|
31 from cubicweb.web.views import schema |
|
32 |
|
33 OWL_CARD_MAP = {'1': '<rdf:type rdf:resource="&owl;FunctionalProperty"/>', |
|
34 '?': '<owl:maxCardinality rdf:datatype="&xsd;int">1</owl:maxCardinality>', |
|
35 '+': '<owl:minCardinality rdf:datatype="&xsd;int">1</owl:minCardinality>', |
|
36 '*': '' |
|
37 } |
|
38 |
|
39 OWL_TYPE_MAP = {'String': 'xsd:string', |
|
40 'Bytes': 'xsd:byte', |
|
41 'Password': 'xsd:byte', |
|
42 |
|
43 'Boolean': 'xsd:boolean', |
|
44 'Int': 'xsd:int', |
|
45 'BigInt': 'xsd:int', |
|
46 'Float': 'xsd:float', |
|
47 'Decimal' : 'xsd:decimal', |
|
48 |
|
49 'Date':'xsd:date', |
|
50 'Datetime': 'xsd:dateTime', |
|
51 'TZDatetime': 'xsd:dateTime', |
|
52 'Time': 'xsd:time', |
|
53 'TZTime': 'xsd:time', |
|
54 'Interval': 'xsd:duration' |
|
55 } |
|
56 |
|
57 OWL_OPENING_ROOT = u'''<?xml version="1.0" encoding="UTF-8"?> |
|
58 <!DOCTYPE rdf:RDF [ |
|
59 <!ENTITY owl "http://www.w3.org/2002/07/owl#" > |
|
60 <!ENTITY xsd "http://www.w3.org/2001/XMLSchema#" > |
|
61 ]> |
|
62 <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" |
|
63 xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#" |
|
64 xmlns:xsd="http://www.w3.org/2001/XMLSchema#" |
|
65 xmlns:owl="http://www.w3.org/2002/07/owl#" |
|
66 xmlns="http://logilab.org/owl/ontologies/%(appid)s#" |
|
67 xmlns:%(appid)s="http://logilab.org/owl/ontologies/%(appid)s#" |
|
68 xmlns:base="http://logilab.org/owl/ontologies/%(appid)s"> |
|
69 |
|
70 <owl:Ontology rdf:about=""> |
|
71 <rdfs:comment> |
|
72 %(appid)s Cubicweb OWL Ontology |
|
73 </rdfs:comment> |
|
74 </owl:Ontology>''' |
|
75 |
|
76 OWL_CLOSING_ROOT = u'</rdf:RDF>' |
|
77 |
|
78 |
|
79 class OWLView(StartupView): |
|
80 """This view export in owl format schema database. It is the TBOX""" |
|
81 __regid__ = 'owl' |
|
82 title = _('owl') |
|
83 templatable = False |
|
84 content_type = 'application/xml' # 'text/xml' |
|
85 |
|
86 def call(self, writeprefix=True): |
|
87 skipmeta = int(self._cw.form.get('skipmeta', True)) |
|
88 if writeprefix: |
|
89 self.w(OWL_OPENING_ROOT % {'appid': self._cw.vreg.schema.name}) |
|
90 self.visit_schema(skiptypes=skipmeta and schema.SKIP_TYPES or ()) |
|
91 if writeprefix: |
|
92 self.w(OWL_CLOSING_ROOT) |
|
93 |
|
94 def should_display_rschema(self, eschema, rschema, role): |
|
95 return not rschema in self.skiptypes and ( |
|
96 rschema.may_have_permission('read', self._cw, eschema, role)) |
|
97 |
|
98 def visit_schema(self, skiptypes): |
|
99 """get a layout for a whole schema""" |
|
100 self.skiptypes = skiptypes |
|
101 entities = sorted(eschema for eschema in self._cw.vreg.schema.entities() |
|
102 if not eschema.final or eschema in skiptypes) |
|
103 self.w(u'<!-- classes definition -->') |
|
104 for eschema in entities: |
|
105 self.visit_entityschema(eschema) |
|
106 self.w(u'<!-- property definition -->') |
|
107 self.visit_property_schema(eschema) |
|
108 self.w(u'<!-- datatype property -->') |
|
109 self.visit_property_object_schema(eschema) |
|
110 |
|
111 def visit_entityschema(self, eschema): |
|
112 """get a layout for an entity OWL schema""" |
|
113 self.w(u'<owl:Class rdf:ID="%s">'% eschema) |
|
114 self.w(u'<!-- relations -->') |
|
115 for rschema, targetschemas, role in eschema.relation_definitions(): |
|
116 if not self.should_display_rschema(eschema, rschema, role): |
|
117 continue |
|
118 for oeschema in targetschemas: |
|
119 card = rschema.role_rdef(eschema, oeschema, role).role_cardinality(role) |
|
120 cardtag = OWL_CARD_MAP[card] |
|
121 if cardtag: |
|
122 self.w(u'''<rdfs:subClassOf> |
|
123 <owl:Restriction> |
|
124 <owl:onProperty rdf:resource="#%s"/> |
|
125 %s |
|
126 </owl:Restriction> |
|
127 </rdfs:subClassOf>''' % (rschema, cardtag)) |
|
128 |
|
129 self.w(u'<!-- attributes -->') |
|
130 for rschema, aschema in eschema.attribute_definitions(): |
|
131 if not self.should_display_rschema(eschema, rschema, 'subject'): |
|
132 continue |
|
133 self.w(u'''<rdfs:subClassOf> |
|
134 <owl:Restriction> |
|
135 <owl:onProperty rdf:resource="#%s"/> |
|
136 <rdf:type rdf:resource="&owl;FunctionalProperty"/> |
|
137 </owl:Restriction> |
|
138 </rdfs:subClassOf>''' % rschema) |
|
139 self.w(u'</owl:Class>') |
|
140 |
|
141 def visit_property_schema(self, eschema): |
|
142 """get a layout for property entity OWL schema""" |
|
143 for rschema, targetschemas, role in eschema.relation_definitions(): |
|
144 if not self.should_display_rschema(eschema, rschema, role): |
|
145 continue |
|
146 for oeschema in targetschemas: |
|
147 self.w(u'''<owl:ObjectProperty rdf:ID="%s"> |
|
148 <rdfs:domain rdf:resource="#%s"/> |
|
149 <rdfs:range rdf:resource="#%s"/> |
|
150 </owl:ObjectProperty>''' % (rschema, eschema, oeschema.type)) |
|
151 |
|
152 def visit_property_object_schema(self, eschema): |
|
153 for rschema, aschema in eschema.attribute_definitions(): |
|
154 if not self.should_display_rschema(eschema, rschema, 'subject'): |
|
155 continue |
|
156 self.w(u'''<owl:DatatypeProperty rdf:ID="%s"> |
|
157 <rdfs:domain rdf:resource="#%s"/> |
|
158 <rdfs:range rdf:resource="%s"/> |
|
159 </owl:DatatypeProperty>''' % (rschema, eschema, OWL_TYPE_MAP[aschema.type])) |
|
160 |
|
161 |
|
162 class OWLABOXView(EntityView): |
|
163 '''This view represents a part of the ABOX for a given entity.''' |
|
164 __regid__ = 'owlabox' |
|
165 title = _('owlabox') |
|
166 templatable = False |
|
167 content_type = 'application/xml' # 'text/xml' |
|
168 |
|
169 def call(self): |
|
170 self.w(OWL_OPENING_ROOT % {'appid': self._cw.vreg.schema.name}) |
|
171 for i in range(self.cw_rset.rowcount): |
|
172 self.cell_call(i, 0) |
|
173 self.w(OWL_CLOSING_ROOT) |
|
174 |
|
175 def cell_call(self, row, col): |
|
176 self.wview('owlaboxitem', self.cw_rset, row=row, col=col) |
|
177 |
|
178 |
|
179 class OWLABOXItemView(EntityView): |
|
180 '''This view represents a part of the ABOX for a given entity.''' |
|
181 __regid__ = 'owlaboxitem' |
|
182 templatable = False |
|
183 content_type = 'application/xml' # 'text/xml' |
|
184 |
|
185 def cell_call(self, row, col): |
|
186 entity = self.cw_rset.complete_entity(row, col) |
|
187 eschema = entity.e_schema |
|
188 self.w(u'<%s rdf:ID="%s">' % (eschema, entity.eid)) |
|
189 self.w(u'<!--attributes-->') |
|
190 for rschema, aschema in eschema.attribute_definitions(): |
|
191 if rschema.meta: |
|
192 continue |
|
193 rdef = rschema.rdef(eschema, aschema) |
|
194 if not rdef.may_have_permission('read', self._cw): |
|
195 continue |
|
196 aname = rschema.type |
|
197 if aname == 'eid': |
|
198 continue |
|
199 try: |
|
200 attr = entity.printable_value(aname, format='text/plain') |
|
201 if attr: |
|
202 self.w(u'<%s>%s</%s>' % (aname, xml_escape(attr), aname)) |
|
203 except TransformError: |
|
204 pass |
|
205 self.w(u'<!--relations -->') |
|
206 for rschema, targetschemas, role in eschema.relation_definitions(): |
|
207 if rschema.meta: |
|
208 continue |
|
209 for tschema in targetschemas: |
|
210 rdef = rschema.role_rdef(eschema, tschema, role) |
|
211 if rdef.may_have_permission('read', self._cw): |
|
212 break |
|
213 else: |
|
214 # no read perms to any relation of this type. Skip. |
|
215 continue |
|
216 if role == 'object': |
|
217 attr = 'reverse_%s' % rschema.type |
|
218 else: |
|
219 attr = rschema.type |
|
220 for x in getattr(entity, attr): |
|
221 self.w(u'<%s>%s %s</%s>' % (attr, x.__regid__, x.eid, attr)) |
|
222 self.w(u'</%s>'% eschema) |
|
223 |
|
224 |
|
225 class DownloadOWLSchemaAction(Action): |
|
226 __regid__ = 'download_as_owl' |
|
227 __select__ = none_rset() & match_view('schema') |
|
228 |
|
229 category = 'mainactions' |
|
230 title = _('download schema as owl') |
|
231 |
|
232 def url(self): |
|
233 return self._cw.build_url('view', vid='owl') |