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
oldaplib/src/project.py
CHANGED
|
@@ -110,7 +110,7 @@ class Project(Model):
|
|
|
110
110
|
#_attributes: dict[ProjectAttr, ProjectAttrTypes]
|
|
111
111
|
#__changeset: dict[ProjectAttr, ProjectAttrChange]
|
|
112
112
|
|
|
113
|
-
__slots__ = ('projectIri', 'projectShortName', 'label', 'comment', 'namespaceIri', 'projectStart', 'projectEnd'
|
|
113
|
+
__slots__ = ('projectIri', 'projectShortName', 'label', 'comment', 'namespaceIri', 'projectStart', 'projectEnd')
|
|
114
114
|
|
|
115
115
|
def __init__(self, *,
|
|
116
116
|
con: IConnection,
|
|
@@ -176,8 +176,6 @@ class Project(Model):
|
|
|
176
176
|
partial(Project._get_value, attr=attr),
|
|
177
177
|
partial(Project._set_value, attr=attr),
|
|
178
178
|
partial(Project._del_value, attr=attr)))
|
|
179
|
-
if self._attributes.get(ProjectAttr.USES_EXTERNAL_ONTOLOGY):
|
|
180
|
-
self._attributes[ProjectAttr.USES_EXTERNAL_ONTOLOGY].set_on_change(self.eo_notify)
|
|
181
179
|
self._changeset = {}
|
|
182
180
|
|
|
183
181
|
def update_notifier(self):
|
|
@@ -185,10 +183,6 @@ class Project(Model):
|
|
|
185
183
|
if getattr(value, 'set_notifier', None) is not None:
|
|
186
184
|
value.set_notifier(self.notifier, attr)
|
|
187
185
|
|
|
188
|
-
def eo_notify(self, d: ObservableDict):
|
|
189
|
-
if not self._changeset.get(ProjectAttr.USES_EXTERNAL_ONTOLOGY):
|
|
190
|
-
self._changeset[ProjectAttr.USES_EXTERNAL_ONTOLOGY] = AttributeChange(self._attributes[ProjectAttr.USES_EXTERNAL_ONTOLOGY].copy(), Action.MODIFY)
|
|
191
|
-
|
|
192
186
|
def _as_dict(self):
|
|
193
187
|
return {x.fragment: y for x, y in self._attributes.items()} | super()._as_dict()
|
|
194
188
|
|
|
@@ -239,29 +233,6 @@ class Project(Model):
|
|
|
239
233
|
"""
|
|
240
234
|
self._changeset[attr] = AttributeChange(self._attributes[attr], Action.MODIFY)
|
|
241
235
|
|
|
242
|
-
def add_external_ontology(self, ontos: dict[Xsd_NCName, NamespaceIRI]):
|
|
243
|
-
"""
|
|
244
|
-
Adds external ontologies to the project.
|
|
245
|
-
:param dict[Xsd_NCName, NamespaceIRI]: Dictionary of external ontologies to add
|
|
246
|
-
:return: None
|
|
247
|
-
"""
|
|
248
|
-
if not ontos:
|
|
249
|
-
return
|
|
250
|
-
if not self._attributes.get(ProjectAttr.USES_EXTERNAL_ONTOLOGY):
|
|
251
|
-
self._attributes[ProjectAttr.USES_EXTERNAL_ONTOLOGY] = ObservableDict()
|
|
252
|
-
for prefix, iri in ontos.items():
|
|
253
|
-
if not self._changeset[ProjectAttr.USES_EXTERNAL_ONTOLOGY]:
|
|
254
|
-
self._changeset[ProjectAttr.USES_EXTERNAL_ONTOLOGY] = AttributeChange(self._attributes[ProjectAttr.USES_EXTERNAL_ONTOLOGY].copy(), Action.MODIFY)
|
|
255
|
-
self._attributes[ProjectAttr.USES_EXTERNAL_ONTOLOGY][prefix] = iri
|
|
256
|
-
|
|
257
|
-
def del_external_ontology(self, ontos: list[Xsd_NCName]):
|
|
258
|
-
if not ontos:
|
|
259
|
-
return
|
|
260
|
-
if not self._attributes.get(ProjectAttr.USES_EXTERNAL_ONTOLOGY):
|
|
261
|
-
raise OldapErrorInconsistency(f'Project {self} has no external ontologies.')
|
|
262
|
-
for prefix in ontos:
|
|
263
|
-
if not self._changeset[ProjectAttr.USES_EXTERNAL_ONTOLOGY]:
|
|
264
|
-
self._changeset[ProjectAttr.USES_EXTERNAL_ONTOLOGY] = AttributeChange(self._attributes[ProjectAttr.USES_EXTERNAL_ONTOLOGY].copy(), Action.MODIFY)
|
|
265
236
|
|
|
266
237
|
|
|
267
238
|
@classmethod
|
|
@@ -355,7 +326,6 @@ class Project(Model):
|
|
|
355
326
|
comment = LangString()
|
|
356
327
|
projectStart = None
|
|
357
328
|
projectEnd = None
|
|
358
|
-
usesExternalOntology: dict[Xsd_QName, NamespaceIRI] | None = None
|
|
359
329
|
for r in res:
|
|
360
330
|
if projectIri is None:
|
|
361
331
|
projectIri = r['proj']
|
|
@@ -380,15 +350,11 @@ class Project(Model):
|
|
|
380
350
|
projectStart = r['val']
|
|
381
351
|
case 'oldap:projectEnd':
|
|
382
352
|
projectEnd = r['val']
|
|
383
|
-
case 'oldap:usesExternalOntology':
|
|
384
|
-
if not usesExternalOntology:
|
|
385
|
-
usesExternalOntology = {}
|
|
386
|
-
usesExternalOntology[r['prefix']] = NamespaceIRI(r['iri'])
|
|
387
353
|
if label:
|
|
388
|
-
label.
|
|
354
|
+
label.clear_changeset()
|
|
389
355
|
label.set_notifier(cls.notifier, Xsd_QName(ProjectAttr.LABEL.value))
|
|
390
356
|
if comment:
|
|
391
|
-
comment.
|
|
357
|
+
comment.clear_changeset()
|
|
392
358
|
comment.set_notifier(cls.notifier, Xsd_QName(ProjectAttr.COMMENT.value))
|
|
393
359
|
context[projectShortName] = namespaceIri
|
|
394
360
|
instance = cls(con=con,
|
|
@@ -402,8 +368,7 @@ class Project(Model):
|
|
|
402
368
|
namespaceIri=namespaceIri,
|
|
403
369
|
comment=comment,
|
|
404
370
|
projectStart=projectStart,
|
|
405
|
-
projectEnd=projectEnd
|
|
406
|
-
usesExternalOntology=usesExternalOntology)
|
|
371
|
+
projectEnd=projectEnd)
|
|
407
372
|
cache = CacheSingletonRedis()
|
|
408
373
|
cache.set(instance.projectIri, instance, instance.projectShortName)
|
|
409
374
|
return instance
|
|
@@ -499,6 +464,17 @@ class Project(Model):
|
|
|
499
464
|
|
|
500
465
|
context = Context(name=self._con.context_name)
|
|
501
466
|
|
|
467
|
+
sparql0 = context.sparql_context
|
|
468
|
+
sparql0 += f"""
|
|
469
|
+
ASK {{
|
|
470
|
+
GRAPH oldap:admin {{
|
|
471
|
+
?p a oldap:Project ;
|
|
472
|
+
oldap:projectShortName ?sname .
|
|
473
|
+
FILTER(?sname = {self.projectShortName.toRdf})
|
|
474
|
+
}}
|
|
475
|
+
}}
|
|
476
|
+
"""
|
|
477
|
+
|
|
502
478
|
sparql1 = context.sparql_context
|
|
503
479
|
sparql1 += f"""
|
|
504
480
|
SELECT ?project
|
|
@@ -508,6 +484,23 @@ class Project(Model):
|
|
|
508
484
|
FILTER(?project = {self.projectIri.toRdf})
|
|
509
485
|
}}
|
|
510
486
|
"""
|
|
487
|
+
sparql1a = context.sparql_context
|
|
488
|
+
sparql1a += f"""
|
|
489
|
+
ASK {{
|
|
490
|
+
GRAPH oldap:admin {{
|
|
491
|
+
?project oldap:namespaceIri {self.namespaceIri.toRdf} .
|
|
492
|
+
}}
|
|
493
|
+
}}
|
|
494
|
+
"""
|
|
495
|
+
|
|
496
|
+
sparql1b = context.sparql_context
|
|
497
|
+
sparql1b += f"""
|
|
498
|
+
ASK {{
|
|
499
|
+
GRAPH oldap:admin {{
|
|
500
|
+
{self.projectIri.toRdf} ?p ?o .
|
|
501
|
+
}}
|
|
502
|
+
}}
|
|
503
|
+
"""
|
|
511
504
|
|
|
512
505
|
blank = ''
|
|
513
506
|
sparql2 = context.sparql_context
|
|
@@ -521,21 +514,25 @@ class Project(Model):
|
|
|
521
514
|
for attr, value in self._attributes.items():
|
|
522
515
|
if not value:
|
|
523
516
|
continue
|
|
524
|
-
|
|
525
|
-
for prefix, iri in value.items():
|
|
526
|
-
sparql2 += f' ;\n{blank:{(indent + 3) * indent_inc}}oldap:usesExternalOntology [ oldap:prefix {prefix.toRdf} ; oldap:fullIri {iri.toRdf} ]'
|
|
527
|
-
pass
|
|
528
|
-
else:
|
|
529
|
-
sparql2 += f' ;\n{blank:{(indent + 3) * indent_inc}}{attr.value.toRdf} {value.toRdf}'
|
|
517
|
+
sparql2 += f' ;\n{blank:{(indent + 3) * indent_inc}}{attr.value.toRdf} {value.toRdf}'
|
|
530
518
|
sparql2 += f' .\n{blank:{(indent + 1) * indent_inc}}}}\n'
|
|
531
519
|
sparql2 += f'{blank:{indent * indent_inc}}}}\n'
|
|
532
520
|
|
|
533
521
|
self._con.transaction_start()
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
if len(res) > 0:
|
|
522
|
+
result = self.safe_query(sparql0)
|
|
523
|
+
if result['boolean']:
|
|
537
524
|
self._con.transaction_abort()
|
|
538
|
-
raise OldapErrorAlreadyExists(f'A Project with a
|
|
525
|
+
raise OldapErrorAlreadyExists(f'A Project with a shortname "{self.projectShortName}" already exists.')
|
|
526
|
+
|
|
527
|
+
result = self.safe_query(sparql1a)
|
|
528
|
+
if result['boolean']:
|
|
529
|
+
self._con.transaction_abort()
|
|
530
|
+
raise OldapErrorAlreadyExists(f'A Project with a namespace IRI "{self.namespaceIri}" already exists.')
|
|
531
|
+
|
|
532
|
+
result = self.safe_query(sparql1b)
|
|
533
|
+
if result['boolean']:
|
|
534
|
+
self._con.transaction_abort()
|
|
535
|
+
raise OldapErrorAlreadyExists(f'A Project with a project IRI "{self.projectIri}" already exists.')
|
|
539
536
|
|
|
540
537
|
self.safe_update(sparql2)
|
|
541
538
|
self.safe_commit()
|
|
@@ -607,70 +604,7 @@ class Project(Model):
|
|
|
607
604
|
field=Xsd_QName(field.value))
|
|
608
605
|
sparql_list.append(sparql)
|
|
609
606
|
continue
|
|
610
|
-
|
|
611
|
-
if change.action == Action.MODIFY:
|
|
612
|
-
diff = dict_diff(self._changeset[ProjectAttr.USES_EXTERNAL_ONTOLOGY].old_value, self._attributes[ProjectAttr.USES_EXTERNAL_ONTOLOGY])
|
|
613
|
-
#
|
|
614
|
-
# first we remove the "removed" and "changed"
|
|
615
|
-
#
|
|
616
|
-
to_delete = set(diff['removed']) | set(diff['changed'])
|
|
617
|
-
for key in to_delete:
|
|
618
|
-
sparql = f"""
|
|
619
|
-
DELETE {{
|
|
620
|
-
GRAPH oldap:admin {{
|
|
621
|
-
{self.projectIri.toRdf} oldap:usesExternalOntology ?o .
|
|
622
|
-
?o ?p ?v .
|
|
623
|
-
}}
|
|
624
|
-
}}
|
|
625
|
-
WHERE {{
|
|
626
|
-
GRAPH oldap:admin {{
|
|
627
|
-
?o oldap:prefix {key.toRdf} .
|
|
628
|
-
?o oldap:fullIri ?anyiri .
|
|
629
|
-
}}
|
|
630
|
-
}}
|
|
631
|
-
"""
|
|
632
|
-
sparql_list.append(sparql)
|
|
633
|
-
to_add = set(diff['added']) | set(diff['changed'])
|
|
634
|
-
sparql = f"""
|
|
635
|
-
INSERT DATA {{
|
|
636
|
-
GRAPH oldap:admin {{
|
|
637
|
-
"""
|
|
638
|
-
for key in to_add:
|
|
639
|
-
sparql += f" {self.projectIri.toRdf} oldap:usesExternalOntology [ oldap:prefix {key.toRdf} ; oldap:fullIri {self._attributes[ProjectAttr.USES_EXTERNAL_ONTOLOGY][key].toRdf} ] .\n"
|
|
640
|
-
sparql += f"""
|
|
641
|
-
}}
|
|
642
|
-
}}
|
|
643
|
-
"""
|
|
644
|
-
sparql_list.append(sparql)
|
|
645
|
-
if change.action == Action.DELETE:
|
|
646
|
-
sparql = f"""
|
|
647
|
-
DELETE {{
|
|
648
|
-
GRAPH oldap:admin {{
|
|
649
|
-
{self.projectIri.toRdf} oldap:usesExternalOntology ?o .
|
|
650
|
-
?o ?p ?v .
|
|
651
|
-
}}
|
|
652
|
-
}}
|
|
653
|
-
WHERE {{
|
|
654
|
-
GRAPH oldap:admin {{
|
|
655
|
-
{self.projectIri.toRdf} oldap:usesExternalOntology ?o .
|
|
656
|
-
?o ?p ?v .
|
|
657
|
-
}}
|
|
658
|
-
}}
|
|
659
|
-
"""
|
|
660
|
-
sparql_list.append(sparql)
|
|
661
|
-
if change.action == Action.CREATE:
|
|
662
|
-
sparql = f"""
|
|
663
|
-
INSERT DATA {{
|
|
664
|
-
GRAPH oldap:admin {{
|
|
665
|
-
"""
|
|
666
|
-
for prefix, iri in self._attributes[ProjectAttr.USES_EXTERNAL_ONTOLOGY].items():
|
|
667
|
-
sparql += f" {self.projectIri.toRdf} oldap:usesExternalOntology [ oldap:prefix {prefix.toRdf} ; oldap:fullIri {iri.toRdf} ] .\n"
|
|
668
|
-
sparql += f"""
|
|
669
|
-
}}
|
|
670
|
-
}}
|
|
671
|
-
"""
|
|
672
|
-
sparql_list.append(sparql)
|
|
673
|
-
continue
|
|
607
|
+
|
|
674
608
|
sparql = f'{blank:{indent * indent_inc}}# Project field "{field.value}" with action "{change.action.value}"\n'
|
|
675
609
|
if change.action != Action.CREATE:
|
|
676
610
|
sparql += f'{blank:{indent * indent_inc}}DELETE {{\n'
|
oldaplib/src/propertyclass.py
CHANGED
|
@@ -22,6 +22,7 @@ from pprint import pprint
|
|
|
22
22
|
from typing import Callable, Self, Any
|
|
23
23
|
|
|
24
24
|
from oldaplib.src.helpers.irincname import IriOrNCName
|
|
25
|
+
from oldaplib.src.helpers.observable_set import ObservableSet
|
|
25
26
|
from oldaplib.src.helpers.serializer import serializer
|
|
26
27
|
from oldaplib.src.oldaplogging import get_logger
|
|
27
28
|
from oldaplib.src.cachesingleton import CacheSingletonRedis
|
|
@@ -312,15 +313,16 @@ class PropertyClass(Model, Notify):
|
|
|
312
313
|
raise OldapErrorInconsistency(f'It\'s not possible to use both DATATYPE="{self._attributes[PropClassAttr.DATATYPE]}" and CLASS={self._attributes[PropClassAttr.CLASS]} restrictions.')
|
|
313
314
|
|
|
314
315
|
# setting property type for OWL which distinguished between Data- and Object-properties
|
|
316
|
+
if not self._attributes.get(PropClassAttr.TYPE):
|
|
317
|
+
self._attributes[PropClassAttr.TYPE] = ObservableSet(notifier=self.notifier, notify_data=PropClassAttr.TYPE)
|
|
315
318
|
if self._statementProperty:
|
|
316
|
-
self._attributes[PropClassAttr.TYPE]
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
self._attributes[PropClassAttr.TYPE] = OwlPropertyType.OwlDataProperty
|
|
319
|
+
self._attributes[PropClassAttr.TYPE].add(OwlPropertyType.StatementProperty)
|
|
320
|
+
if self._attributes.get(PropClassAttr.CLASS) is not None:
|
|
321
|
+
self._attributes[PropClassAttr.TYPE].add(OwlPropertyType.OwlObjectProperty)
|
|
322
|
+
if self._attributes.get(PropClassAttr.DATATYPE) is not None:
|
|
323
|
+
raise OldapError(f'Datatype "{self._attributes.get(PropClassAttr.DATATYPE)}" not possible for OwlObjectProperty')
|
|
324
|
+
elif self._attributes.get(PropClassAttr.DATATYPE) is not None:
|
|
325
|
+
self._attributes[PropClassAttr.TYPE].add(OwlPropertyType.OwlDataProperty)
|
|
324
326
|
|
|
325
327
|
#
|
|
326
328
|
# set the class properties
|
|
@@ -344,6 +346,7 @@ class PropertyClass(Model, Notify):
|
|
|
344
346
|
self._force_external = True
|
|
345
347
|
self.__version = SemanticVersion()
|
|
346
348
|
self.__from_triplestore = _from_triplestore
|
|
349
|
+
self.clear_changeset()
|
|
347
350
|
|
|
348
351
|
def __len__(self) -> int:
|
|
349
352
|
return len(self._attributes)
|
|
@@ -755,12 +758,16 @@ class PropertyClass(Model, Notify):
|
|
|
755
758
|
maxCount: Xsd_integer | None = None
|
|
756
759
|
order: Xsd_decimal | None = None
|
|
757
760
|
group: Xsd_QName | None = None
|
|
761
|
+
nodeKind: Xsd_QName | None = None
|
|
758
762
|
propkeys = {Xsd_QName(x.value) for x in PropClassAttr}
|
|
759
763
|
for key, val in attributes.items():
|
|
760
764
|
if key == 'rdf:type':
|
|
761
765
|
if val != 'sh:PropertyShape':
|
|
762
766
|
raise OldapError(f'Inconsistency, expected "sh:PropertyType", got "{val}".')
|
|
763
767
|
continue
|
|
768
|
+
elif key == 'sh:nodeKind':
|
|
769
|
+
nodeKind = val
|
|
770
|
+
continue
|
|
764
771
|
elif key == 'sh:path':
|
|
765
772
|
if isinstance(val, Xsd_QName):
|
|
766
773
|
self._property_class_iri = val
|
|
@@ -831,12 +838,15 @@ class PropertyClass(Model, Notify):
|
|
|
831
838
|
group=group)
|
|
832
839
|
|
|
833
840
|
if self._attributes.get(PropClassAttr.CLASS) is not None:
|
|
834
|
-
self._attributes[PropClassAttr.TYPE]
|
|
841
|
+
self._attributes[PropClassAttr.TYPE].add(OwlPropertyType.OwlObjectProperty)
|
|
835
842
|
dt = self._attributes.get(PropClassAttr.DATATYPE)
|
|
836
843
|
if dt and (dt != XsdDatatypes.anyURI and dt != XsdDatatypes.QName):
|
|
837
844
|
raise OldapError(f'Datatype "{dt}" not valid for OwlObjectProperty')
|
|
838
845
|
else:
|
|
839
|
-
|
|
846
|
+
if nodeKind in {Xsd_QName('sh:IRI'), Xsd_QName('sh:BlankNode'), Xsd_QName('sh:BlankNodeOrIRI')}:
|
|
847
|
+
self._attributes[PropClassAttr.TYPE].add(OwlPropertyType.OwlObjectProperty)
|
|
848
|
+
else:
|
|
849
|
+
self._attributes[PropClassAttr.TYPE].add(OwlPropertyType.OwlDataProperty)
|
|
840
850
|
#
|
|
841
851
|
# update all notifiers of properties
|
|
842
852
|
#
|
|
@@ -869,11 +879,23 @@ class PropertyClass(Model, Notify):
|
|
|
869
879
|
match attr:
|
|
870
880
|
case 'rdf:type':
|
|
871
881
|
if obj == 'owl:DatatypeProperty':
|
|
872
|
-
self._attributes[PropClassAttr.TYPE]
|
|
882
|
+
self._attributes[PropClassAttr.TYPE].add(OwlPropertyType.OwlDataProperty)
|
|
873
883
|
elif obj == 'owl:ObjectProperty':
|
|
874
|
-
self._attributes[PropClassAttr.TYPE]
|
|
884
|
+
self._attributes[PropClassAttr.TYPE].add(OwlPropertyType.OwlObjectProperty)
|
|
875
885
|
elif obj == 'rdf:Property':
|
|
876
|
-
self._attributes[PropClassAttr.TYPE]
|
|
886
|
+
self._attributes[PropClassAttr.TYPE].add(OwlPropertyType.StatementProperty)
|
|
887
|
+
elif obj == 'owl:TransitiveProperty':
|
|
888
|
+
self._attributes[PropClassAttr.TYPE].add(OwlPropertyType.TransitiveProperty)
|
|
889
|
+
elif obj == 'owl:SymmetricProperty':
|
|
890
|
+
self._attributes[PropClassAttr.TYPE].add(OwlPropertyType.SymmetricProperty)
|
|
891
|
+
elif obj == 'owl:ReflexiveProperty':
|
|
892
|
+
self._attributes[PropClassAttr.TYPE].add(OwlPropertyType.ReflexiveProperty)
|
|
893
|
+
elif obj == 'owl:IrreflexiveProperty':
|
|
894
|
+
self._attributes[PropClassAttr.TYPE].add(OwlPropertyType.IrreflexiveProperty)
|
|
895
|
+
elif obj == 'owl:InverseFunctionalProperty':
|
|
896
|
+
self._attributes[PropClassAttr.TYPE].add(OwlPropertyType.InverseFunctionalProperty)
|
|
897
|
+
else:
|
|
898
|
+
raise OldapErrorNotFound(f'Unknown owl:Property type "{obj}"')
|
|
877
899
|
case 'owl:subPropertyOf':
|
|
878
900
|
self._attributes[PropClassAttr.SUBPROPERTY_OF] = obj
|
|
879
901
|
case 'rdfs:range':
|
|
@@ -901,18 +923,18 @@ class PropertyClass(Model, Notify):
|
|
|
901
923
|
# Consistency checks
|
|
902
924
|
#
|
|
903
925
|
if self._statementProperty:
|
|
904
|
-
if self._attributes.get(PropClassAttr.TYPE)
|
|
926
|
+
if OwlPropertyType.StatementProperty not in self._attributes.get(PropClassAttr.TYPE):
|
|
905
927
|
raise OldapErrorInconsistency(f'Property "{self._property_class_iri}" is a statementProperty, but not an StatementProperty.')
|
|
906
928
|
else:
|
|
907
|
-
if self._attributes.get(PropClassAttr.TYPE)
|
|
929
|
+
if OwlPropertyType.StatementProperty in self._attributes.get(PropClassAttr.TYPE):
|
|
908
930
|
raise OldapErrorInconsistency(f'Property "{self._property_class_iri}" is not a statementProperty, but an StatementProperty.')
|
|
909
|
-
if self._attributes[PropClassAttr.TYPE]
|
|
931
|
+
if OwlPropertyType.OwlDataProperty in self._attributes[PropClassAttr.TYPE]:
|
|
910
932
|
if not datatype:
|
|
911
933
|
raise OldapError(f'OwlDataProperty "{self._property_class_iri}" has no rdfs:range datatype defined!')
|
|
912
934
|
if datatype != self._attributes.get(PropClassAttr.DATATYPE).value:
|
|
913
935
|
raise OldapError(
|
|
914
936
|
f'Property "{self._property_class_iri}" has inconsistent datatype definitions: OWL: "{datatype}" vs. SHACL: "{self._attributes[PropClassAttr.DATATYPE].value}"')
|
|
915
|
-
if self._attributes[PropClassAttr.TYPE]
|
|
937
|
+
if OwlPropertyType.OwlObjectProperty in self._attributes[PropClassAttr.TYPE]:
|
|
916
938
|
if not to_node_iri:
|
|
917
939
|
raise OldapError(f'OwlObjectProperty "{self._property_class_iri}" has no rdfs:range resource class defined!')
|
|
918
940
|
if to_node_iri != self._attributes.get(PropClassAttr.CLASS):
|
|
@@ -960,9 +982,10 @@ class PropertyClass(Model, Notify):
|
|
|
960
982
|
attributes = PropertyClass.__query_shacl(con, property._graph, property_class_iri)
|
|
961
983
|
property.parse_shacl(attributes=attributes)
|
|
962
984
|
property.read_owl()
|
|
963
|
-
|
|
985
|
+
property.clear_changeset()
|
|
964
986
|
|
|
965
987
|
property.update_notifier()
|
|
988
|
+
cache.set(property.property_class_iri, property)
|
|
966
989
|
|
|
967
990
|
return property
|
|
968
991
|
|
|
@@ -1062,14 +1085,14 @@ class PropertyClass(Model, Notify):
|
|
|
1062
1085
|
|
|
1063
1086
|
def create_owl_part1(self, timestamp: Xsd_dateTime, indent: int = 0, indent_inc: int = 4) -> str:
|
|
1064
1087
|
blank = ''
|
|
1065
|
-
sparql = f'{blank:{indent * indent_inc}}{self._property_class_iri.toRdf} rdf:type {self._attributes[PropClassAttr.TYPE].
|
|
1088
|
+
sparql = f'{blank:{indent * indent_inc}}{self._property_class_iri.toRdf} rdf:type {self._attributes[PropClassAttr.TYPE].toRdf}'
|
|
1066
1089
|
if self._attributes.get(PropClassAttr.SUBPROPERTY_OF):
|
|
1067
1090
|
sparql += f' ;\n{blank:{(indent + 1) * indent_inc}}rdfs:subPropertyOf {self._attributes[PropClassAttr.SUBPROPERTY_OF].toRdf}'
|
|
1068
1091
|
if self._internal:
|
|
1069
1092
|
sparql += f' ;\n{blank:{(indent + 1) * indent_inc}}rdfs:domain {self._internal.toRdf}'
|
|
1070
|
-
if self._attributes.get(PropClassAttr.TYPE)
|
|
1093
|
+
if self._attributes.get(PropClassAttr.TYPE) and OwlPropertyType.OwlDataProperty in self._attributes[PropClassAttr.TYPE]:
|
|
1071
1094
|
sparql += f' ;\n{blank:{(indent + 1) * indent_inc}}rdfs:range {self._attributes[PropClassAttr.DATATYPE].value}'
|
|
1072
|
-
elif self._attributes.get(PropClassAttr.TYPE)
|
|
1095
|
+
elif self._attributes.get(PropClassAttr.TYPE) and OwlPropertyType.OwlObjectProperty in self._attributes[PropClassAttr.TYPE]:
|
|
1073
1096
|
sparql += f' ;\n{blank:{(indent + 1) * indent_inc}}rdfs:range {self._attributes[PropClassAttr.CLASS].toRdf}'
|
|
1074
1097
|
sparql += f' ;\n{blank:{(indent + 1) * indent_inc}}dcterms:creator {self._con.userIri.toRdf}'
|
|
1075
1098
|
sparql += f' ;\n{blank:{(indent + 1) * indent_inc}}dcterms:created {timestamp.toRdf}'
|
|
@@ -1104,9 +1127,9 @@ class PropertyClass(Model, Notify):
|
|
|
1104
1127
|
# of the property within the given resource. However, rdfs:range is "global" for all use of this property!
|
|
1105
1128
|
#
|
|
1106
1129
|
if self._attributes.get(PropClassAttr.DATATYPE) or self._attributes.get(PropClassAttr.CLASS):
|
|
1107
|
-
if self._attributes[PropClassAttr.TYPE]
|
|
1130
|
+
if OwlPropertyType.OwlDataProperty in self._attributes[PropClassAttr.TYPE]:
|
|
1108
1131
|
sparql += f' ;\n{blank:{(indent + 1) * indent_inc}}owl:onDataRange {self._attributes[PropClassAttr.DATATYPE].value}'
|
|
1109
|
-
elif self._attributes[PropClassAttr.TYPE]
|
|
1132
|
+
elif OwlPropertyType.OwlObjectProperty in self._attributes[PropClassAttr.TYPE]:
|
|
1110
1133
|
sparql += f' ;\n{blank:{(indent + 1) * indent_inc}}owl:onClass {self._attributes[PropClassAttr.CLASS]}'
|
|
1111
1134
|
sparql += f' ;\n{blank:{indent * indent_inc}}]'
|
|
1112
1135
|
return sparql
|
|
@@ -1269,6 +1292,16 @@ class PropertyClass(Model, Notify):
|
|
|
1269
1292
|
attr=prop,
|
|
1270
1293
|
modified=self._modified,
|
|
1271
1294
|
indent=indent, indent_inc=indent_inc)
|
|
1295
|
+
elif prop.datatype == ObservableSet:
|
|
1296
|
+
if prop == PropClassAttr.TYPE:
|
|
1297
|
+
continue
|
|
1298
|
+
# sparql += self._attributes[prop].update_shacl(graph=self._graph,
|
|
1299
|
+
# owlclass_iri=owlclass_iri,
|
|
1300
|
+
# prop_iri=self._property_class_iri,
|
|
1301
|
+
# attr=prop,
|
|
1302
|
+
# modified=self._modified,
|
|
1303
|
+
# indent=indent, indent_inc=indent_inc)
|
|
1304
|
+
# print(gaga)
|
|
1272
1305
|
else:
|
|
1273
1306
|
raise OldapError(f'SHACL property {prop.value} should not have update action "MODIFY" ({prop.datatype}).')
|
|
1274
1307
|
sparql_list.append(sparql)
|
|
@@ -1367,6 +1400,13 @@ class PropertyClass(Model, Notify):
|
|
|
1367
1400
|
last_modified=self._modified,
|
|
1368
1401
|
indent=indent, indent_inc=indent_inc)
|
|
1369
1402
|
sparql_list.append(sparql)
|
|
1403
|
+
if prop == PropClassAttr.TYPE:
|
|
1404
|
+
|
|
1405
|
+
sparql = self._attributes[prop].update_sparql(graph=Xsd_QName(str(self._graph), 'onto'),
|
|
1406
|
+
subject=self._property_class_iri,
|
|
1407
|
+
ignoreitems={OwlPropertyType.OwlDataProperty,OwlPropertyType.OwlObjectProperty},
|
|
1408
|
+
field=prop)
|
|
1409
|
+
sparql_list.extend(sparql)
|
|
1370
1410
|
if prop == PropClassAttr.NAME:
|
|
1371
1411
|
if change.action == Action.CREATE:
|
|
1372
1412
|
sparql = self.name.create(graph=Xsd_QName(self._graph, 'onto'),
|
|
@@ -1574,7 +1614,7 @@ class PropertyClass(Model, Notify):
|
|
|
1574
1614
|
self._contributor = self._con.userIri
|
|
1575
1615
|
for prop, change in self._changeset.items():
|
|
1576
1616
|
if change.action == Action.MODIFY:
|
|
1577
|
-
self._attributes[prop].
|
|
1617
|
+
self._attributes[prop].clear_changeset()
|
|
1578
1618
|
self._changeset = {}
|
|
1579
1619
|
cache = CacheSingletonRedis()
|
|
1580
1620
|
cache.set(self._property_class_iri, self)
|
oldaplib/src/resourceclass.py
CHANGED
|
@@ -309,7 +309,7 @@ class ResourceClass(Model, Notify):
|
|
|
309
309
|
# now we add, if necessary, the mandatory superclass "oldap:Thing". Every ResourceClass is OLDAP must be
|
|
310
310
|
# a subclass of "oldap:Thing"! We don't do it for system things with a prefix of "oldap".
|
|
311
311
|
#
|
|
312
|
-
if
|
|
312
|
+
if self._owlclass_iri.prefix != "oldap":
|
|
313
313
|
thing_iri = Xsd_QName('oldap:Thing', validate=False)
|
|
314
314
|
if self._owlclass_iri != thing_iri:
|
|
315
315
|
if not new_kwargs.get(ResClassAttribute.SUPERCLASS.value.fragment):
|
|
@@ -639,7 +639,9 @@ class ResourceClass(Model, Notify):
|
|
|
639
639
|
s += f'{blank:{indent*2}}{qname} = {hasprop.prop} (minCount={hasprop.minCount}, maxCount={hasprop.maxCount}\n'
|
|
640
640
|
return s
|
|
641
641
|
|
|
642
|
-
def
|
|
642
|
+
def clear_changeset(self) -> None:
|
|
643
|
+
for prop in self._properties.values():
|
|
644
|
+
prop.clear_changeset()
|
|
643
645
|
super().clear_changeset()
|
|
644
646
|
|
|
645
647
|
def notifier(self, what: ResClassAttribute | Xsd_QName):
|
|
@@ -797,7 +799,7 @@ class ResourceClass(Model, Notify):
|
|
|
797
799
|
self._attributes[attr].set_notifier(self.notifier, attr)
|
|
798
800
|
|
|
799
801
|
self.__from_triplestore = True
|
|
800
|
-
self.
|
|
802
|
+
self.clear_changeset()
|
|
801
803
|
|
|
802
804
|
@staticmethod
|
|
803
805
|
def __query_resource_props(con: IConnection,
|
|
@@ -1082,7 +1084,7 @@ class ResourceClass(Model, Notify):
|
|
|
1082
1084
|
if not resclass.externalOntology:
|
|
1083
1085
|
resclass.__read_owl()
|
|
1084
1086
|
|
|
1085
|
-
resclass.
|
|
1087
|
+
resclass.clear_changeset()
|
|
1086
1088
|
|
|
1087
1089
|
resclass.update_notifier()
|
|
1088
1090
|
|
|
@@ -1296,7 +1298,7 @@ class ResourceClass(Model, Notify):
|
|
|
1296
1298
|
raise OldapErrorUpdateFailed(f'Creating resource "{self._owlclass_iri}" failed.')
|
|
1297
1299
|
else:
|
|
1298
1300
|
self._con.transaction_commit()
|
|
1299
|
-
self.
|
|
1301
|
+
self.clear_changeset()
|
|
1300
1302
|
cache = CacheSingletonRedis()
|
|
1301
1303
|
cache.set(self._owlclass_iri, self)
|
|
1302
1304
|
|
|
@@ -1859,7 +1861,7 @@ class ResourceClass(Model, Notify):
|
|
|
1859
1861
|
raise OldapErrorUpdateFailed(f'Update of {self._owlclass_iri} failed. {modtime_shacl} {modtime_owl} {timestamp}')
|
|
1860
1862
|
else:
|
|
1861
1863
|
self._con.transaction_commit()
|
|
1862
|
-
self.
|
|
1864
|
+
self.clear_changeset()
|
|
1863
1865
|
self._modified = timestamp
|
|
1864
1866
|
self._contributor = self._con.userIri
|
|
1865
1867
|
self._test_in_use = False
|
oldaplib/src/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.3.
|
|
1
|
+
__version__ = "0.3.4"
|
oldaplib/src/xsd/iri.py
CHANGED
oldaplib/src/xsd/xsd_anyuri.py
CHANGED
|
@@ -65,20 +65,20 @@ class Xsd_anyURI(Xsd):
|
|
|
65
65
|
if isinstance(value, str):
|
|
66
66
|
value = value.replace("<", "").replace(">", "")
|
|
67
67
|
if value.startswith("urn:"):
|
|
68
|
-
if not re.match(r'^urn:[a-z0-9][a-z0-9-]{0,31}:[^\s]+', value):
|
|
68
|
+
if not re.match(r'^urn:[a-z0-9][a-z0-9-]{0,31}:[^\s]+', str(value)):
|
|
69
69
|
raise OldapErrorValue(f'Invalid URN format for "{value}".')
|
|
70
70
|
elif value.startswith("http"):
|
|
71
|
-
if not re.match(self._uri_pattern, value):
|
|
71
|
+
if not re.match(self._uri_pattern, str(value)):
|
|
72
72
|
raise OldapErrorValue(f'Invalid string "{value}" for xsd:anyURI.')
|
|
73
73
|
if validate:
|
|
74
|
-
if not XsdValidator.validate(XsdDatatypes.anyURI, value):
|
|
74
|
+
if not XsdValidator.validate(XsdDatatypes.anyURI, str(value)):
|
|
75
75
|
raise OldapErrorValue(f'Invalid string "{value}" for anyURI')
|
|
76
76
|
else:
|
|
77
|
-
if not url(value):
|
|
77
|
+
if not url(str(value)):
|
|
78
78
|
raise OldapErrorValue(f'Invalid string "{value}" for xsd:anyURI.')
|
|
79
79
|
else:
|
|
80
80
|
raise OldapErrorValue(f'Invalid string "{value}" for anyURI')
|
|
81
|
-
self._value = value
|
|
81
|
+
self._value = str(value)
|
|
82
82
|
self._append_allowed = self._value[-1] == '/' or self._value[-1] == '#'
|
|
83
83
|
|
|
84
84
|
def __repr__(self) -> str:
|
oldaplib/src/xsd/xsd_qname.py
CHANGED
|
@@ -58,8 +58,11 @@ class Xsd_QName(Xsd):
|
|
|
58
58
|
raise OldapErrorValue(f'Invalid value for QName "{value}"')
|
|
59
59
|
else:
|
|
60
60
|
prefix = Xsd_NCName(value, validate=validate)
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
if fragment:
|
|
62
|
+
fragment = Xsd_NCName(fragment, validate=validate)
|
|
63
|
+
self._value = f'{prefix}:{fragment}'
|
|
64
|
+
else:
|
|
65
|
+
self._value = f'{prefix}:'
|
|
63
66
|
|
|
64
67
|
def __len__(self) -> int:
|
|
65
68
|
"""
|
oldaplib/test/test_context.py
CHANGED
|
@@ -78,9 +78,7 @@ PREFIX xml: <http://www.w3.org/XML/1998/namespace#>
|
|
|
78
78
|
PREFIX sh: <http://www.w3.org/ns/shacl#>
|
|
79
79
|
PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
|
|
80
80
|
PREFIX schema: <http://schema.org/>
|
|
81
|
-
PREFIX dc: <http://purl.org/dc/elements/1.1/>
|
|
82
81
|
PREFIX dcterms: <http://purl.org/dc/terms/>
|
|
83
|
-
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
|
|
84
82
|
PREFIX oldap: <http://oldap.org/base#>
|
|
85
83
|
PREFIX shared: <http://oldap.org/shared#>
|
|
86
84
|
PREFIX test: <http://www.test.org/gaga#>
|
|
@@ -98,9 +96,7 @@ PREFIX test: <http://www.test.org/gaga#>
|
|
|
98
96
|
@prefix sh: <http://www.w3.org/ns/shacl#> .
|
|
99
97
|
@prefix skos: <http://www.w3.org/2004/02/skos/core#> .
|
|
100
98
|
@prefix schema: <http://schema.org/> .
|
|
101
|
-
@prefix dc: <http://purl.org/dc/elements/1.1/> .
|
|
102
99
|
@prefix dcterms: <http://purl.org/dc/terms/> .
|
|
103
|
-
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
|
|
104
100
|
@prefix oldap: <http://oldap.org/base#> .
|
|
105
101
|
@prefix shared: <http://oldap.org/shared#> .
|
|
106
102
|
@prefix test: <http://www.test.org/gaga#> .
|