oldaplib 0.3.2__py3-none-any.whl → 0.3.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- oldaplib/ontologies/admin-testing.trig +2 -4
- oldaplib/ontologies/admin.trig +2 -4
- oldaplib/ontologies/oldap.trig +221 -131
- oldaplib/ontologies/shared.trig +0 -3
- oldaplib/src/datamodel.py +109 -17
- oldaplib/src/dtypes/namespaceiri.py +16 -1
- oldaplib/src/enums/externalontologyattr.py +22 -0
- oldaplib/src/enums/owlpropertytype.py +10 -0
- oldaplib/src/enums/projectattr.py +0 -1
- oldaplib/src/enums/propertyclassattr.py +3 -1
- oldaplib/src/externalontology.py +554 -0
- oldaplib/src/hasproperty.py +5 -0
- oldaplib/src/helpers/context.py +4 -4
- oldaplib/src/helpers/langstring.py +11 -2
- oldaplib/src/helpers/observable_dict.py +4 -1
- oldaplib/src/helpers/observable_set.py +135 -80
- oldaplib/src/helpers/query_processor.py +3 -0
- oldaplib/src/model.py +1 -1
- oldaplib/src/oldaplist.py +2 -2
- oldaplib/src/oldaplistnode.py +2 -2
- oldaplib/src/permissionset.py +3 -3
- oldaplib/src/project.py +47 -113
- oldaplib/src/propertyclass.py +64 -24
- oldaplib/src/resourceclass.py +8 -6
- oldaplib/src/version.py +1 -1
- oldaplib/src/xsd/iri.py +3 -0
- oldaplib/src/xsd/xsd_anyuri.py +5 -5
- oldaplib/src/xsd/xsd_qname.py +5 -2
- oldaplib/test/test_context.py +0 -4
- oldaplib/test/test_datamodel.py +50 -1
- oldaplib/test/test_dtypes.py +3 -2
- oldaplib/test/test_externalontologies.py +175 -0
- oldaplib/test/test_project.py +31 -76
- oldaplib/test/test_propertyclass.py +93 -6
- oldaplib/test/test_resourceclass.py +10 -10
- oldaplib/testdata/connection_test.trig +29 -12
- oldaplib/testdata/datamodel_test.trig +1 -1
- oldaplib/testdata/objectfactory_test.trig +2 -1
- {oldaplib-0.3.2.dist-info → oldaplib-0.3.4.dist-info}/METADATA +1 -1
- {oldaplib-0.3.2.dist-info → oldaplib-0.3.4.dist-info}/RECORD +41 -38
- {oldaplib-0.3.2.dist-info → oldaplib-0.3.4.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,554 @@
|
|
|
1
|
+
from copy import deepcopy
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from functools import partial
|
|
4
|
+
from typing import Any, Self, Callable
|
|
5
|
+
|
|
6
|
+
from elementpath.datatypes import NCName
|
|
7
|
+
|
|
8
|
+
from oldaplib.src.cachesingleton import CacheSingletonRedis
|
|
9
|
+
from oldaplib.src.dtypes.languagein import LanguageIn
|
|
10
|
+
from oldaplib.src.dtypes.namespaceiri import NamespaceIRI
|
|
11
|
+
from oldaplib.src.dtypes.xsdset import XsdSet
|
|
12
|
+
from oldaplib.src.enums.action import Action
|
|
13
|
+
from oldaplib.src.enums.adminpermissions import AdminPermission
|
|
14
|
+
from oldaplib.src.enums.attributeclass import AttributeClass
|
|
15
|
+
from oldaplib.src.enums.externalontologyattr import ExternalOntologyAttr
|
|
16
|
+
from oldaplib.src.helpers.Notify import Notify
|
|
17
|
+
from oldaplib.src.helpers.attributechange import AttributeChange
|
|
18
|
+
from oldaplib.src.helpers.context import Context
|
|
19
|
+
from oldaplib.src.helpers.langstring import LangString
|
|
20
|
+
from oldaplib.src.helpers.oldaperror import OldapError, OldapErrorNoPermission, OldapErrorAlreadyExists, \
|
|
21
|
+
OldapErrorNotFound, OldapErrorUpdateFailed, OldapErrorInUse
|
|
22
|
+
from oldaplib.src.helpers.query_processor import QueryProcessor
|
|
23
|
+
from oldaplib.src.helpers.serializer import serializer
|
|
24
|
+
from oldaplib.src.iconnection import IConnection
|
|
25
|
+
from oldaplib.src.model import Model
|
|
26
|
+
from oldaplib.src.project import Project
|
|
27
|
+
from oldaplib.src.xsd.iri import Iri
|
|
28
|
+
from oldaplib.src.xsd.xsd_datetime import Xsd_dateTime
|
|
29
|
+
from oldaplib.src.xsd.xsd_ncname import Xsd_NCName
|
|
30
|
+
from oldaplib.src.xsd.xsd_qname import Xsd_QName
|
|
31
|
+
from oldaplib.src.xsd.xsd_string import Xsd_string
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@serializer
|
|
35
|
+
class ExternalOntology(Model, Notify):
|
|
36
|
+
"""Class representing external ontologies.
|
|
37
|
+
|
|
38
|
+
This class provides methods for managing external ontologies.
|
|
39
|
+
"""
|
|
40
|
+
__extonto_qname: Xsd_QName | None
|
|
41
|
+
__projectShortName: Xsd_NCName | None
|
|
42
|
+
|
|
43
|
+
def __init__(self, *,
|
|
44
|
+
con: IConnection,
|
|
45
|
+
creator: Iri | str |None = None,
|
|
46
|
+
created: Xsd_dateTime | datetime | str | None = None,
|
|
47
|
+
contributor: Iri | None = None,
|
|
48
|
+
modified: Xsd_dateTime | datetime | str | None = None,
|
|
49
|
+
notifier: Callable[[Xsd_QName], None] | None = None,
|
|
50
|
+
notify_data: Xsd_QName | None = None,
|
|
51
|
+
projectShortName: Xsd_NCName | str,
|
|
52
|
+
validate: bool = False,
|
|
53
|
+
**kwargs):
|
|
54
|
+
Model.__init__(self,
|
|
55
|
+
connection=con,
|
|
56
|
+
creator=creator,
|
|
57
|
+
created=created,
|
|
58
|
+
contributor=contributor,
|
|
59
|
+
modified=modified,
|
|
60
|
+
validate=validate)
|
|
61
|
+
Notify.__init__(self, notifier, notify_data)
|
|
62
|
+
if isinstance(projectShortName, Xsd_NCName):
|
|
63
|
+
self.__projectShortName = projectShortName
|
|
64
|
+
else:
|
|
65
|
+
self.__projectShortName = Xsd_NCName(projectShortName, validate=validate)
|
|
66
|
+
self.set_attributes(kwargs, ExternalOntologyAttr)
|
|
67
|
+
self.__extonto_qname = Xsd_QName(self.__projectShortName, self._attributes[ExternalOntologyAttr.PREFIX])
|
|
68
|
+
|
|
69
|
+
for attr in ExternalOntologyAttr:
|
|
70
|
+
setattr(ExternalOntology, attr.value.fragment, property(
|
|
71
|
+
partial(ExternalOntology._get_value, attr=attr),
|
|
72
|
+
partial(ExternalOntology._set_value, attr=attr),
|
|
73
|
+
partial(ExternalOntology._del_value, attr=attr)))
|
|
74
|
+
self.update_notifier()
|
|
75
|
+
#self._changeset = {}
|
|
76
|
+
|
|
77
|
+
def __len__(self):
|
|
78
|
+
return len(self._attributes)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def extonto_qname(self) -> Xsd_QName:
|
|
83
|
+
return self.__extonto_qname
|
|
84
|
+
|
|
85
|
+
def update_notifier(self,
|
|
86
|
+
notifier: Callable[[AttributeClass | Xsd_QName], None] | None = None,
|
|
87
|
+
notify_data: AttributeClass | None = None,):
|
|
88
|
+
self.set_notifier(notifier, notify_data)
|
|
89
|
+
for attr, value in self._attributes.items():
|
|
90
|
+
if getattr(value, 'set_notifier', None) is not None:
|
|
91
|
+
value.set_notifier(self.notifier, attr)
|
|
92
|
+
|
|
93
|
+
def _as_dict(self):
|
|
94
|
+
return {x.fragment: y for x, y in self._attributes.items()} | super()._as_dict() | {
|
|
95
|
+
'projectShortName': self.__projectShortName
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
def __deepcopy__(self, memo: dict[Any, Any]) -> Self:
|
|
99
|
+
if id(self) in memo:
|
|
100
|
+
return memo[id(self)]
|
|
101
|
+
cls = self.__class__
|
|
102
|
+
instance = cls.__new__(cls)
|
|
103
|
+
memo[id(self)] = instance
|
|
104
|
+
Model.__init__(instance,
|
|
105
|
+
connection=deepcopy(self._con, memo),
|
|
106
|
+
creator=deepcopy(self._creator, memo),
|
|
107
|
+
created=deepcopy(self._created, memo),
|
|
108
|
+
contributor=deepcopy(self._contributor, memo),
|
|
109
|
+
modified=deepcopy(self._modified, memo))
|
|
110
|
+
Notify.__init__(instance,
|
|
111
|
+
notifier=self._notifier,
|
|
112
|
+
data=deepcopy(self._notify_data, memo))
|
|
113
|
+
# Copy internals of Model:
|
|
114
|
+
instance._attributes = deepcopy(self._attributes, memo)
|
|
115
|
+
instance._changset = deepcopy(self._changeset, memo)
|
|
116
|
+
instance.__projectShortName = deepcopy(self.__projectShortName, memo)
|
|
117
|
+
instance.__extonto_qname = deepcopy(self.__extonto_qname, memo)
|
|
118
|
+
return instance
|
|
119
|
+
|
|
120
|
+
def check_for_permissions(self) -> (bool, str):
|
|
121
|
+
"""
|
|
122
|
+
Internal method to check if a user may modify the permission set.
|
|
123
|
+
:return: a tuple with a boolean (True, False) and the error message (or "OK")
|
|
124
|
+
"""
|
|
125
|
+
#
|
|
126
|
+
# First we check if the logged-in user ("actor") has the ADMIN_PERMISSION_SETS permission for
|
|
127
|
+
# the given project!
|
|
128
|
+
#
|
|
129
|
+
actor = self._con.userdata
|
|
130
|
+
sysperms = actor.inProject.get(Iri('oldap:SystemProject'))
|
|
131
|
+
if sysperms and AdminPermission.ADMIN_OLDAP in sysperms:
|
|
132
|
+
#
|
|
133
|
+
# user has root privileges!
|
|
134
|
+
#
|
|
135
|
+
return True, "OK"
|
|
136
|
+
else:
|
|
137
|
+
if actor.inProject.get(self.__project.projectIri) is None:
|
|
138
|
+
return False, f'Actor has no ADMIN_MODEL permission for project {self.__project.projectIri}'
|
|
139
|
+
else:
|
|
140
|
+
if AdminPermission.ADMIN_MODEL not in actor.inProject.get(self.__project.projectIri):
|
|
141
|
+
return False, f'Actor has no ADMIN_MODEL permission for project {self.__project.projectIri}'
|
|
142
|
+
return True, "OK"
|
|
143
|
+
|
|
144
|
+
def notifier(self, attr: ExternalOntologyAttr, value: Any = None) -> None:
|
|
145
|
+
self._changeset[attr] = AttributeChange(None, Action.MODIFY)
|
|
146
|
+
self.notify()
|
|
147
|
+
|
|
148
|
+
def create_shacl(self, *,
|
|
149
|
+
timestamp: Xsd_dateTime,
|
|
150
|
+
indent: int = 0,
|
|
151
|
+
indent_inc: int = 4) -> str:
|
|
152
|
+
blank = ''
|
|
153
|
+
sparql = ''
|
|
154
|
+
sparql += f'{blank:{indent * indent_inc}}INSERT DATA {{\n'
|
|
155
|
+
sparql += f'{blank:{(indent + 1) * indent_inc}}GRAPH {self.__projectShortName}:shacl {{\n'
|
|
156
|
+
sparql += f'{blank:{(indent + 2) * indent_inc}} {self.__extonto_qname.toRdf} a oldap:ExternalOntology'
|
|
157
|
+
sparql += f' ;\n{blank:{(indent + 3) * indent_inc}}dcterms:creator {self._con.userIri.toRdf}'
|
|
158
|
+
sparql += f' ;\n{blank:{(indent + 3) * indent_inc}}dcterms:created {timestamp.toRdf}'
|
|
159
|
+
sparql += f' ;\n{blank:{(indent + 3) * indent_inc}}dcterms:contributor {self._con.userIri.toRdf}'
|
|
160
|
+
sparql += f' ;\n{blank:{(indent + 3) * indent_inc}}dcterms:modified {timestamp.toRdf}'
|
|
161
|
+
for attr, value in self._attributes.items():
|
|
162
|
+
sparql += f' ;\n{blank:{(indent + 3) * indent_inc}}{attr.value.toRdf} {value.toRdf}'
|
|
163
|
+
sparql += f'\n{blank:{(indent + 1) * indent_inc}}}}\n'
|
|
164
|
+
sparql += f'{blank:{indent * indent_inc}}}}\n'
|
|
165
|
+
return sparql
|
|
166
|
+
|
|
167
|
+
def create(self, indent: int = 0, indent_inc: int = 4) -> None:
|
|
168
|
+
if self._con is None:
|
|
169
|
+
raise OldapError("Cannot create: no connection")
|
|
170
|
+
|
|
171
|
+
result, message = self.check_for_permissions()
|
|
172
|
+
if not result:
|
|
173
|
+
raise OldapErrorNoPermission(message)
|
|
174
|
+
|
|
175
|
+
timestamp = Xsd_dateTime()
|
|
176
|
+
|
|
177
|
+
context = Context(name=self._con.context_name)
|
|
178
|
+
blank = ''
|
|
179
|
+
query1 = context.sparql_context
|
|
180
|
+
query1 += f"""
|
|
181
|
+
ASK {{
|
|
182
|
+
GRAPH {self.__projectShortName}:shacl {{
|
|
183
|
+
{self.__extonto_qname.toRdf} a oldap:ExternalOntology .
|
|
184
|
+
}}
|
|
185
|
+
}}
|
|
186
|
+
"""
|
|
187
|
+
|
|
188
|
+
sparql = context.sparql_context
|
|
189
|
+
sparql += self.create_shacl(timestamp=timestamp)
|
|
190
|
+
|
|
191
|
+
self._con.transaction_start()
|
|
192
|
+
res1 = self.safe_query(query1)
|
|
193
|
+
if res1['boolean']:
|
|
194
|
+
self._con.transaction_abort()
|
|
195
|
+
raise OldapErrorAlreadyExists(f'An ExternalOntology with a graphIri "{self.__projectShortName}" already exists.')
|
|
196
|
+
self.safe_update(sparql)
|
|
197
|
+
self._con.transaction_commit()
|
|
198
|
+
self._created = timestamp
|
|
199
|
+
self._creator = self._con.userIri
|
|
200
|
+
self._modified = timestamp
|
|
201
|
+
self._contributor = self._con.userIri
|
|
202
|
+
#context[self._attributes[ExternalOntologyAttr.PREFIX]] = NamespaceIRI(str(self._attributes[ExternalOntologyAttr.NAMESPACE_IRI]))
|
|
203
|
+
cache = CacheSingletonRedis()
|
|
204
|
+
cache.set(self.__extonto_qname, self)
|
|
205
|
+
|
|
206
|
+
@classmethod
|
|
207
|
+
def read(cls, *,
|
|
208
|
+
con: IConnection,
|
|
209
|
+
projectShortName: Xsd_NCName | str,
|
|
210
|
+
prefix: NCName | str | None = None,
|
|
211
|
+
validate: bool = False,
|
|
212
|
+
ignore_cache: bool = False):
|
|
213
|
+
if not isinstance(projectShortName, Xsd_NCName):
|
|
214
|
+
projectShortName = Xsd_NCName(projectShortName, validate=validate)
|
|
215
|
+
if isinstance(prefix, NCName):
|
|
216
|
+
extonto_qname = Xsd_QName(projectShortName, prefix, validate=validate)
|
|
217
|
+
else:
|
|
218
|
+
extonto_qname = Xsd_QName(projectShortName, Xsd_NCName(prefix, validate=validate), validate=validate)
|
|
219
|
+
if not ignore_cache:
|
|
220
|
+
cache = CacheSingletonRedis()
|
|
221
|
+
tmp = cache.get(extonto_qname, connection=con)
|
|
222
|
+
if tmp is not None:
|
|
223
|
+
tmp.update_notifier()
|
|
224
|
+
return tmp
|
|
225
|
+
context = Context(name=con.context_name)
|
|
226
|
+
sparql = context.sparql_context
|
|
227
|
+
sparql += f"""
|
|
228
|
+
SELECT ?extonto ?p ?o
|
|
229
|
+
FROM {projectShortName}:shacl
|
|
230
|
+
WHERE {{
|
|
231
|
+
BIND({extonto_qname} as ?extonto)
|
|
232
|
+
?extonto a oldap:ExternalOntology .
|
|
233
|
+
?extonto ?p ?o .
|
|
234
|
+
}}
|
|
235
|
+
"""
|
|
236
|
+
jsonobj = con.query(sparql)
|
|
237
|
+
res = QueryProcessor(context, jsonobj)
|
|
238
|
+
if len(res) == 0:
|
|
239
|
+
raise OldapErrorNotFound(f'No external ontology "{cls.__extonto_qname}" found.')
|
|
240
|
+
creator: Iri | None = None
|
|
241
|
+
created: Xsd_dateTime | None = None
|
|
242
|
+
contributor: Iri | None = None
|
|
243
|
+
modified: Xsd_dateTime | None = None
|
|
244
|
+
namespace_iri: NamespaceIRI | None = None
|
|
245
|
+
prefix: NCName | None = None
|
|
246
|
+
label: LangString = LangString()
|
|
247
|
+
comment: LangString = LangString()
|
|
248
|
+
for r in res:
|
|
249
|
+
match str(r['p']):
|
|
250
|
+
case 'dcterms:creator':
|
|
251
|
+
creator = r['o']
|
|
252
|
+
case 'dcterms:created':
|
|
253
|
+
created = r['o']
|
|
254
|
+
case 'dcterms:contributor':
|
|
255
|
+
contributor = r['o']
|
|
256
|
+
case 'dcterms:modified':
|
|
257
|
+
modified = r['o']
|
|
258
|
+
case 'oldap:namespaceIri':
|
|
259
|
+
namespace_iri = NamespaceIRI(r['o'])
|
|
260
|
+
case 'oldap:prefix':
|
|
261
|
+
prefix = r['o']
|
|
262
|
+
case 'rdfs:label':
|
|
263
|
+
label.add(r['o'])
|
|
264
|
+
case 'rdfs:comment':
|
|
265
|
+
comment.add(r['o'])
|
|
266
|
+
if comment:
|
|
267
|
+
comment.clear_changeset()
|
|
268
|
+
comment.set_notifier(cls.notifier, Xsd_QName(ExternalOntologyAttr.LABEL.value))
|
|
269
|
+
if label:
|
|
270
|
+
label.clear_changeset()
|
|
271
|
+
label.set_notifier(cls.notifier, Xsd_QName(ExternalOntologyAttr.LABEL.value))
|
|
272
|
+
instance = cls(con=con,
|
|
273
|
+
projectShortName=projectShortName,
|
|
274
|
+
creator=creator,
|
|
275
|
+
created=created,
|
|
276
|
+
contributor=contributor,
|
|
277
|
+
modified=modified,
|
|
278
|
+
prefix=prefix,
|
|
279
|
+
namespaceIri=namespace_iri,
|
|
280
|
+
label=label,
|
|
281
|
+
comment=comment,
|
|
282
|
+
validate=False)
|
|
283
|
+
instance.update_notifier()
|
|
284
|
+
cache = CacheSingletonRedis()
|
|
285
|
+
cache.set(instance.__extonto_qname, instance)
|
|
286
|
+
return instance
|
|
287
|
+
|
|
288
|
+
@staticmethod
|
|
289
|
+
def search(con: IConnection, *, # TODO: finish work here!!! This is just a small sceleton!
|
|
290
|
+
projectShortName: Xsd_NCName | str,
|
|
291
|
+
prefix: NCName | str | None = None,
|
|
292
|
+
namespaceIri: NamespaceIRI | str | None = None,
|
|
293
|
+
label: Xsd_string | str | None = None,
|
|
294
|
+
validate: bool = False) -> list['ExternalOntology']:
|
|
295
|
+
if not isinstance(projectShortName, Xsd_NCName):
|
|
296
|
+
projectShortName = Xsd_NCName(projectShortName, validate=validate)
|
|
297
|
+
|
|
298
|
+
context = Context(name=con.context_name)
|
|
299
|
+
sparql = context.sparql_context
|
|
300
|
+
sparql += f"SELECT DISTINCT ?extonto ?p ?o"
|
|
301
|
+
sparql += f"\nFROM {projectShortName}:shacl"
|
|
302
|
+
sparql += "\nWHERE {"
|
|
303
|
+
sparql += "\n ?extonto a oldap:ExternalOntology ."
|
|
304
|
+
sparql += "\n ?extonto ?p ?o ."
|
|
305
|
+
if prefix:
|
|
306
|
+
sparql += f'\n FILTER( REPLACE(STR(?extonto), ".+[#/]", "") = "{prefix}" )'
|
|
307
|
+
elif namespaceIri:
|
|
308
|
+
sparql += f'\n ?extonto oldap:namespaceIri ?namespaceIri .'
|
|
309
|
+
sparql += f'\n FILTER( ?namespaceIri = "{namespaceIri}" )'
|
|
310
|
+
elif label:
|
|
311
|
+
sparql += f'\n ?extonto rdfs:label ?label .'
|
|
312
|
+
if label.lang:
|
|
313
|
+
sparql += f'?label = {label.toRdf}'
|
|
314
|
+
else:
|
|
315
|
+
sparql += f'CONTAINS(STR(?label), "{Xsd_string.escaping(label.value)}")'
|
|
316
|
+
sparql += "\n}"
|
|
317
|
+
sparql += "\nORDER BY ?extonto"
|
|
318
|
+
jsonobj = con.query(sparql)
|
|
319
|
+
res = QueryProcessor(context, jsonobj)
|
|
320
|
+
result: list[ExternalOntology] = []
|
|
321
|
+
working_on: Xsd_QName | None = None
|
|
322
|
+
data: dict = {}
|
|
323
|
+
cache = CacheSingletonRedis()
|
|
324
|
+
for r in res:
|
|
325
|
+
if working_on is None or working_on != r['extonto']:
|
|
326
|
+
if working_on:
|
|
327
|
+
tmp = ExternalOntology(con=con,
|
|
328
|
+
projectShortName=projectShortName,
|
|
329
|
+
**data)
|
|
330
|
+
result.append(tmp)
|
|
331
|
+
cache.set(tmp.__extonto_qname, tmp)
|
|
332
|
+
data = {}
|
|
333
|
+
data['label'] = LangString()
|
|
334
|
+
data['comment'] = LangString()
|
|
335
|
+
working_on = r['extonto']
|
|
336
|
+
if r['p'] == 'rdf:type':
|
|
337
|
+
continue
|
|
338
|
+
if r['p'] == 'rdfs:label':
|
|
339
|
+
data['label'].add(r['o'])
|
|
340
|
+
elif r['p'] == 'rdfs:comment':
|
|
341
|
+
data['comment'].add(r['o'])
|
|
342
|
+
elif r['p'] == 'oldap:namespaceIri':
|
|
343
|
+
data['namespaceIri'] = NamespaceIRI(r['o'])
|
|
344
|
+
else:
|
|
345
|
+
data[str(r['p'].fragment)] = r['o']
|
|
346
|
+
if working_on:
|
|
347
|
+
tmp = ExternalOntology(con=con,
|
|
348
|
+
projectShortName=projectShortName,
|
|
349
|
+
**data)
|
|
350
|
+
result.append(tmp)
|
|
351
|
+
cache.set(tmp.__extonto_qname, tmp)
|
|
352
|
+
|
|
353
|
+
return result
|
|
354
|
+
|
|
355
|
+
def update(self, indent: int = 0, indent_inc: int = 4) -> None:
|
|
356
|
+
result, message = self.check_for_permissions()
|
|
357
|
+
if not result:
|
|
358
|
+
raise OldapErrorNoPermission(message)
|
|
359
|
+
timestamp = Xsd_dateTime.now()
|
|
360
|
+
context = Context(name=self._con.context_name)
|
|
361
|
+
blank = ''
|
|
362
|
+
sparql_list = []
|
|
363
|
+
|
|
364
|
+
for attr, change in self._changeset.items():
|
|
365
|
+
if attr == ExternalOntologyAttr.LABEL or attr == ExternalOntologyAttr.COMMENT:
|
|
366
|
+
if change.action == Action.MODIFY:
|
|
367
|
+
sparql_list.extend(
|
|
368
|
+
self._attributes[attr].update(graph=Xsd_QName(self.__projectShortName, 'shacl'),
|
|
369
|
+
subject=self.__extonto_qname,
|
|
370
|
+
field=attr.value))
|
|
371
|
+
if change.action == Action.DELETE or change.action == Action.REPLACE:
|
|
372
|
+
sparql = self._changeset[attr].old_value.delete(graph=Xsd_QName(self.__projectShortName, 'shacl'),
|
|
373
|
+
subject=self.__extonto_qname,
|
|
374
|
+
field=attr.value)
|
|
375
|
+
sparql_list.append(sparql)
|
|
376
|
+
if change.action == Action.CREATE or change.action == Action.REPLACE:
|
|
377
|
+
sparql = self._attributes[attr].create(graph=Xsd_QName(self.__projectShortName, 'shacl'),
|
|
378
|
+
subject=self.__extonto_qname,
|
|
379
|
+
field=attr.value)
|
|
380
|
+
sparql_list.append(sparql)
|
|
381
|
+
continue
|
|
382
|
+
sparql = f'{blank:{indent * indent_inc}}WITH {self.__projectShortName}:shacl\n'
|
|
383
|
+
if change.action != Action.CREATE:
|
|
384
|
+
sparql += f'{blank:{indent * indent_inc}}DELETE {{\n'
|
|
385
|
+
sparql += f'{blank:{(indent + 1) * indent_inc}}{self.__extonto_qname.toRdf} {attr.value} {change.old_value.toRdf} .\n'
|
|
386
|
+
sparql += f'{blank:{indent * indent_inc}}}}\n'
|
|
387
|
+
if change.action != Action.DELETE:
|
|
388
|
+
sparql += f'{blank:{indent * indent_inc}}INSERT {{\n'
|
|
389
|
+
sparql += f'{blank:{(indent + 1) * indent_inc}}{self.__extonto_qname.toRdf} {attr.value} {self._attributes[attr].toRdf} .\n'
|
|
390
|
+
sparql += f'{blank:{indent * indent_inc}}}}\n'
|
|
391
|
+
sparql += f'{blank:{indent * indent_inc}}WHERE {{\n'
|
|
392
|
+
sparql += f'{blank:{(indent + 1) * indent_inc}}{self.__extonto_qname.toRdf} {attr.value} {change.old_value.toRdf} .\n'
|
|
393
|
+
sparql += f'{blank:{indent * indent_inc}}}}'
|
|
394
|
+
sparql_list.append(sparql)
|
|
395
|
+
sparql = context.sparql_context
|
|
396
|
+
sparql += " ;\n".join(sparql_list)
|
|
397
|
+
|
|
398
|
+
self._con.transaction_start()
|
|
399
|
+
try:
|
|
400
|
+
self._con.transaction_update(sparql)
|
|
401
|
+
self.set_modified_by_iri(Xsd_QName(self.__projectShortName, 'shacl'), self.__extonto_qname, self._modified, timestamp)
|
|
402
|
+
modtime = self.get_modified_by_iri(Xsd_QName(self.__projectShortName, 'shacl'), self.__extonto_qname)
|
|
403
|
+
except OldapError:
|
|
404
|
+
self._con.transaction_abort()
|
|
405
|
+
raise
|
|
406
|
+
if timestamp != modtime:
|
|
407
|
+
self._con.transaction_abort()
|
|
408
|
+
raise OldapErrorUpdateFailed(f'Update of ExternalOntology "{self.__extonto_qname}" failed! Timestamp does not match"')
|
|
409
|
+
try:
|
|
410
|
+
self._con.transaction_commit()
|
|
411
|
+
except OldapError:
|
|
412
|
+
self._con.transaction_abort()
|
|
413
|
+
raise
|
|
414
|
+
self._modified = timestamp
|
|
415
|
+
self._contributor = self._con.userIri # TODO: move creator, created etc. to Model!
|
|
416
|
+
cache = CacheSingletonRedis()
|
|
417
|
+
cache.set(self.__extonto_qname, self)
|
|
418
|
+
|
|
419
|
+
def in_use_queries(self) -> (str, str):
|
|
420
|
+
"""
|
|
421
|
+
Generates two SPARQL ASK queries to check the usage of a permission set in two contexts:
|
|
422
|
+
assigned to a user or used by a data object (resource). The generated queries are used
|
|
423
|
+
to confirm whether the specified permission set is currently in use within the system.
|
|
424
|
+
|
|
425
|
+
The first query checks if the permission set is assigned to any user. The second query
|
|
426
|
+
validates if the permission set is associated with any data object or resource.
|
|
427
|
+
|
|
428
|
+
:return: A tuple containing two SPARQL ASK queries as strings. The first query checks
|
|
429
|
+
if the permission set is assigned to a user, and the second query checks if
|
|
430
|
+
the permission set is associated with a data object or resource.
|
|
431
|
+
:rtype: tuple[str, str]
|
|
432
|
+
"""
|
|
433
|
+
context = Context(name=self._con.context_name)
|
|
434
|
+
|
|
435
|
+
#
|
|
436
|
+
# first check if the external ontology is used in the datamodel
|
|
437
|
+
# TODO: Exlucde ExternalOntology definitions itself....
|
|
438
|
+
#
|
|
439
|
+
query1 = context.sparql_context
|
|
440
|
+
query1 += f"""
|
|
441
|
+
ASK {{
|
|
442
|
+
GRAPH {self.__projectShortName}:shacl {{
|
|
443
|
+
?s ?p ?o .
|
|
444
|
+
FILTER(
|
|
445
|
+
(STRSTARTS(STR(?p), "{self.namespaceIri}") ||
|
|
446
|
+
(isIRI(?o) && STRSTARTS(STR(?o), "{self.namespaceIri}"))) &&
|
|
447
|
+
?p != oldap:namespaceIri
|
|
448
|
+
)
|
|
449
|
+
}}
|
|
450
|
+
}}
|
|
451
|
+
"""
|
|
452
|
+
|
|
453
|
+
#
|
|
454
|
+
# now check if the permission set is used by a data object (resource)
|
|
455
|
+
#
|
|
456
|
+
query2 = context.sparql_context
|
|
457
|
+
query2 += f"""
|
|
458
|
+
ASK {{
|
|
459
|
+
VALUES ?g {{ {self.__projectShortName}:data {self.__projectShortName}:lists }}
|
|
460
|
+
GRAPH ?g {{
|
|
461
|
+
?s ?p ?o .
|
|
462
|
+
FILTER(
|
|
463
|
+
STRSTARTS(STR(?p), "{self.namespaceIri}") ||
|
|
464
|
+
(isIRI(?o) && STRSTARTS(STR(?o), "{self.namespaceIri}"))
|
|
465
|
+
)
|
|
466
|
+
}}
|
|
467
|
+
}}
|
|
468
|
+
"""
|
|
469
|
+
return query1, query2
|
|
470
|
+
|
|
471
|
+
def in_use(self) -> bool:
|
|
472
|
+
result, message = self.check_for_permissions()
|
|
473
|
+
if not result:
|
|
474
|
+
raise OldapErrorNoPermission(message)
|
|
475
|
+
|
|
476
|
+
query1, query2 = self.in_use_queries()
|
|
477
|
+
result1 = self._con.query(query1)
|
|
478
|
+
if result1['boolean']:
|
|
479
|
+
return True
|
|
480
|
+
result2 = self._con.query(query2)
|
|
481
|
+
if result2['boolean']:
|
|
482
|
+
return True
|
|
483
|
+
return False
|
|
484
|
+
|
|
485
|
+
def delete(self, indent: int = 0, indent_inc: int = 4) -> None:
|
|
486
|
+
result, message = self.check_for_permissions()
|
|
487
|
+
if not result:
|
|
488
|
+
raise OldapErrorNoPermission(message)
|
|
489
|
+
|
|
490
|
+
query1, query2 = self.in_use_queries()
|
|
491
|
+
context = Context(name=self._con.context_name)
|
|
492
|
+
|
|
493
|
+
#
|
|
494
|
+
# Now prepare the queries for deleting the permission set
|
|
495
|
+
#
|
|
496
|
+
sparql = context.sparql_context
|
|
497
|
+
sparql += f"""
|
|
498
|
+
DELETE WHERE {{
|
|
499
|
+
GRAPH {self.__projectShortName}:shacl {{
|
|
500
|
+
{self.__extonto_qname.toRdf} ?prop ?val .
|
|
501
|
+
}}
|
|
502
|
+
}}
|
|
503
|
+
"""
|
|
504
|
+
|
|
505
|
+
self._con.transaction_start()
|
|
506
|
+
result1 = self.safe_query(query1)
|
|
507
|
+
if result1['boolean']:
|
|
508
|
+
self._con.transaction_abort()
|
|
509
|
+
raise OldapErrorInUse(f"External ontology is used in the data model.")
|
|
510
|
+
result2 = self.safe_query(query2)
|
|
511
|
+
if result2['boolean']:
|
|
512
|
+
self._con.transaction_abort()
|
|
513
|
+
raise OldapErrorInUse("External ontology is used in the data.")
|
|
514
|
+
self.safe_update(sparql)
|
|
515
|
+
self._con.transaction_commit()
|
|
516
|
+
cache = CacheSingletonRedis()
|
|
517
|
+
cache.delete(self.__extonto_qname)
|
|
518
|
+
|
|
519
|
+
@classmethod
|
|
520
|
+
def delete_all(cls, *,
|
|
521
|
+
con: IConnection,
|
|
522
|
+
projectShortName: Xsd_NCName | str,
|
|
523
|
+
validate: bool = False) -> None:
|
|
524
|
+
if not isinstance(projectShortName, Xsd_NCName):
|
|
525
|
+
projectShortName = Xsd_NCName(projectShortName, validate=validate)
|
|
526
|
+
context = Context(name=con.context_name)
|
|
527
|
+
|
|
528
|
+
query0 = context.sparql_context
|
|
529
|
+
query0 += f"""
|
|
530
|
+
SELECT DISTINCT ?extonto
|
|
531
|
+
FROM {projectShortName}:shacl
|
|
532
|
+
WHERE {{
|
|
533
|
+
?extonto a oldap:ExternalOntology .
|
|
534
|
+
}}
|
|
535
|
+
"""
|
|
536
|
+
|
|
537
|
+
sparql = context.sparql_context
|
|
538
|
+
sparql += f"""
|
|
539
|
+
DELETE {{
|
|
540
|
+
GRAPH {projectShortName}:shacl {{
|
|
541
|
+
?s ?p ?o .
|
|
542
|
+
}}
|
|
543
|
+
}}
|
|
544
|
+
WHERE {{
|
|
545
|
+
GRAPH {projectShortName}:shacl {{
|
|
546
|
+
?s a oldap:ExternalOntology .
|
|
547
|
+
?s ?p ?o .
|
|
548
|
+
}}
|
|
549
|
+
}}
|
|
550
|
+
"""
|
|
551
|
+
con.update_query(sparql)
|
|
552
|
+
|
|
553
|
+
|
|
554
|
+
|
oldaplib/src/hasproperty.py
CHANGED
|
@@ -188,6 +188,11 @@ class HasProperty(Model, Notify):
|
|
|
188
188
|
order=self._attributes.get(HasPropertyAttr.ORDER, None),
|
|
189
189
|
group=self._attributes.get(HasPropertyAttr.GROUP, None))
|
|
190
190
|
|
|
191
|
+
def clear_changeset(self) -> None:
|
|
192
|
+
if hasattr(self._prop, 'clear_changeset'):
|
|
193
|
+
self._prop.clear_changeset()
|
|
194
|
+
super().clear_changeset()
|
|
195
|
+
|
|
191
196
|
def notifier(self, attr: HasPropertyAttr | Iri | Xsd_QName) -> None:
|
|
192
197
|
#if attr == HasPropertyAttr.PROP:
|
|
193
198
|
# return
|
oldaplib/src/helpers/context.py
CHANGED
|
@@ -46,9 +46,9 @@ class ContextSingleton(type):
|
|
|
46
46
|
Xsd_NCName('sh'): NamespaceIRI('http://www.w3.org/ns/shacl#'),
|
|
47
47
|
Xsd_NCName('skos'): NamespaceIRI('http://www.w3.org/2004/02/skos/core#'),
|
|
48
48
|
Xsd_NCName('schema'): NamespaceIRI('http://schema.org/'),
|
|
49
|
-
Xsd_NCName('dc'): NamespaceIRI('http://purl.org/dc/elements/1.1/'),
|
|
49
|
+
#Xsd_NCName('dc'): NamespaceIRI('http://purl.org/dc/elements/1.1/'),
|
|
50
50
|
Xsd_NCName('dcterms'): NamespaceIRI('http://purl.org/dc/terms/'),
|
|
51
|
-
Xsd_NCName('foaf'): NamespaceIRI('http://xmlns.com/foaf/0.1/'),
|
|
51
|
+
#Xsd_NCName('foaf'): NamespaceIRI('http://xmlns.com/foaf/0.1/'),
|
|
52
52
|
Xsd_NCName('oldap'): NamespaceIRI('http://oldap.org/base#'),
|
|
53
53
|
Xsd_NCName('shared'): NamespaceIRI('http://oldap.org/shared#')
|
|
54
54
|
}
|
|
@@ -61,9 +61,9 @@ class ContextSingleton(type):
|
|
|
61
61
|
NamespaceIRI('http://www.w3.org/ns/shacl#'): Xsd_NCName('sh'),
|
|
62
62
|
NamespaceIRI('http://www.w3.org/2004/02/skos/core#'): Xsd_NCName('skos'),
|
|
63
63
|
NamespaceIRI('http://schema.org/'): Xsd_NCName('schema'),
|
|
64
|
-
NamespaceIRI('http://purl.org/dc/elements/1.1/'): Xsd_NCName('dc'),
|
|
64
|
+
#NamespaceIRI('http://purl.org/dc/elements/1.1/'): Xsd_NCName('dc'),
|
|
65
65
|
NamespaceIRI('http://purl.org/dc/terms/'): Xsd_NCName('dcterms'),
|
|
66
|
-
NamespaceIRI('http://xmlns.com/foaf/0.1/'): Xsd_NCName('foaf'),
|
|
66
|
+
#NamespaceIRI('http://xmlns.com/foaf/0.1/'): Xsd_NCName('foaf'),
|
|
67
67
|
NamespaceIRI('http://oldap.org/base#'): Xsd_NCName('oldap'),
|
|
68
68
|
NamespaceIRI('http://oldap.org/shared#'): Xsd_NCName('shared'),
|
|
69
69
|
}
|
|
@@ -66,7 +66,7 @@ class LangString(Notify):
|
|
|
66
66
|
- _add()_: Add a string
|
|
67
67
|
- _undo_(): Forget all changes
|
|
68
68
|
- _changeset()_: Return the changeset dict (Note: this is not for generic use)
|
|
69
|
-
-
|
|
69
|
+
- _clear_changeset()_: Clear changeset to an empty dict
|
|
70
70
|
- _update_shacl_(): Return the SPARQL code piece that updates a Language string SHACL part of the triple store.
|
|
71
71
|
- _delete_shacl_(): Return the SPARQL code piece that deletes an LanguageString
|
|
72
72
|
"""
|
|
@@ -440,7 +440,7 @@ class LangString(Notify):
|
|
|
440
440
|
"""
|
|
441
441
|
return self._changeset
|
|
442
442
|
|
|
443
|
-
def
|
|
443
|
+
def clear_changeset(self) -> None:
|
|
444
444
|
"""
|
|
445
445
|
Clear changeset to an empty dict
|
|
446
446
|
:return: Nothing
|
|
@@ -448,6 +448,15 @@ class LangString(Notify):
|
|
|
448
448
|
"""
|
|
449
449
|
self._changeset = {}
|
|
450
450
|
|
|
451
|
+
def clear_changset(self) -> None:
|
|
452
|
+
"""
|
|
453
|
+
Clear changeset to an empty dict
|
|
454
|
+
:return: Nothing
|
|
455
|
+
:rtype: None
|
|
456
|
+
"""
|
|
457
|
+
self._changeset = {}
|
|
458
|
+
|
|
459
|
+
|
|
451
460
|
def create(self, *,
|
|
452
461
|
graph: Xsd_QName,
|
|
453
462
|
subject: Iri,
|
|
@@ -77,6 +77,9 @@ class ObservableDict(UserDict):
|
|
|
77
77
|
def _as_dict(self):
|
|
78
78
|
return {'obsdict': [{'key': key, 'val': val} for key, val in self.data.items()]}
|
|
79
79
|
|
|
80
|
-
def clear_changeset(self):
|
|
80
|
+
def clear_changeset(self) -> None:
|
|
81
|
+
for item in self.data.values():
|
|
82
|
+
if hasattr(item, 'clear_changeset'):
|
|
83
|
+
item.clear_changeset()
|
|
81
84
|
self._changeset = {}
|
|
82
85
|
|