21 from cubicweb import _ |
21 from cubicweb import _ |
22 |
22 |
23 from hashlib import sha1 |
23 from hashlib import sha1 |
24 from itertools import chain |
24 from itertools import chain |
25 |
25 |
26 from rdflib import URIRef, Literal |
26 from rdflib import URIRef, Literal, namespace as rdflib_namespace |
27 |
|
28 from logilab.mtconverter import TransformError |
27 from logilab.mtconverter import TransformError |
29 from logilab.common.decorators import cached, cachedproperty |
28 from logilab.common.decorators import cached, cachedproperty |
30 |
29 |
31 from cubicweb.entity import EntityAdapter |
30 from cubicweb import rdf |
32 from cubicweb import (Unauthorized, ValidationError, ViolatedConstraint, |
31 from cubicweb import (Unauthorized, ValidationError, ViolatedConstraint, |
33 UniqueTogetherError) |
32 UniqueTogetherError) |
34 from cubicweb.schema import constraint_name_for |
33 from cubicweb.entity import EntityAdapter |
|
34 from cubicweb.schema import constraint_name_for, VIRTUAL_RTYPES |
35 from cubicweb.predicates import is_instance, relation_possible, match_exception |
35 from cubicweb.predicates import is_instance, relation_possible, match_exception |
36 |
|
37 from cubicweb.rdf import NAMESPACES |
|
38 |
36 |
39 |
37 |
40 class EntityRDFAdapter(EntityAdapter): |
38 class EntityRDFAdapter(EntityAdapter): |
41 """EntityRDFAdapter is to be specialized for each entity that wants to |
39 """EntityRDFAdapter is to be specialized for each entity that wants to |
42 be converted to RDF using the mechanism from cubicweb.rdf |
40 be converted to RDF using the mechanism from cubicweb.rdf |
43 """ |
41 """ |
44 __abstract__ = True |
42 __regid__ = "rdf" |
|
43 |
|
44 SKIP_RTYPES = VIRTUAL_RTYPES | set(['cwuri', 'is', 'is_instance_of']) |
45 |
45 |
46 def __init__(self, _cw, **kwargs): |
46 def __init__(self, _cw, **kwargs): |
47 super().__init__(_cw, **kwargs) |
47 super().__init__(_cw, **kwargs) |
48 self.entity.complete() |
48 self.entity.complete() |
|
49 self.used_namespaces = {} |
|
50 |
|
51 def _use_namespace(self, prefix, base_url=None): |
|
52 if prefix not in self.used_namespaces: |
|
53 if base_url is not None: |
|
54 if prefix in rdf.NAMESPACES: |
|
55 raise KeyError('prefix redefinition not allowed: ' |
|
56 f'"{prefix}" already exists as "{rdf.NAMESPACES[prefix]}"') |
|
57 ns = rdflib_namespace.Namespace(base_url) |
|
58 else: |
|
59 ns = rdf.NAMESPACES[prefix] |
|
60 self.used_namespaces[prefix] = ns |
|
61 elif base_url is not None: |
|
62 if self.used_namespaces[prefix] != rdflib_namespace.Namespace(base_url): |
|
63 raise ValueError('prefix redefinition not allowed: ' |
|
64 f'"{prefix}" already exists as "{self.used_namespaces[prefix]}"') |
|
65 return self.used_namespaces[prefix] |
49 |
66 |
50 @cachedproperty |
67 @cachedproperty |
51 def uri(self): |
68 def uri(self): |
52 return self.entity.cwuri |
69 return URIRef(self.entity.cwuri) |
53 |
70 |
54 def triples(self): |
71 def triples(self): |
55 """return sequence of 3-tuple of rdflib identifiers""" |
72 """return sequence of 3-tuple of rdflib identifiers""" |
56 raise NotImplementedError() |
73 yield from self.cw_triples() |
57 |
74 yield from self.dc_triples() |
58 |
75 |
59 class CWUserFoafAdapter(EntityRDFAdapter): |
76 def cw_triples(self): |
60 __regid__ = "rdf.foaf" |
77 RDF = self._use_namespace('rdf') |
|
78 CW = self._use_namespace('cubicweb') |
|
79 |
|
80 yield (self.uri, RDF.type, CW[self.entity.e_schema.type]) |
|
81 |
|
82 for rschema, targettypes, role in self.entity.e_schema.relation_definitions('relation'): |
|
83 rtype = rschema.type |
|
84 if rtype in self.SKIP_RTYPES or rtype.endswith('_permission'): |
|
85 continue |
|
86 for targetype in targettypes: |
|
87 # if rschema is an attribute |
|
88 if targetype.final: |
|
89 try: |
|
90 value = self.entity.cw_attr_cache[rtype] |
|
91 except KeyError: |
|
92 continue |
|
93 if value is not None: |
|
94 yield (self.uri, CW[rtype], Literal(value)) |
|
95 # else if rschema is a relation |
|
96 else: |
|
97 for related in self.entity.related(rtype, role, entities=True, safe=True): |
|
98 if role == 'subject': |
|
99 yield (self.uri, CW[rtype], URIRef(related.cwuri)) |
|
100 else: |
|
101 yield (URIRef(related.cwuri), CW[rtype], self.uri) |
|
102 |
|
103 def dc_triples(self): |
|
104 dc_entity = self.entity.cw_adapt_to('IDublinCore') |
|
105 DC = self._use_namespace('dc') |
|
106 yield (self.uri, DC.title, Literal(dc_entity.long_title())) # or title() ? |
|
107 desc = dc_entity.description() |
|
108 if desc: |
|
109 yield (self.uri, DC.description, Literal(desc)) |
|
110 creator = dc_entity.creator() # use URI instead of Literal ? |
|
111 if creator: |
|
112 yield (self.uri, DC.creator, Literal(creator)) |
|
113 yield (self.uri, DC.date, Literal(dc_entity.date())) |
|
114 yield (self.uri, DC.type, Literal(dc_entity.type())) |
|
115 yield (self.uri, DC.language, Literal(dc_entity.language())) |
|
116 |
|
117 |
|
118 class CWUserRDFAdapter(EntityRDFAdapter): |
61 __select__ = is_instance("CWUser") |
119 __select__ = is_instance("CWUser") |
62 |
120 |
63 def triples(self): |
121 def triples(self): |
64 RDF = NAMESPACES["rdf"] |
122 yield from super().triples() |
65 FOAF = NAMESPACES["foaf"] |
123 yield from self.foaf_triples() |
66 uri = URIRef(self.uri) |
124 |
67 yield (uri, RDF.type, FOAF.Person) |
125 def foaf_triples(self): |
|
126 RDF = self._use_namespace('rdf') |
|
127 FOAF = self._use_namespace('foaf') |
|
128 yield (self.uri, RDF.type, FOAF.Person) |
68 if self.entity.surname: |
129 if self.entity.surname: |
69 yield (uri, FOAF.familyName, Literal(self.entity.surname)) |
130 yield (self.uri, FOAF.familyName, Literal(self.entity.surname)) |
70 if self.entity.firstname: |
131 if self.entity.firstname: |
71 yield (uri, FOAF.givenName, Literal(self.entity.firstname)) |
132 yield (self.uri, FOAF.givenName, Literal(self.entity.firstname)) |
72 emailaddr = self.entity.cw_adapt_to("IEmailable").get_email() |
133 emailaddr = self.entity.cw_adapt_to("IEmailable").get_email() |
73 if emailaddr: |
134 if emailaddr: |
74 email_digest = sha1(emailaddr.encode("utf-8")).hexdigest() |
135 email_digest = sha1(emailaddr.encode("utf-8")).hexdigest() |
75 yield (uri, FOAF.mbox_sha1sum, Literal(email_digest)) |
136 yield (self.uri, FOAF.mbox_sha1sum, Literal(email_digest)) |
76 |
137 |
77 |
138 |
78 class IDublinCoreAdapter(EntityAdapter): |
139 class IDublinCoreAdapter(EntityAdapter): |
79 __regid__ = 'IDublinCore' |
140 __regid__ = 'IDublinCore' |
80 __select__ = is_instance('Any') |
141 __select__ = is_instance('Any') |