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.
@@ -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, TYPE_CHECKING
7
- from unittest import case
6
+ from typing import Union, List, Dict, Callable, Self, Any, TypeVar
8
7
 
9
- from oldaplib.src.cachesingleton import CacheSingleton, CacheSingletonRedis
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.superclass:
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.superclass[iri] = sucla
179
+ self._attributes[ResClassAttribute.SUPERCLASS][iri] = sucla
181
180
  self.notify()
182
181
  else:
183
- if superclass in self.superclass:
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.superclass[iri] = sucla
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.superclass:
210
+ if scIri not in self._attributes[ResClassAttribute.SUPERCLASS]:
212
211
  raise OldapErrorValue(f'Superclass "{scIri}" not found in superclass list')
213
- del self.superclass[scIri]
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.superclass:
216
+ if superclassIri not in self._attributes[ResClassAttribute.SUPERCLASS]:
218
217
  raise OldapErrorValue(f'Superclass "{superclass}" not found in superclass list')
219
- del self.superclass[superclassIri]
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
- external_ontology: bool | Xsd_boolean = False,
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
- #Notify.__init__(self, notifier, notify_data)
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
- thing_iri = Iri('oldap:Thing', validate=False)
315
- if self._owlclass_iri != thing_iri:
316
- if not new_kwargs.get(ResClassAttribute.SUPERCLASS.value.fragment):
317
- new_kwargs[ResClassAttribute.SUPERCLASS.value.fragment] = self.assign_superclass(thing_iri)
318
- else:
319
- if not thing_iri in new_kwargs[ResClassAttribute.SUPERCLASS.value.fragment]:
320
- thing = ResourceClass.read(self._con, self._sysproject, thing_iri)
321
- new_kwargs[ResClassAttribute.SUPERCLASS.value.fragment][thing_iri] = thing
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
- hasprop.prop = fixed_prop
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 HasProperty instance
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.prop._internal = self._owlclass_iri # we need to access the private variable here
470
- value.prop._property_class_iri = key # we need to access the private variable here
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. sh:property <iri> ;
801
- # Reference to an external property without any additional information. The property may be a foreign
802
- # property (e.g. cidoc:E5_Event) or a standalone property within the given datamodel
803
- # B. sh:property [
804
- # sh:node <iri> ;
805
- # sh:maxCount "1"^^xsd:integer
806
- # ]
807
- # Reference to a foreign or standalone property with additional information (sh:minCount, sh:maxCount,
808
- # sh:order, sh:group)
809
- # C. sh:property [
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
- # An internal property local to this resource. May not be reused for other resources!
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
- BIND({owlclass_iri}Shape AS ?shape)
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 r.get('value') and isinstance(r['value'], Iri) and r['value'] == 'rdf:type':
842
- continue
843
- if r.get('attriri') and not isinstance(r['attriri'], Iri):
844
- raise OldapError(f"There is some inconsistency in this shape! ({r['attriri']})")
845
- propnode = r['prop'] # Iri (case A. above) or a BNode (case B. and C. above)
846
- prop: PropertyClass | Iri
847
- if isinstance(propnode, Iri):
848
- qname = propnode
849
- propinfos[qname] = propnode
850
- elif isinstance(propnode, BNode):
851
- if propinfos.get(propnode) is None:
852
- propinfos[propnode]: Attributes = {}
853
- PropertyClass.process_triple(r, propinfos[propnode])
854
- else:
855
- raise OldapError(f'Unexpected type for propnode in SHACL. Type = "{type(propnode)}".')
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 propinfos.items():
862
- if isinstance(attributes, Iri):
863
- #
864
- # Case A.: sh:property <iri> ;
865
- #
866
- if sa_props and prop_iri in sa_props:
867
- proplist.append(HasProperty(con=con,
868
- project=project,
869
- prop=sa_props[prop_iri]))
870
- else:
871
- proplist.append(HasProperty(con=con,
872
- project=project,
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 haspropdata.refprop:
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
- if sa_props and haspropdata.refprop in sa_props:
882
- proplist.append(HasProperty(con=con,
883
- project=project,
884
- prop=sa_props[haspropdata.refprop],
885
- minCount=haspropdata.minCount,
886
- maxCount=haspropdata.maxCount,
887
- order=haspropdata.order,
888
- group=haspropdata.group)) # TODO: Callback ????
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
- prop.read_owl()
915
- if prop._internal != owlclass_iri:
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)) # TODO: Callback ????
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:onDatatype':
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.read_owl()
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}}BIND({self._owlclass_iri}Shape as ?res)\n'
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}}BIND({self._owlclass_iri} as ?res)\n'
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, {self._owlclass_iri.toRdf}'
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
- sparql += f' ;\n{blank:{(indent + 2) * indent_inc}}sh:property'
1135
- sparql += f'\n{blank:{(indent + 3) * indent_inc}}['
1136
- sparql += f'\n{blank:{(indent + 4) * indent_inc}}sh:path rdf:type'
1137
- sparql += f' ;\n{blank:{(indent + 3) * indent_inc}}]'
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 isinstance(hp.prop, Iri):
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 += f'\n{blank:{(indent + 4) * indent_inc}}sh:node {iri}Shape'
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
- #if len(self._properties) > 0:
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 isinstance(hp.prop, Iri):
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
- lprint(sparql)
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:node {iri.toRdf}'
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:node {propclass_iri.toRdf}Shape .\n'
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.toRdf}Shape .\n'
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.toRdf}Shape .\n'
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): # Something affected the self._properties
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:node {self.propclass_iri}Shape .\n'
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.2.11"
1
+ __version__ = "0.3.1"