oldaplib 0.2.11__py3-none-any.whl → 0.3.1__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/example.trig +52 -0
- oldaplib/ontologies/oldap.trig +518 -425
- oldaplib/ontologies/oldap.ttl +38 -38
- oldaplib/ontologies/shared.trig +74 -116
- oldaplib/src/cachesingleton.py +4 -0
- oldaplib/src/datamodel.py +8 -1
- oldaplib/src/dtypes/bnode.py +1 -0
- oldaplib/src/enums/haspropertyattr.py +4 -2
- oldaplib/src/enums/projectattr.py +4 -0
- oldaplib/src/hasproperty.py +36 -5
- oldaplib/src/helpers/observable_dict.py +39 -8
- oldaplib/src/helpers/serializer.py +1 -1
- oldaplib/src/objectfactory.py +1 -1
- oldaplib/src/oldaplist.py +2 -2
- oldaplib/src/oldaplistnode.py +24 -24
- oldaplib/src/project.py +144 -12
- oldaplib/src/propertyclass.py +39 -30
- oldaplib/src/resourceclass.py +174 -161
- oldaplib/src/version.py +1 -1
- oldaplib/test/test_datamodel.py +19 -14
- oldaplib/test/test_observable_dict.py +1 -1
- oldaplib/test/test_project.py +79 -3
- oldaplib/test/test_propertyclass.py +2 -2
- oldaplib/test/test_resourceclass.py +160 -9
- oldaplib/testdata/connection_test.trig +8 -19
- oldaplib/testdata/objectfactory_test.trig +67 -67
- {oldaplib-0.2.11.dist-info → oldaplib-0.3.1.dist-info}/METADATA +1 -1
- {oldaplib-0.2.11.dist-info → oldaplib-0.3.1.dist-info}/RECORD +29 -28
- {oldaplib-0.2.11.dist-info → oldaplib-0.3.1.dist-info}/WHEEL +0 -0
oldaplib/src/project.py
CHANGED
|
@@ -2,8 +2,9 @@ import json
|
|
|
2
2
|
from copy import deepcopy
|
|
3
3
|
from dataclasses import dataclass
|
|
4
4
|
from functools import partial
|
|
5
|
+
from pprint import pprint
|
|
5
6
|
|
|
6
|
-
from typing import List, Self, Any
|
|
7
|
+
from typing import List, Self, Any, Callable
|
|
7
8
|
from datetime import date, datetime
|
|
8
9
|
|
|
9
10
|
from oldaplib.src.cachesingleton import CacheSingletonRedis
|
|
@@ -13,6 +14,7 @@ from oldaplib.src.helpers.context import Context
|
|
|
13
14
|
from oldaplib.src.enums.action import Action
|
|
14
15
|
from oldaplib.src.dtypes.namespaceiri import NamespaceIRI
|
|
15
16
|
from oldaplib.src.helpers.irincname import IriOrNCName
|
|
17
|
+
from oldaplib.src.helpers.observable_dict import ObservableDict
|
|
16
18
|
from oldaplib.src.helpers.serializer import serializer
|
|
17
19
|
from oldaplib.src.helpers.tools import lprint
|
|
18
20
|
from oldaplib.src.xsd.iri import Iri
|
|
@@ -108,7 +110,7 @@ class Project(Model):
|
|
|
108
110
|
#_attributes: dict[ProjectAttr, ProjectAttrTypes]
|
|
109
111
|
#__changeset: dict[ProjectAttr, ProjectAttrChange]
|
|
110
112
|
|
|
111
|
-
__slots__ = ('projectIri', 'projectShortName', 'label', 'comment', 'namespaceIri', 'projectStart', 'projectEnd')
|
|
113
|
+
__slots__ = ('projectIri', 'projectShortName', 'label', 'comment', 'namespaceIri', 'projectStart', 'projectEnd', 'usesExternalOntology')
|
|
112
114
|
|
|
113
115
|
def __init__(self, *,
|
|
114
116
|
con: IConnection,
|
|
@@ -174,6 +176,8 @@ class Project(Model):
|
|
|
174
176
|
partial(Project._get_value, attr=attr),
|
|
175
177
|
partial(Project._set_value, attr=attr),
|
|
176
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)
|
|
177
181
|
self._changeset = {}
|
|
178
182
|
|
|
179
183
|
def update_notifier(self):
|
|
@@ -181,6 +185,10 @@ class Project(Model):
|
|
|
181
185
|
if getattr(value, 'set_notifier', None) is not None:
|
|
182
186
|
value.set_notifier(self.notifier, attr)
|
|
183
187
|
|
|
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
|
+
|
|
184
192
|
def _as_dict(self):
|
|
185
193
|
return {x.fragment: y for x, y in self._attributes.items()} | super()._as_dict()
|
|
186
194
|
|
|
@@ -231,6 +239,31 @@ class Project(Model):
|
|
|
231
239
|
"""
|
|
232
240
|
self._changeset[attr] = AttributeChange(self._attributes[attr], Action.MODIFY)
|
|
233
241
|
|
|
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
|
+
|
|
266
|
+
|
|
234
267
|
@classmethod
|
|
235
268
|
def read(cls,
|
|
236
269
|
con: IConnection,
|
|
@@ -295,15 +328,17 @@ class Project(Model):
|
|
|
295
328
|
tmp._con = con
|
|
296
329
|
return tmp
|
|
297
330
|
query += f"""
|
|
298
|
-
SELECT ?proj ?prop ?val
|
|
299
|
-
FROM NAMED oldap:admin
|
|
331
|
+
SELECT ?proj ?prop ?val ?prefix ?iri
|
|
300
332
|
WHERE {{
|
|
301
333
|
GRAPH oldap:admin {{
|
|
302
334
|
?proj a oldap:Project .
|
|
303
|
-
?proj oldap:projectShortName
|
|
335
|
+
?proj oldap:projectShortName {shortname.toRdf} .
|
|
304
336
|
?proj ?prop ?val .
|
|
337
|
+
OPTIONAL {{
|
|
338
|
+
?val oldap:prefix ?prefix .
|
|
339
|
+
?val oldap:fullIri ?iri
|
|
340
|
+
}}
|
|
305
341
|
}}
|
|
306
|
-
FILTER(?shortname = {shortname.toRdf})
|
|
307
342
|
}}
|
|
308
343
|
"""
|
|
309
344
|
jsonobj = con.query(query)
|
|
@@ -320,6 +355,7 @@ class Project(Model):
|
|
|
320
355
|
comment = LangString()
|
|
321
356
|
projectStart = None
|
|
322
357
|
projectEnd = None
|
|
358
|
+
usesExternalOntology: dict[Xsd_QName, NamespaceIRI] | None = None
|
|
323
359
|
for r in res:
|
|
324
360
|
if projectIri is None:
|
|
325
361
|
projectIri = r['proj']
|
|
@@ -344,6 +380,10 @@ class Project(Model):
|
|
|
344
380
|
projectStart = r['val']
|
|
345
381
|
case 'oldap:projectEnd':
|
|
346
382
|
projectEnd = r['val']
|
|
383
|
+
case 'oldap:usesExternalOntology':
|
|
384
|
+
if not usesExternalOntology:
|
|
385
|
+
usesExternalOntology = {}
|
|
386
|
+
usesExternalOntology[r['prefix']] = NamespaceIRI(r['iri'])
|
|
347
387
|
if label:
|
|
348
388
|
label.changeset_clear()
|
|
349
389
|
label.set_notifier(cls.notifier, Xsd_QName(ProjectAttr.LABEL.value))
|
|
@@ -362,7 +402,8 @@ class Project(Model):
|
|
|
362
402
|
namespaceIri=namespaceIri,
|
|
363
403
|
comment=comment,
|
|
364
404
|
projectStart=projectStart,
|
|
365
|
-
projectEnd=projectEnd
|
|
405
|
+
projectEnd=projectEnd,
|
|
406
|
+
usesExternalOntology=usesExternalOntology)
|
|
366
407
|
cache = CacheSingletonRedis()
|
|
367
408
|
cache.set(instance.projectIri, instance, instance.projectShortName)
|
|
368
409
|
return instance
|
|
@@ -480,7 +521,12 @@ class Project(Model):
|
|
|
480
521
|
for attr, value in self._attributes.items():
|
|
481
522
|
if not value:
|
|
482
523
|
continue
|
|
483
|
-
|
|
524
|
+
if attr == ProjectAttr.USES_EXTERNAL_ONTOLOGY:
|
|
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}'
|
|
484
530
|
sparql2 += f' .\n{blank:{(indent + 1) * indent_inc}}}}\n'
|
|
485
531
|
sparql2 += f'{blank:{indent * indent_inc}}}}\n'
|
|
486
532
|
|
|
@@ -520,6 +566,22 @@ class Project(Model):
|
|
|
520
566
|
:raises OldapErrorUpdateFailed: Raised if the update fails due to timestamp mismatch or other inconsistencies.
|
|
521
567
|
:raises OldapError: Raised for other internal errors.
|
|
522
568
|
"""
|
|
569
|
+
|
|
570
|
+
def dict_diff(a: dict, b: dict) -> dict:
|
|
571
|
+
"""
|
|
572
|
+
Compare two dicts and return added, removed, and changed key–values.
|
|
573
|
+
"""
|
|
574
|
+
a_keys = set(a) if a else set()
|
|
575
|
+
b_keys = set(b) if b else set()
|
|
576
|
+
shared = a_keys & b_keys
|
|
577
|
+
|
|
578
|
+
return {
|
|
579
|
+
'added': {k: b[k] for k in b_keys - a_keys},
|
|
580
|
+
'removed': {k: a[k] for k in a_keys - b_keys},
|
|
581
|
+
'changed': {k: (a[k], b[k]) for k in shared if a[k] != b[k]},
|
|
582
|
+
'same': {k: a[k] for k in shared if a[k] == b[k]},
|
|
583
|
+
}
|
|
584
|
+
|
|
523
585
|
result, message = self.check_for_permissions()
|
|
524
586
|
if not result:
|
|
525
587
|
raise OldapErrorNoPermission(message)
|
|
@@ -545,20 +607,89 @@ class Project(Model):
|
|
|
545
607
|
field=Xsd_QName(field.value))
|
|
546
608
|
sparql_list.append(sparql)
|
|
547
609
|
continue
|
|
610
|
+
if field == ProjectAttr.USES_EXTERNAL_ONTOLOGY:
|
|
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
|
|
548
674
|
sparql = f'{blank:{indent * indent_inc}}# Project field "{field.value}" with action "{change.action.value}"\n'
|
|
549
|
-
sparql += f'{blank:{indent * indent_inc}}WITH oldap:admin\n'
|
|
550
675
|
if change.action != Action.CREATE:
|
|
551
676
|
sparql += f'{blank:{indent * indent_inc}}DELETE {{\n'
|
|
552
|
-
sparql += f'{blank:{(indent + 1) * indent_inc}}
|
|
677
|
+
sparql += f'{blank:{(indent + 1) * indent_inc}}GRAPH oldap:admin {{\n'
|
|
678
|
+
sparql += f'{blank:{(indent + 2) * indent_inc}}?project {field.value} {change.old_value.toRdf} .\n'
|
|
679
|
+
sparql += f'{blank:{(indent + 1)* indent_inc}}}}\n'
|
|
553
680
|
sparql += f'{blank:{indent * indent_inc}}}}\n'
|
|
554
681
|
if change.action != Action.DELETE:
|
|
555
682
|
sparql += f'{blank:{indent * indent_inc}}INSERT {{\n'
|
|
556
|
-
sparql += f'{blank:{(indent + 1) * indent_inc}}
|
|
683
|
+
sparql += f'{blank:{(indent + 1) * indent_inc}}GRAPH oldap:admin {{\n'
|
|
684
|
+
sparql += f'{blank:{(indent + 2) * indent_inc}}?project {field.value} {self._attributes[field].toRdf} .\n'
|
|
685
|
+
sparql += f'{blank:{(indent + 1) * indent_inc}}}}\n'
|
|
557
686
|
sparql += f'{blank:{indent * indent_inc}}}}\n'
|
|
558
687
|
sparql += f'{blank:{indent * indent_inc}}WHERE {{\n'
|
|
559
688
|
sparql += f'{blank:{(indent + 1) * indent_inc}}BIND({self.projectIri.toRdf} as ?project)\n'
|
|
560
689
|
if change.action != Action.CREATE:
|
|
561
|
-
sparql += f'{blank:{(indent + 1) * indent_inc}}
|
|
690
|
+
sparql += f'{blank:{(indent + 1) * indent_inc}}GRAPH oldap:admin {{\n'
|
|
691
|
+
sparql += f'{blank:{(indent + 2) * indent_inc}}?project {field.value} {change.old_value.toRdf} .\n'
|
|
692
|
+
sparql += f'{blank:{(indent + 1) * indent_inc}}}}\n'
|
|
562
693
|
sparql += f'{blank:{indent * indent_inc}}}}'
|
|
563
694
|
sparql_list.append(sparql)
|
|
564
695
|
sparql = context.sparql_context
|
|
@@ -570,6 +701,7 @@ class Project(Model):
|
|
|
570
701
|
self.set_modified_by_iri(Xsd_QName('oldap:admin'), self.projectIri, self.modified, timestamp)
|
|
571
702
|
modtime = self.get_modified_by_iri(Xsd_QName('oldap:admin'), self.projectIri)
|
|
572
703
|
except OldapError:
|
|
704
|
+
print(sparql)
|
|
573
705
|
self._con.transaction_abort()
|
|
574
706
|
raise
|
|
575
707
|
if timestamp != modtime:
|
oldaplib/src/propertyclass.py
CHANGED
|
@@ -66,8 +66,8 @@ Attributes = dict[Iri, PropTypes]
|
|
|
66
66
|
@serializer
|
|
67
67
|
class HasPropertyData:
|
|
68
68
|
refprop: Iri | None = None
|
|
69
|
-
minCount:
|
|
70
|
-
maxCount:
|
|
69
|
+
minCount: Xsd_nonNegativeInteger | None = None
|
|
70
|
+
maxCount: Xsd_nonNegativeInteger | None = None
|
|
71
71
|
order: Xsd_decimal | None = None
|
|
72
72
|
group: Iri | None = None
|
|
73
73
|
|
|
@@ -88,19 +88,16 @@ class HasPropertyData:
|
|
|
88
88
|
def create_owl(self, indent: int = 0, indent_inc: int = 4):
|
|
89
89
|
blank = ''
|
|
90
90
|
sparql = ''
|
|
91
|
-
min_count = Xsd_nonNegativeInteger(int(self.minCount)) if self.minCount else None
|
|
92
|
-
max_count = Xsd_nonNegativeInteger(int(self.maxCount)) if self.maxCount else None
|
|
93
91
|
|
|
94
|
-
if
|
|
95
|
-
sparql += f' ;\n{blank:{(indent + 1) * indent_inc}}owl:qualifiedCardinality {
|
|
92
|
+
if self.minCount and self.maxCount and self.minCount == self.maxCount:
|
|
93
|
+
sparql += f' ;\n{blank:{(indent + 1) * indent_inc}}owl:qualifiedCardinality {self.minCount.toRdf}'
|
|
96
94
|
else:
|
|
97
|
-
if
|
|
98
|
-
sparql += f' ;\n{blank:{(indent + 1) * indent_inc}}owl:minQualifiedCardinality {
|
|
99
|
-
if
|
|
100
|
-
sparql += f' ;\n{blank:{(indent + 1) * indent_inc}}owl:maxQualifiedCardinality {
|
|
95
|
+
if self.minCount:
|
|
96
|
+
sparql += f' ;\n{blank:{(indent + 1) * indent_inc}}owl:minQualifiedCardinality {self.minCount.toRdf}'
|
|
97
|
+
if self.maxCount:
|
|
98
|
+
sparql += f' ;\n{blank:{(indent + 1) * indent_inc}}owl:maxQualifiedCardinality {self.maxCount.toRdf}'
|
|
101
99
|
return sparql
|
|
102
100
|
|
|
103
|
-
|
|
104
101
|
#@strict
|
|
105
102
|
@serializer
|
|
106
103
|
class PropertyClass(Model, Notify):
|
|
@@ -196,8 +193,8 @@ class PropertyClass(Model, Notify):
|
|
|
196
193
|
property_class_iri: Iri | str | None = None,
|
|
197
194
|
notifier: Callable[[PropClassAttr], None] | None = None,
|
|
198
195
|
notify_data: PropClassAttr | None = None,
|
|
199
|
-
|
|
200
|
-
|
|
196
|
+
_statementProperty: bool | Xsd_boolean= False,
|
|
197
|
+
_externalOntology: bool | Xsd_boolean = False,
|
|
201
198
|
_internal: Iri | None = None, # DO NOT USE!! Only for serialization!
|
|
202
199
|
_force_external: bool | None = None, # DO NOT USE!! Only for serialization!
|
|
203
200
|
_from_triplestore: bool = False,
|
|
@@ -230,12 +227,12 @@ class PropertyClass(Model, Notify):
|
|
|
230
227
|
:type notifier: Callable[[PropClassAttr], None] | None
|
|
231
228
|
:param notify_data: Data or attribute passed to the notifier function, optional.
|
|
232
229
|
:type notify_data: PropClassAttr | None
|
|
233
|
-
:param
|
|
230
|
+
:param _statementProperty: Boolean indicating if the property is a statement-property
|
|
234
231
|
(used for RDF*star statements).
|
|
235
|
-
:type
|
|
236
|
-
:param
|
|
232
|
+
:type _statementProperty: bool
|
|
233
|
+
:param _externalOntology: Boolean indicating whether this property comes from an
|
|
237
234
|
external ontology (false by default).
|
|
238
|
-
:type
|
|
235
|
+
:type _externalOntology: bool
|
|
239
236
|
:param validate: Boolean that determines whether validation is active.
|
|
240
237
|
:type validate: bool
|
|
241
238
|
:param kwargs: Arbitrary additional named arguments that might be used
|
|
@@ -255,8 +252,8 @@ class PropertyClass(Model, Notify):
|
|
|
255
252
|
validate=validate)
|
|
256
253
|
Notify.__init__(self, notifier, notify_data)
|
|
257
254
|
|
|
258
|
-
self._statementProperty =
|
|
259
|
-
self._externalOntology =
|
|
255
|
+
self._statementProperty = _statementProperty if isinstance(_statementProperty, Xsd_boolean) else Xsd_boolean(_statementProperty, validate=True)
|
|
256
|
+
self._externalOntology = _externalOntology if isinstance(_externalOntology, Xsd_boolean) else Xsd_boolean(_externalOntology, validate=True)
|
|
260
257
|
if self._externalOntology:
|
|
261
258
|
self._force_external = True
|
|
262
259
|
if not isinstance(project, Project):
|
|
@@ -322,7 +319,7 @@ class PropertyClass(Model, Notify):
|
|
|
322
319
|
self._attributes[PropClassAttr.TYPE] = OwlPropertyType.OwlObjectProperty
|
|
323
320
|
if self._attributes.get(PropClassAttr.DATATYPE) is not None:
|
|
324
321
|
raise OldapError(f'Datatype "{self._attributes.get(PropClassAttr.DATATYPE)}" not possible for OwlObjectProperty')
|
|
325
|
-
|
|
322
|
+
elif self._attributes.get(PropClassAttr.DATATYPE) is not None:
|
|
326
323
|
self._attributes[PropClassAttr.TYPE] = OwlPropertyType.OwlDataProperty
|
|
327
324
|
|
|
328
325
|
#
|
|
@@ -348,6 +345,9 @@ class PropertyClass(Model, Notify):
|
|
|
348
345
|
self.__version = SemanticVersion()
|
|
349
346
|
self.__from_triplestore = _from_triplestore
|
|
350
347
|
|
|
348
|
+
def __len__(self) -> int:
|
|
349
|
+
return len(self._attributes)
|
|
350
|
+
|
|
351
351
|
def update_notifier(self,
|
|
352
352
|
notifier: Callable[[PropClassAttr], None] | None = None,
|
|
353
353
|
notify_data: PropClassAttr | None = None,):
|
|
@@ -363,6 +363,8 @@ class PropertyClass(Model, Notify):
|
|
|
363
363
|
'property_class_iri': self.property_class_iri,
|
|
364
364
|
**({'_internal': self._internal} if self._internal else {}),
|
|
365
365
|
**({'_force_external': self._force_external} if self._force_external else {}),
|
|
366
|
+
**({'_externalOntology': self._externalOntology} if self._externalOntology else {}),
|
|
367
|
+
**({'_statementProperty': self._statementProperty} if self._statementProperty else {}),
|
|
366
368
|
'_from_triplestore': self.__from_triplestore,
|
|
367
369
|
}
|
|
368
370
|
|
|
@@ -700,6 +702,8 @@ class PropertyClass(Model, Notify):
|
|
|
700
702
|
if attributes.get(attriri) is None:
|
|
701
703
|
attributes[attriri] = XsdSet()
|
|
702
704
|
attributes[attriri].add(r['oo'])
|
|
705
|
+
elif r['attriri'].fragment == 'or':
|
|
706
|
+
return # TODO: ignore sh:or for the moment... It's in SHACL, but we do not yet support it
|
|
703
707
|
else:
|
|
704
708
|
if isinstance(r['value'], Xsd_string) and r['value'].lang is not None:
|
|
705
709
|
if attributes.get(attriri) is None:
|
|
@@ -710,6 +714,7 @@ class PropertyClass(Model, Notify):
|
|
|
710
714
|
raise OldapError(f'Invalid value for attribute {attriri}: {err}.')
|
|
711
715
|
else:
|
|
712
716
|
if attributes.get(attriri) is not None:
|
|
717
|
+
print("===>", r)
|
|
713
718
|
raise OldapError(f'Property ({propiri}) attribute "{attriri}" already defined (value="{r['value']}", type="{type(r['value']).__name__}").')
|
|
714
719
|
attributes[attriri] = r['value']
|
|
715
720
|
|
|
@@ -1016,11 +1021,12 @@ class PropertyClass(Model, Notify):
|
|
|
1016
1021
|
sparql += f'\n{blank:{(indent + 1) * indent_inc}} {bnode} sh:path {self._property_class_iri.toRdf}'
|
|
1017
1022
|
else:
|
|
1018
1023
|
sparql += f'\n{blank:{(indent + 1) * indent_inc}}sh:path {self._property_class_iri.toRdf}'
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
+
if len(self._attributes) > 0:
|
|
1025
|
+
sparql += f' ;\n{blank:{(indent + 1) * indent_inc}}schema:version {self.__version.toRdf}'
|
|
1026
|
+
sparql += f' ;\n{blank:{(indent + 1) * indent_inc}}dcterms:creator {self._con.userIri.toRdf}'
|
|
1027
|
+
sparql += f' ;\n{blank:{(indent + 1) * indent_inc}}dcterms:created {timestamp.toRdf}'
|
|
1028
|
+
sparql += f' ;\n{blank:{(indent + 1) * indent_inc}}dcterms:contributor {self._con.userIri.toRdf}'
|
|
1029
|
+
sparql += f' ;\n{blank:{(indent + 1) * indent_inc}}dcterms:modified {timestamp.toRdf}'
|
|
1024
1030
|
sparql += f' ;\n{blank:{(indent + 1) * indent_inc}}oldap:statementProperty {self._statementProperty.toRdf}'
|
|
1025
1031
|
sparql += f' ;\n{blank:{(indent + 1) * indent_inc}}oldap:externalOntology {self._externalOntology.toRdf}'
|
|
1026
1032
|
for prop, value in self._attributes.items():
|
|
@@ -1080,6 +1086,8 @@ class PropertyClass(Model, Notify):
|
|
|
1080
1086
|
def create_owl_part2(self, *,
|
|
1081
1087
|
haspropdata: HasPropertyData | None = None,
|
|
1082
1088
|
indent: int = 0, indent_inc: int = 4) -> str:
|
|
1089
|
+
if not (haspropdata.minCount or haspropdata.maxCount or self._attributes.get(PropClassAttr.DATATYPE) or self._attributes.get(PropClassAttr.CLASS)):
|
|
1090
|
+
return '' # no OWL to be added!
|
|
1083
1091
|
blank = ''
|
|
1084
1092
|
sparql = f'{blank:{indent * indent_inc}}[\n'
|
|
1085
1093
|
sparql += f'{blank:{(indent + 1) * indent_inc}}rdf:type owl:Restriction ;\n'
|
|
@@ -1093,13 +1101,14 @@ class PropertyClass(Model, Notify):
|
|
|
1093
1101
|
if haspropdata.maxCount:
|
|
1094
1102
|
sparql += f' ;\n{blank:{(indent + 1)*indent_inc}}owl:maxQualifiedCardinality {haspropdata.maxCount.toRdf}'
|
|
1095
1103
|
#
|
|
1096
|
-
# (NOTE: owl:onClass and owl:
|
|
1104
|
+
# (NOTE: owl:onClass and owl:onDataRange can be used only in a restriction and are "local" to the use
|
|
1097
1105
|
# of the property within the given resource. However, rdfs:range is "global" for all use of this property!
|
|
1098
1106
|
#
|
|
1099
|
-
if self._attributes
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1107
|
+
if self._attributes.get(PropClassAttr.DATATYPE) or self._attributes.get(PropClassAttr.CLASS):
|
|
1108
|
+
if self._attributes[PropClassAttr.TYPE] == OwlPropertyType.OwlDataProperty:
|
|
1109
|
+
sparql += f' ;\n{blank:{(indent + 1) * indent_inc}}owl:onDataRange {self._attributes[PropClassAttr.DATATYPE].value}'
|
|
1110
|
+
elif self._attributes[PropClassAttr.TYPE] == OwlPropertyType.OwlObjectProperty:
|
|
1111
|
+
sparql += f' ;\n{blank:{(indent + 1) * indent_inc}}owl:onClass {self._attributes[PropClassAttr.CLASS]}'
|
|
1103
1112
|
sparql += f' ;\n{blank:{indent * indent_inc}}]'
|
|
1104
1113
|
return sparql
|
|
1105
1114
|
|