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/resourceclass.py
CHANGED
|
@@ -3,15 +3,14 @@ from dataclasses import dataclass
|
|
|
3
3
|
from datetime import datetime
|
|
4
4
|
from functools import partial
|
|
5
5
|
from pprint import pprint
|
|
6
|
-
from typing import Union, List, Dict, Callable, Self, Any, TypeVar
|
|
7
|
-
from unittest import case
|
|
6
|
+
from typing import Union, List, Dict, Callable, Self, Any, TypeVar
|
|
8
7
|
|
|
9
|
-
from oldaplib.src.cachesingleton import
|
|
8
|
+
from oldaplib.src.cachesingleton import CacheSingletonRedis
|
|
10
9
|
from oldaplib.src.enums.adminpermissions import AdminPermission
|
|
11
10
|
from oldaplib.src.enums.attributeclass import AttributeClass
|
|
12
11
|
from oldaplib.src.enums.haspropertyattr import HasPropertyAttr
|
|
13
12
|
from oldaplib.src.globalconfig import GlobalConfig
|
|
14
|
-
from oldaplib.src.hasproperty import HasProperty
|
|
13
|
+
from oldaplib.src.hasproperty import HasProperty, PropType
|
|
15
14
|
from oldaplib.src.helpers.Notify import Notify
|
|
16
15
|
from oldaplib.src.helpers.irincname import IriOrNCName
|
|
17
16
|
from oldaplib.src.helpers.observable_dict import ObservableDict
|
|
@@ -174,16 +173,16 @@ class ResourceClass(Model, Notify):
|
|
|
174
173
|
"""
|
|
175
174
|
if isinstance(superclass, (list, tuple, set)):
|
|
176
175
|
for sc in superclass:
|
|
177
|
-
if sc is None or sc in self.
|
|
176
|
+
if sc is None or sc in self._attributes[ResClassAttribute.SUPERCLASS]:
|
|
178
177
|
continue
|
|
179
178
|
iri, sucla = self.__check(sc, validate=validate)
|
|
180
|
-
self.
|
|
179
|
+
self._attributes[ResClassAttribute.SUPERCLASS][iri] = sucla
|
|
181
180
|
self.notify()
|
|
182
181
|
else:
|
|
183
|
-
if superclass in self.
|
|
182
|
+
if superclass in self._attributes[ResClassAttribute.SUPERCLASS]:
|
|
184
183
|
raise OldapErrorAlreadyExists(f'Superclass "{superclass}" already exists in superclass list of {self._owlclass_iri}.')
|
|
185
184
|
iri, sucla = self.__check(superclass, validate=validate)
|
|
186
|
-
self.
|
|
185
|
+
self._attributes[ResClassAttribute.SUPERCLASS][iri] = sucla
|
|
187
186
|
self.notify()
|
|
188
187
|
|
|
189
188
|
def del_superclasses(self, superclass: SuperclassParam, validate = False):
|
|
@@ -208,15 +207,15 @@ class ResourceClass(Model, Notify):
|
|
|
208
207
|
if isinstance(superclass, (list, tuple, set)):
|
|
209
208
|
for sc in superclass:
|
|
210
209
|
scIri = Iri(sc, validate=validate)
|
|
211
|
-
if scIri not in self.
|
|
210
|
+
if scIri not in self._attributes[ResClassAttribute.SUPERCLASS]:
|
|
212
211
|
raise OldapErrorValue(f'Superclass "{scIri}" not found in superclass list')
|
|
213
|
-
del self.
|
|
212
|
+
del self._attributes[ResClassAttribute.SUPERCLASS][scIri]
|
|
214
213
|
self.notify()
|
|
215
214
|
else:
|
|
216
215
|
superclassIri = Iri(superclass, validate=validate)
|
|
217
|
-
if superclassIri not in self.
|
|
216
|
+
if superclassIri not in self._attributes[ResClassAttribute.SUPERCLASS]:
|
|
218
217
|
raise OldapErrorValue(f'Superclass "{superclass}" not found in superclass list')
|
|
219
|
-
del self.
|
|
218
|
+
del self._attributes[ResClassAttribute.SUPERCLASS][superclassIri]
|
|
220
219
|
self.notify()
|
|
221
220
|
|
|
222
221
|
|
|
@@ -225,7 +224,7 @@ class ResourceClass(Model, Notify):
|
|
|
225
224
|
project: Project | Iri | Xsd_NCName | str,
|
|
226
225
|
owlclass_iri: Iri | str | None = None,
|
|
227
226
|
hasproperties: List[HasProperty] | None = None,
|
|
228
|
-
|
|
227
|
+
_externalOntology: bool | Xsd_boolean = False,
|
|
229
228
|
notifier: Callable[[PropClassAttr], None] | None = None,
|
|
230
229
|
notify_data: PropClassAttr | None = None,
|
|
231
230
|
creator: Iri | None = None, # DO NO USE! Only for jsonify!!
|
|
@@ -275,9 +274,7 @@ class ResourceClass(Model, Notify):
|
|
|
275
274
|
contributor=con.userIri,
|
|
276
275
|
modified=modified,
|
|
277
276
|
validate=validate)
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
self._externalOntology = external_ontology if isinstance(external_ontology, Xsd_boolean) else Xsd_boolean(external_ontology)
|
|
277
|
+
self._externalOntology = _externalOntology if isinstance(_externalOntology, Xsd_boolean) else Xsd_boolean(_externalOntology)
|
|
281
278
|
if isinstance(project, Project):
|
|
282
279
|
self._project = project
|
|
283
280
|
else:
|
|
@@ -308,17 +305,18 @@ class ResourceClass(Model, Notify):
|
|
|
308
305
|
else:
|
|
309
306
|
new_kwargs[name] = value
|
|
310
307
|
#
|
|
311
|
-
# now we add if necessary the mandatory superclass "oldap:Thing". Every ResourceClass is OLDAP must be
|
|
312
|
-
# a subclass of "oldap:Thing"!
|
|
308
|
+
# now we add, if necessary, the mandatory superclass "oldap:Thing". Every ResourceClass is OLDAP must be
|
|
309
|
+
# a subclass of "oldap:Thing"! We don't do it for system things with a prefix of "oldap".
|
|
313
310
|
#
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
if
|
|
317
|
-
new_kwargs
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
311
|
+
if owlclass_iri.prefix != "oldap":
|
|
312
|
+
thing_iri = Iri('oldap:Thing', validate=False)
|
|
313
|
+
if self._owlclass_iri != thing_iri:
|
|
314
|
+
if not new_kwargs.get(ResClassAttribute.SUPERCLASS.value.fragment):
|
|
315
|
+
new_kwargs[ResClassAttribute.SUPERCLASS.value.fragment] = self.assign_superclass(thing_iri)
|
|
316
|
+
else:
|
|
317
|
+
if not thing_iri in new_kwargs[ResClassAttribute.SUPERCLASS.value.fragment]:
|
|
318
|
+
thing = ResourceClass.read(self._con, self._sysproject, thing_iri)
|
|
319
|
+
new_kwargs[ResClassAttribute.SUPERCLASS.value.fragment][thing_iri] = thing
|
|
322
320
|
self.set_attributes(new_kwargs, ResClassAttribute)
|
|
323
321
|
|
|
324
322
|
self._properties = {}
|
|
@@ -329,9 +327,13 @@ class ResourceClass(Model, Notify):
|
|
|
329
327
|
try:
|
|
330
328
|
hasprop.prop = PropertyClass.read(self._con, self._project, fixed_prop)
|
|
331
329
|
except OldapErrorNotFound as err:
|
|
332
|
-
|
|
330
|
+
prop = PropertyClass(con=self._con,
|
|
331
|
+
project=self._project,
|
|
332
|
+
property_class_iri=fixed_prop,
|
|
333
|
+
_externalOntology=Xsd_boolean(True))
|
|
334
|
+
hasprop.prop = prop
|
|
333
335
|
elif isinstance(hasprop.prop, PropertyClass): # an internal, private property definition
|
|
334
|
-
if not hasprop.prop._force_external:
|
|
336
|
+
if hasprop.type == PropType.INTERNAL and not hasprop.prop._force_external:
|
|
335
337
|
hasprop.prop._internal = owlclass_iri
|
|
336
338
|
else:
|
|
337
339
|
raise OldapErrorValue(f'Unexpected property type: {type(hasprop.prop).__name__}')
|
|
@@ -392,6 +394,7 @@ class ResourceClass(Model, Notify):
|
|
|
392
394
|
'project': self._project.projectShortName,
|
|
393
395
|
'owlclass_iri': self._owlclass_iri,
|
|
394
396
|
'hasproperties': [x for x in self._properties.values()],
|
|
397
|
+
**({'_externalOntology': self._externalOntology} if self._externalOntology else {}),
|
|
395
398
|
}
|
|
396
399
|
|
|
397
400
|
def __eq__(self, other: Self):
|
|
@@ -443,7 +446,9 @@ class ResourceClass(Model, Notify):
|
|
|
443
446
|
raise ValueError(f'Invalid key type {type(key)} of key {key}')
|
|
444
447
|
if getattr(value, 'set_notifier', None) is not None:
|
|
445
448
|
value.set_notifier(self.notifier, key)
|
|
449
|
+
|
|
446
450
|
if isinstance(key, ResClassAttribute):
|
|
451
|
+
assert isinstance(key, ResClassAttribute), f"Expected ResClassAttribute type, got {type(value)}"
|
|
447
452
|
super()._change_setter(key, value)
|
|
448
453
|
if self._attributes.get(key) is None:
|
|
449
454
|
self._changeset[key] = ResourceClassPropertyChange(None, Action.CREATE, False)
|
|
@@ -455,7 +460,7 @@ class ResourceClass(Model, Notify):
|
|
|
455
460
|
if key == ResClassAttribute.SUPERCLASS: # we can only change the superclass in the instance of ResourceClass if it's not in use
|
|
456
461
|
self._test_in_use = True
|
|
457
462
|
|
|
458
|
-
elif isinstance(key, Iri): # Iri, we add a
|
|
463
|
+
elif isinstance(key, Iri): # Iri, we add a HasProrty instance
|
|
459
464
|
if self._properties.get(key) is None: # Property not set -> CREATE action
|
|
460
465
|
self._changeset[key] = ResourceClassPropertyChange(None, Action.CREATE, False)
|
|
461
466
|
if isinstance(value.prop, Iri): # we just add a reference to an existing (!) standalone property!
|
|
@@ -466,8 +471,9 @@ class ResourceClass(Model, Notify):
|
|
|
466
471
|
except OldapErrorNotFound as err:
|
|
467
472
|
self._properties[key] = value
|
|
468
473
|
else:
|
|
469
|
-
value.
|
|
470
|
-
|
|
474
|
+
if value.type == PropType.INTERNAL:
|
|
475
|
+
value.prop._internal = self._owlclass_iri # we need to access the private variable here
|
|
476
|
+
value.prop._property_class_iri = key # we need to access the private variable here
|
|
471
477
|
self._properties[key] = value
|
|
472
478
|
else: # REPLACE action
|
|
473
479
|
if self._changeset.get(key) is None:
|
|
@@ -676,6 +682,27 @@ class ResourceClass(Model, Notify):
|
|
|
676
682
|
def __query_shacl(con: IConnection,
|
|
677
683
|
project: Project,
|
|
678
684
|
owl_class_iri: Iri) -> Attributes:
|
|
685
|
+
"""
|
|
686
|
+
Executes a SPARQL query to retrieve the attributes of a given OWL class from a SHACL
|
|
687
|
+
graph using the provided connection and project context. This function processes the
|
|
688
|
+
query results and organizes attributes into a dictionary format for further usage.
|
|
689
|
+
NOTE: It reads only the attributes of a given OWL class, not the properties!
|
|
690
|
+
|
|
691
|
+
:param con: Connection instance providing methods to interact with the ontology.
|
|
692
|
+
:type con: IConnection
|
|
693
|
+
:param project: Project instance associated with the ontology and used to build the
|
|
694
|
+
query context and graph.
|
|
695
|
+
:type project: Project
|
|
696
|
+
:param owl_class_iri: The IRI of the OWL class for which attributes need to be
|
|
697
|
+
retrieved.
|
|
698
|
+
:type owl_class_iri: Iri
|
|
699
|
+
:return: A dictionary containing attributes of the OWL class. Keys are attribute IRIs
|
|
700
|
+
and values are lists of corresponding attribute values.
|
|
701
|
+
:rtype: Attributes
|
|
702
|
+
:raises OldapErrorNotFound: If the provided OWL class IRI does not correspond to any
|
|
703
|
+
resource in the SHACL graph.
|
|
704
|
+
:raises OldapError: If an inconsistent shape is found for the provided OWL class IRI.
|
|
705
|
+
"""
|
|
679
706
|
context = Context(name=con.context_name)
|
|
680
707
|
context[project.projectShortName] = project.namespaceIri
|
|
681
708
|
context.use(project.projectShortName)
|
|
@@ -704,7 +731,7 @@ class ResourceClass(Model, Notify):
|
|
|
704
731
|
if tmp_owl_class_iri != owl_class_iri:
|
|
705
732
|
raise OldapError(f'Inconsistent Shape for "{owl_class_iri}": rdf:type="{tmp_owl_class_iri}"')
|
|
706
733
|
elif attriri == 'sh:property':
|
|
707
|
-
continue # processes later – points to a BNode containing
|
|
734
|
+
continue # processes later – points to a BNode containing the property definition or to a PropertyShape...
|
|
708
735
|
else:
|
|
709
736
|
attriri = r['attriri']
|
|
710
737
|
if isinstance(r['value'], Iri):
|
|
@@ -797,30 +824,42 @@ class ResourceClass(Model, Notify):
|
|
|
797
824
|
#
|
|
798
825
|
# There may be several ways to define these properties:
|
|
799
826
|
#
|
|
800
|
-
# A.
|
|
801
|
-
#
|
|
802
|
-
#
|
|
803
|
-
#
|
|
804
|
-
#
|
|
805
|
-
#
|
|
806
|
-
#
|
|
807
|
-
#
|
|
808
|
-
#
|
|
809
|
-
#
|
|
827
|
+
# A. Standalone property where we add per-property-usage constraints
|
|
828
|
+
# HasProperty.type = HasProperty.STANDALONE
|
|
829
|
+
#
|
|
830
|
+
# sh:property <iri>Shape ,
|
|
831
|
+
# [
|
|
832
|
+
# sh:path <iri> ;
|
|
833
|
+
# sh:maxCount 1 ; # OPTIONAL
|
|
834
|
+
# ... # minCount, orer, group
|
|
835
|
+
# ] ;
|
|
836
|
+
#
|
|
837
|
+
# B: Internal property
|
|
838
|
+
#
|
|
839
|
+
# sh:property [
|
|
840
|
+
# sh:path <iri> ;
|
|
810
841
|
# dcterm:creation "..." ;
|
|
811
842
|
# ...
|
|
812
843
|
# sh:datatype: xsd:string ;
|
|
813
844
|
# ...
|
|
814
|
-
# ]
|
|
815
|
-
#
|
|
845
|
+
# ] ;
|
|
846
|
+
#
|
|
847
|
+
# C. External Property from an external ontology
|
|
848
|
+
# HasProperty.type = HasProperty.EXTERNAL
|
|
849
|
+
#
|
|
850
|
+
# sh:property [
|
|
851
|
+
# sh:path <iri> ;
|
|
852
|
+
# sh:maxCount 1 ; # OPTIONAL
|
|
853
|
+
# ... # minCount, orer, group
|
|
854
|
+
# ] ;
|
|
855
|
+
#
|
|
816
856
|
#
|
|
817
857
|
query = context.sparql_context
|
|
818
858
|
query += f"""
|
|
819
859
|
SELECT ?prop ?attriri ?value ?oo
|
|
820
860
|
FROM {graph}:shacl
|
|
821
861
|
WHERE {{
|
|
822
|
-
|
|
823
|
-
?shape sh:property ?prop .
|
|
862
|
+
{owlclass_iri.toRdf}Shape sh:property ?prop .
|
|
824
863
|
OPTIONAL {{
|
|
825
864
|
?prop ?attriri ?value .
|
|
826
865
|
OPTIONAL {{
|
|
@@ -831,96 +870,85 @@ class ResourceClass(Model, Notify):
|
|
|
831
870
|
"""
|
|
832
871
|
jsonobj = con.query(query)
|
|
833
872
|
res = QueryProcessor(context=context, query_result=jsonobj)
|
|
834
|
-
propinfos: Dict[Iri, Attributes] = {}
|
|
873
|
+
propinfos: Dict[Iri | BNode, Attributes] = {}
|
|
835
874
|
#
|
|
836
875
|
# first we run over all triples to gather the information about the properties of the possible
|
|
837
876
|
# BNode based sh:property-Shapes.
|
|
838
877
|
# NOTE: some of the nodes may actually be QNames referencing shapes defines as "standalone" sh:PropertyShape's.
|
|
839
878
|
#
|
|
840
879
|
for r in res:
|
|
841
|
-
if
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
880
|
+
if isinstance(r['prop'], Iri):
|
|
881
|
+
# we have a reference to a property shape of a standalone property: we read it
|
|
882
|
+
# and add it to the list of standalone properties if it does not exist yet
|
|
883
|
+
if str(r['prop']).endswith("Shape"):
|
|
884
|
+
refprop = Iri(str(r['prop'])[:-5], validate=False)
|
|
885
|
+
if not sa_props:
|
|
886
|
+
sa_props: dict[Iri, PropertyClass] = {}
|
|
887
|
+
if not refprop in sa_props:
|
|
888
|
+
sa_props[refprop] = PropertyClass.read(con=con, project=project, property_class_iri=refprop)
|
|
889
|
+
sa_props[refprop]._externalOntology = Xsd_boolean(True)
|
|
890
|
+
else:
|
|
891
|
+
raise OldapErrorInconsistency(f'Value "{r['prop']}" must end with "Shape".')
|
|
892
|
+
if isinstance(r['prop'], BNode):
|
|
893
|
+
# it's a blank node containing the property information
|
|
894
|
+
# if it's a new BNode, let's add the property attributes for this new property defintion
|
|
895
|
+
if r['attriri'] == 'sh:path' and r['value'] == 'rdf:type':
|
|
896
|
+
continue # TODO: get rid of the triple "BNODE sh:path sh:type !!!
|
|
897
|
+
if r['prop'] not in propinfos:
|
|
898
|
+
propinfos[r['prop']]: Attributes = {}
|
|
899
|
+
if r.get('attriri') and not isinstance(r['attriri'], Iri):
|
|
900
|
+
raise OldapError(f"There is some inconsistency in this shape! ({r['attriri']})")
|
|
901
|
+
# now let's process the triples of the property (blank) node
|
|
902
|
+
PropertyClass.process_triple(r, propinfos[r['prop']])
|
|
903
|
+
|
|
904
|
+
propinfos2 = {v["sh:path"]: v for v in propinfos.values() if "sh:path" in v}
|
|
905
|
+
|
|
856
906
|
#
|
|
857
907
|
# now we collected all the information from the triple store. Let's process the information into
|
|
858
908
|
# a list of full PropertyClasses or QName's to external definitions
|
|
859
909
|
#
|
|
860
910
|
proplist: List[HasProperty] = []
|
|
861
|
-
for prop_iri, attributes in
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
prop=prop_iri))
|
|
911
|
+
for prop_iri, attributes in propinfos2.items():
|
|
912
|
+
#
|
|
913
|
+
# Case A, standalone property
|
|
914
|
+
#
|
|
915
|
+
if sa_props and prop_iri in sa_props:
|
|
916
|
+
proplist.append(HasProperty(con=con,
|
|
917
|
+
project=project,
|
|
918
|
+
prop=sa_props[prop_iri],
|
|
919
|
+
minCount=attributes.get(Iri('sh:minCount')),
|
|
920
|
+
maxCount=attributes.get(Iri('sh:maxCount')),
|
|
921
|
+
order=attributes.get(Iri('sh:order')),
|
|
922
|
+
group=attributes.get(Iri('sh:group'))))
|
|
874
923
|
else:
|
|
875
924
|
prop = PropertyClass(con=con, project=project)
|
|
876
925
|
haspropdata = prop.parse_shacl(attributes=attributes)
|
|
877
|
-
if
|
|
926
|
+
if prop.property_class_iri.as_qname.prefix in [project.projectShortName, 'oldap', 'shared']:
|
|
878
927
|
#
|
|
879
|
-
# Case B
|
|
928
|
+
# Case B, internal property
|
|
880
929
|
#
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
else:
|
|
890
|
-
if haspropdata.refprop.is_qname:
|
|
891
|
-
if haspropdata.refprop.as_qname.prefix != project.projectShortName:
|
|
892
|
-
# TODO: the list has to come from outside! Config?? Or read from triplestore? !!!!!!!!!!
|
|
893
|
-
if haspropdata.refprop.as_qname.prefix in {'rdfs', 'dcterms', 'schema'}:
|
|
894
|
-
propproj = Project.read(con=con, projectIri_SName='oldap')
|
|
895
|
-
else:
|
|
896
|
-
propproj = Project.read(con=con, projectIri_SName=haspropdata.refprop.as_qname.prefix)
|
|
897
|
-
else:
|
|
898
|
-
propproj = project
|
|
899
|
-
else:
|
|
900
|
-
propproj = project
|
|
901
|
-
prop = PropertyClass.read(con, propproj, haspropdata.refprop)
|
|
902
|
-
prop.force_external()
|
|
903
|
-
proplist.append(HasProperty(con=con,
|
|
904
|
-
project=project,
|
|
905
|
-
prop=prop,
|
|
906
|
-
minCount=haspropdata.minCount,
|
|
907
|
-
maxCount=haspropdata.maxCount,
|
|
908
|
-
order=haspropdata.order,
|
|
909
|
-
group=haspropdata.group)) # TODO: Callback ????
|
|
930
|
+
prop._internal = owlclass_iri
|
|
931
|
+
proplist.append(HasProperty(con=con,
|
|
932
|
+
project=project,
|
|
933
|
+
prop=prop,
|
|
934
|
+
minCount=haspropdata.minCount,
|
|
935
|
+
maxCount=haspropdata.maxCount,
|
|
936
|
+
order=haspropdata.order,
|
|
937
|
+
group=haspropdata.group))
|
|
910
938
|
else:
|
|
911
939
|
#
|
|
912
|
-
# Case C
|
|
940
|
+
# Case C, external property
|
|
913
941
|
#
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
OldapErrorInconsistency(f'ERRROR ERROR ERROR')
|
|
942
|
+
# we check, if the external property is already defined somewhere in the project or the shared
|
|
943
|
+
prop._externalOntology = Xsd_boolean(True)
|
|
917
944
|
proplist.append(HasProperty(con=con,
|
|
918
945
|
project=project,
|
|
946
|
+
#prop=sa_props[prop_iri] if sa_props and prop_iri in sa_props else prop.property_class_iri,
|
|
919
947
|
prop=prop,
|
|
920
948
|
minCount=haspropdata.minCount,
|
|
921
949
|
maxCount=haspropdata.maxCount,
|
|
922
950
|
order=haspropdata.order,
|
|
923
|
-
group=haspropdata.group))
|
|
951
|
+
group=haspropdata.group))
|
|
924
952
|
return proplist
|
|
925
953
|
|
|
926
954
|
def __read_owl(self) -> None:
|
|
@@ -957,7 +985,7 @@ class ResourceClass(Model, Notify):
|
|
|
957
985
|
elif p == 'owl:qualifiedCardinality':
|
|
958
986
|
propdict[bnode_id]['min_count'] = r['o']
|
|
959
987
|
propdict[bnode_id]['max_count'] = r['o']
|
|
960
|
-
elif p == 'owl:
|
|
988
|
+
elif p == 'owl:onDataRange':
|
|
961
989
|
propdict[bnode_id]['datatype'] = r['o']
|
|
962
990
|
else:
|
|
963
991
|
print(f'ERROR ERROR ERROR: Unknown restriction property: "{p}"')
|
|
@@ -968,7 +996,8 @@ class ResourceClass(Model, Notify):
|
|
|
968
996
|
prop = [x for x in self._properties if x == property_iri]
|
|
969
997
|
if len(prop) != 1:
|
|
970
998
|
raise OldapError(f'Property "{property_iri}" of "{self._owlclass_iri}" from OWL has no SHACL definition!')
|
|
971
|
-
self._properties[prop[0]].prop.
|
|
999
|
+
if isinstance(self._properties[prop[0]].prop, PropertyClass) and not self._properties[prop[0]].prop._externalOntology:
|
|
1000
|
+
self._properties[prop[0]].prop.read_owl()
|
|
972
1001
|
#
|
|
973
1002
|
# now get all the subClassOf of other classes
|
|
974
1003
|
#
|
|
@@ -1046,15 +1075,6 @@ class ResourceClass(Model, Notify):
|
|
|
1046
1075
|
sa_props=sa_props)
|
|
1047
1076
|
resclass = cls(con=con, project=project, owlclass_iri=owl_class_iri, hasproperties=hasproperties)
|
|
1048
1077
|
resclass.update_notifier()
|
|
1049
|
-
# for hasprop in hasproperties:
|
|
1050
|
-
# if isinstance(hasprop, HasProperty): # not an Iri...
|
|
1051
|
-
# if isinstance(hasprop.prop, PropertyClass):
|
|
1052
|
-
# hasprop.prop.set_notifier(resclass.notifier, hasprop.prop.property_class_iri)
|
|
1053
|
-
# hasprop.set_notifier(resclass.hp_notifier, hasprop.prop.property_class_iri)
|
|
1054
|
-
# elif isinstance(hasprop.prop, Iri):
|
|
1055
|
-
# hasprop.set_notifier(resclass.hp_notifier, hasprop.prop)
|
|
1056
|
-
# else:
|
|
1057
|
-
# raise OldapError(f'Invalid datatype: {type(hasprop.prop).__name}')
|
|
1058
1078
|
attributes = ResourceClass.__query_shacl(con, project=project, owl_class_iri=owl_class_iri)
|
|
1059
1079
|
resclass._parse_shacl(attributes=attributes)
|
|
1060
1080
|
if not resclass.externalOntology:
|
|
@@ -1077,8 +1097,7 @@ class ResourceClass(Model, Notify):
|
|
|
1077
1097
|
sparql += f"{blank:{indent * indent_inc}}SELECT ?modified\n"
|
|
1078
1098
|
sparql += f"{blank:{indent * indent_inc}}FROM {graph}:shacl\n"
|
|
1079
1099
|
sparql += f"{blank:{indent * indent_inc}}WHERE {{\n"
|
|
1080
|
-
sparql += f'{blank:{(indent + 1) * indent_inc}}
|
|
1081
|
-
sparql += f'{blank:{(indent + 1) * indent_inc}}?res dcterms:modified ?modified .\n'
|
|
1100
|
+
sparql += f'{blank:{(indent + 1) * indent_inc}}{self._owlclass_iri}Shape dcterms:modified ?modified .\n'
|
|
1082
1101
|
sparql += f"{blank:{indent * indent_inc}}}}"
|
|
1083
1102
|
jsonobj = self.safe_query(sparql)
|
|
1084
1103
|
res = QueryProcessor(context, jsonobj)
|
|
@@ -1095,8 +1114,7 @@ class ResourceClass(Model, Notify):
|
|
|
1095
1114
|
sparql += f"{blank:{indent * indent_inc}}SELECT ?modified\n"
|
|
1096
1115
|
sparql += f"{blank:{indent * indent_inc}}FROM {graph}:onto\n"
|
|
1097
1116
|
sparql += f"{blank:{indent * indent_inc}}WHERE {{\n"
|
|
1098
|
-
sparql += f'{blank:{(indent + 1) * indent_inc}}
|
|
1099
|
-
sparql += f'{blank:{(indent + 1) * indent_inc}}?res dcterms:modified ?modified .\n'
|
|
1117
|
+
sparql += f'{blank:{(indent + 1) * indent_inc}}{self._owlclass_iri} dcterms:modified ?modified .\n'
|
|
1100
1118
|
sparql += f"{blank:{indent * indent_inc}}}}"
|
|
1101
1119
|
jsonobj = self.safe_query(sparql)
|
|
1102
1120
|
res = QueryProcessor(context, jsonobj)
|
|
@@ -1107,9 +1125,8 @@ class ResourceClass(Model, Notify):
|
|
|
1107
1125
|
def create_shacl(self, timestamp: Xsd_dateTime, indent: int = 0, indent_inc: int = 4) -> str:
|
|
1108
1126
|
blank = ''
|
|
1109
1127
|
sparql = ''
|
|
1110
|
-
sparql += f'{blank:{(indent + 1)*indent_inc}}{self._owlclass_iri}Shape a sh:NodeShape
|
|
1128
|
+
sparql += f'{blank:{(indent + 1)*indent_inc}}{self._owlclass_iri}Shape a sh:NodeShape'
|
|
1111
1129
|
sparql += f' ;\n{blank:{(indent + 2) * indent_inc}}sh:targetClass {self._owlclass_iri.toRdf}'
|
|
1112
|
-
sparql += f' ;\n{blank:{(indent + 2) * indent_inc}}schema:version {self.__version.toRdf}'
|
|
1113
1130
|
self._created = timestamp
|
|
1114
1131
|
sparql += f' ;\n{blank:{(indent + 2) * indent_inc}}dcterms:created {timestamp.toRdf}'
|
|
1115
1132
|
sparql += f' ;\n{blank:{(indent + 2) * indent_inc}}dcterms:creator {self._creator.toRdf}'
|
|
@@ -1131,35 +1148,26 @@ class ResourceClass(Model, Notify):
|
|
|
1131
1148
|
else:
|
|
1132
1149
|
sparql += f' ;\n{blank:{(indent + 2) * indent_inc}}{attr.value} {value.toRdf}'
|
|
1133
1150
|
|
|
1134
|
-
|
|
1135
|
-
sparql += f'
|
|
1136
|
-
sparql += f'\n{blank:{(indent +
|
|
1137
|
-
sparql += f' ;\n{blank:{(indent +
|
|
1151
|
+
# TODO: Check if the following is needed.
|
|
1152
|
+
sparql += f' ;\n{blank:{(indent + 2) * indent_inc}}sh:property ['
|
|
1153
|
+
sparql += f'\n{blank:{(indent + 3) * indent_inc}}sh:path rdf:type'
|
|
1154
|
+
sparql += f' ;\n{blank:{(indent + 2) * indent_inc}}]'
|
|
1138
1155
|
|
|
1139
1156
|
for iri, hp in self._properties.items():
|
|
1140
|
-
if
|
|
1141
|
-
# just a property Iri (to some foreign property in an "unknown" ontology...
|
|
1142
|
-
sparql += f' ;\n{blank:{(indent + 2) * indent_inc}}sh:property {hp.prop.toRdf}'
|
|
1143
|
-
continue
|
|
1144
|
-
if hp.prop.internal is not None:
|
|
1145
|
-
# it's an internal property
|
|
1146
|
-
sparql += f' ;\n{blank:{(indent + 2)*indent_inc}}sh:property'
|
|
1147
|
-
sparql += f'\n{blank:{(indent + 3)*indent_inc}}[\n'
|
|
1148
|
-
sparql += hp.prop.property_node_shacl(timestamp=timestamp,
|
|
1149
|
-
haspropdata=hp.haspropdata,
|
|
1150
|
-
indent=4)
|
|
1151
|
-
sparql += f' ;\n{blank:{(indent + 3) * indent_inc}}]'
|
|
1152
|
-
else:
|
|
1153
|
-
# it's an external but well known property within the ontolopgy...
|
|
1157
|
+
if hp.type == PropType.STANDALONE:
|
|
1154
1158
|
if hp.minCount or hp.maxCount or hp.order or hp.group:
|
|
1155
|
-
sparql += f' ;\n{blank:{(indent + 2) * indent_inc}}sh:property'
|
|
1156
|
-
sparql += f'\n{blank:{(indent + 3) * indent_inc}}
|
|
1157
|
-
sparql +=
|
|
1158
|
-
sparql += hp.create_shacl(indent=4)
|
|
1159
|
+
sparql += f' ;\n{blank:{(indent + 2) * indent_inc}}sh:property {iri}Shape, ['
|
|
1160
|
+
sparql += f'\n{blank:{(indent + 3) * indent_inc}}sh:path {iri.toRdf}'
|
|
1161
|
+
sparql += hp.create_shacl(indent=2)
|
|
1159
1162
|
sparql += f' ;\n{blank:{(indent + 3) * indent_inc}}]'
|
|
1160
1163
|
else:
|
|
1161
|
-
sparql += f' ;\n{blank:{(indent + 2)*indent_inc}}sh:property {iri}Shape'
|
|
1162
|
-
|
|
1164
|
+
sparql += f' ;\n{blank:{(indent + 2) * indent_inc}}sh:property {iri}Shape'
|
|
1165
|
+
else:
|
|
1166
|
+
sparql += f' ;\n{blank:{(indent + 2) * indent_inc}}sh:property ['
|
|
1167
|
+
sparql += hp.prop.property_node_shacl(timestamp=timestamp,
|
|
1168
|
+
haspropdata=hp.haspropdata,
|
|
1169
|
+
indent=3)
|
|
1170
|
+
sparql += f' ;\n{blank:{(indent + 2) * indent_inc}}]'
|
|
1163
1171
|
sparql += ' .\n'
|
|
1164
1172
|
return sparql
|
|
1165
1173
|
|
|
@@ -1169,7 +1177,7 @@ class ResourceClass(Model, Notify):
|
|
|
1169
1177
|
blank = ''
|
|
1170
1178
|
sparql = ''
|
|
1171
1179
|
for iri, hp in self._properties.items():
|
|
1172
|
-
if
|
|
1180
|
+
if hp.type == PropType.EXTERNAL:
|
|
1173
1181
|
continue
|
|
1174
1182
|
if not hp.prop.from_triplestore:
|
|
1175
1183
|
sparql += hp.prop.create_owl_part1(timestamp, indent + 2) + '\n'
|
|
@@ -1195,6 +1203,8 @@ class ResourceClass(Model, Notify):
|
|
|
1195
1203
|
sparql += f'{blank:{(indent + 3)*indent_inc}}rdfs:subClassOf {valstr}'
|
|
1196
1204
|
i = 0
|
|
1197
1205
|
for iri, hp in self._properties.items():
|
|
1206
|
+
if not (hp.minCount or hp.maxCount or self._attributes.get(PropClassAttr.DATATYPE) or self._attributes.get(PropClassAttr.CLASS)):
|
|
1207
|
+
continue
|
|
1198
1208
|
sparql += ' ,\n'
|
|
1199
1209
|
if isinstance(hp.prop, Iri):
|
|
1200
1210
|
sparql += f'{blank:{(indent + 3) * indent_inc}}[\n'
|
|
@@ -1265,7 +1275,7 @@ class ResourceClass(Model, Notify):
|
|
|
1265
1275
|
try:
|
|
1266
1276
|
self._con.transaction_update(sparql)
|
|
1267
1277
|
except OldapError:
|
|
1268
|
-
|
|
1278
|
+
print(sparql)
|
|
1269
1279
|
self._con.transaction_abort()
|
|
1270
1280
|
raise
|
|
1271
1281
|
|
|
@@ -1328,7 +1338,7 @@ class ResourceClass(Model, Notify):
|
|
|
1328
1338
|
sparql = f'INSERT DATA {{#B\n'
|
|
1329
1339
|
sparql += f' GRAPH {self._graph}:shacl {{\n'
|
|
1330
1340
|
sparql += f'{blank:{indent * indent_inc}}{self._owlclass_iri}Shape sh:property [\n'
|
|
1331
|
-
sparql += f'{blank:{(indent + 1) * indent_inc}}sh:
|
|
1341
|
+
sparql += f'{blank:{(indent + 1) * indent_inc}}sh:path {iri.toRdf}'
|
|
1332
1342
|
sparql += hasprop.create_shacl()
|
|
1333
1343
|
sparql += f' ; \n{blank:{indent * indent_inc}}] .\n'
|
|
1334
1344
|
sparql += f' }}\n'
|
|
@@ -1350,7 +1360,7 @@ class ResourceClass(Model, Notify):
|
|
|
1350
1360
|
sparql += f'{blank:{indent * indent_inc}}WHERE {{\n'
|
|
1351
1361
|
sparql += f'{blank:{(indent + 1) * indent_inc}}{owlclass_iri}Shape sh:property ?propnode .\n'
|
|
1352
1362
|
sparql += f'{blank:{(indent + 1) * indent_inc}}{{\n'
|
|
1353
|
-
sparql += f'{blank:{(indent + 2) * indent_inc}}?propnode sh:
|
|
1363
|
+
sparql += f'{blank:{(indent + 2) * indent_inc}}?propnode sh:path {propclass_iri.toRdf} .\n'
|
|
1354
1364
|
sparql += f'{blank:{(indent + 2) * indent_inc}}?propnode ?p ?v .\n'
|
|
1355
1365
|
sparql += f'{blank:{(indent + 1) * indent_inc}}}} UNION {{\n'
|
|
1356
1366
|
sparql += f'{blank:{(indent + 2) * indent_inc}}FILTER(?propnode = {propclass_iri.toRdf}Shape)\n'
|
|
@@ -1391,6 +1401,7 @@ class ResourceClass(Model, Notify):
|
|
|
1391
1401
|
# we loop over all items in the changeset of the resource
|
|
1392
1402
|
#
|
|
1393
1403
|
for item, change in self._changeset.items():
|
|
1404
|
+
item: Union[Iri, HasProperty]
|
|
1394
1405
|
if isinstance(item, ResClassAttribute): # we have just an attribute or ResourceClass
|
|
1395
1406
|
#
|
|
1396
1407
|
# Do the changes to the ResourceClass attributes
|
|
@@ -1418,12 +1429,12 @@ class ResourceClass(Model, Notify):
|
|
|
1418
1429
|
if to_be_deleted:
|
|
1419
1430
|
sparql += f'{blank:{indent * indent_inc}}DELETE {{\n'
|
|
1420
1431
|
for ov in to_be_deleted:
|
|
1421
|
-
sparql += f'{blank:{(indent + 1) * indent_inc}}?res sh:node {ov
|
|
1432
|
+
sparql += f'{blank:{(indent + 1) * indent_inc}}?res sh:node {ov}Shape .\n'
|
|
1422
1433
|
sparql += f'{blank:{indent * indent_inc}}}}\n'
|
|
1423
1434
|
if to_be_added:
|
|
1424
1435
|
sparql += f'{blank:{indent * indent_inc}}INSERT {{\n'
|
|
1425
1436
|
for nv in to_be_added:
|
|
1426
|
-
sparql += f'{blank:{(indent + 1) * indent_inc}}?res sh:node {nv
|
|
1437
|
+
sparql += f'{blank:{(indent + 1) * indent_inc}}?res sh:node {nv}Shape .\n'
|
|
1427
1438
|
sparql += f'{blank:{indent * indent_inc}}}}\n'
|
|
1428
1439
|
sparql += f'{blank:{indent * indent_inc}}WHERE {{\n'
|
|
1429
1440
|
sparql += f'{blank:{(indent + 1) * indent_inc}}BIND({self.owl_class_iri.toRdf}Shape as ?res)\n'
|
|
@@ -1440,7 +1451,7 @@ class ResourceClass(Model, Notify):
|
|
|
1440
1451
|
last_modified=self._modified)
|
|
1441
1452
|
if sparql:
|
|
1442
1453
|
sparql_list.append(sparql)
|
|
1443
|
-
elif isinstance(item, Iri):
|
|
1454
|
+
elif isinstance(item, Iri): # noinspection PyUnreachableCode
|
|
1444
1455
|
#
|
|
1445
1456
|
# Something affected the self._properties
|
|
1446
1457
|
#
|
|
@@ -1531,7 +1542,8 @@ class ResourceClass(Model, Notify):
|
|
|
1531
1542
|
timestamp=timestamp)
|
|
1532
1543
|
if sparql:
|
|
1533
1544
|
sparql_list.append(sparql)
|
|
1534
|
-
|
|
1545
|
+
else:
|
|
1546
|
+
pass
|
|
1535
1547
|
#
|
|
1536
1548
|
#######################################
|
|
1537
1549
|
#
|
|
@@ -1593,7 +1605,7 @@ class ResourceClass(Model, Notify):
|
|
|
1593
1605
|
sparql += f'{blank:{indent * indent_inc}}}}\n'
|
|
1594
1606
|
sparql += f'{blank:{indent * indent_inc}}WHERE {{\n'
|
|
1595
1607
|
sparql += f'{blank:{(indent + 1) * indent_inc}}{owlclass_iri}Shape sh:property ?propnode .\n'
|
|
1596
|
-
sparql += f'{blank:{(indent + 1) * indent_inc}}?propnode sh:
|
|
1608
|
+
sparql += f'{blank:{(indent + 1) * indent_inc}}?propnode sh:path {self.propclass_iri} .\n'
|
|
1597
1609
|
sparql += f'{blank:{(indent + 1) * indent_inc}}?propnode ?p ?v .\n'
|
|
1598
1610
|
sparql += f'{blank:{indent * indent_inc}}}}'
|
|
1599
1611
|
return sparql
|
|
@@ -1612,6 +1624,7 @@ class ResourceClass(Model, Notify):
|
|
|
1612
1624
|
# we loop over all items in the changeset of the resource
|
|
1613
1625
|
#
|
|
1614
1626
|
for item, change in self._changeset.items():
|
|
1627
|
+
item: Union[ResClassAttribute, Iri]
|
|
1615
1628
|
if isinstance(item, ResClassAttribute): # we have just an attribute or ResourceClass
|
|
1616
1629
|
#
|
|
1617
1630
|
# Do the changes to the ResourceClass attributes
|
oldaplib/src/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.
|
|
1
|
+
__version__ = "0.3.1"
|