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.
Files changed (41) hide show
  1. oldaplib/ontologies/admin-testing.trig +2 -4
  2. oldaplib/ontologies/admin.trig +2 -4
  3. oldaplib/ontologies/oldap.trig +221 -131
  4. oldaplib/ontologies/shared.trig +0 -3
  5. oldaplib/src/datamodel.py +109 -17
  6. oldaplib/src/dtypes/namespaceiri.py +16 -1
  7. oldaplib/src/enums/externalontologyattr.py +22 -0
  8. oldaplib/src/enums/owlpropertytype.py +10 -0
  9. oldaplib/src/enums/projectattr.py +0 -1
  10. oldaplib/src/enums/propertyclassattr.py +3 -1
  11. oldaplib/src/externalontology.py +554 -0
  12. oldaplib/src/hasproperty.py +5 -0
  13. oldaplib/src/helpers/context.py +4 -4
  14. oldaplib/src/helpers/langstring.py +11 -2
  15. oldaplib/src/helpers/observable_dict.py +4 -1
  16. oldaplib/src/helpers/observable_set.py +135 -80
  17. oldaplib/src/helpers/query_processor.py +3 -0
  18. oldaplib/src/model.py +1 -1
  19. oldaplib/src/oldaplist.py +2 -2
  20. oldaplib/src/oldaplistnode.py +2 -2
  21. oldaplib/src/permissionset.py +3 -3
  22. oldaplib/src/project.py +47 -113
  23. oldaplib/src/propertyclass.py +64 -24
  24. oldaplib/src/resourceclass.py +8 -6
  25. oldaplib/src/version.py +1 -1
  26. oldaplib/src/xsd/iri.py +3 -0
  27. oldaplib/src/xsd/xsd_anyuri.py +5 -5
  28. oldaplib/src/xsd/xsd_qname.py +5 -2
  29. oldaplib/test/test_context.py +0 -4
  30. oldaplib/test/test_datamodel.py +50 -1
  31. oldaplib/test/test_dtypes.py +3 -2
  32. oldaplib/test/test_externalontologies.py +175 -0
  33. oldaplib/test/test_project.py +31 -76
  34. oldaplib/test/test_propertyclass.py +93 -6
  35. oldaplib/test/test_resourceclass.py +10 -10
  36. oldaplib/testdata/connection_test.trig +29 -12
  37. oldaplib/testdata/datamodel_test.trig +1 -1
  38. oldaplib/testdata/objectfactory_test.trig +2 -1
  39. {oldaplib-0.3.2.dist-info → oldaplib-0.3.4.dist-info}/METADATA +1 -1
  40. {oldaplib-0.3.2.dist-info → oldaplib-0.3.4.dist-info}/RECORD +41 -38
  41. {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', 'usesExternalOntology')
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.changeset_clear()
354
+ label.clear_changeset()
389
355
  label.set_notifier(cls.notifier, Xsd_QName(ProjectAttr.LABEL.value))
390
356
  if comment:
391
- comment.changeset_clear()
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
- 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}'
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
- jsonobj = self.safe_query(sparql1)
535
- res = QueryProcessor(context, jsonobj)
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 projectIri "{self.projectIri}" already exists')
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
- 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
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'
@@ -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] = OwlPropertyType.StatementProperty
317
- else:
318
- if self._attributes.get(PropClassAttr.CLASS) is not None:
319
- self._attributes[PropClassAttr.TYPE] = OwlPropertyType.OwlObjectProperty
320
- if self._attributes.get(PropClassAttr.DATATYPE) is not None:
321
- raise OldapError(f'Datatype "{self._attributes.get(PropClassAttr.DATATYPE)}" not possible for OwlObjectProperty')
322
- elif self._attributes.get(PropClassAttr.DATATYPE) is not None:
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] = OwlPropertyType.OwlObjectProperty
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
- self._attributes[PropClassAttr.TYPE] = OwlPropertyType.OwlDataProperty
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] = OwlPropertyType.OwlDataProperty
882
+ self._attributes[PropClassAttr.TYPE].add(OwlPropertyType.OwlDataProperty)
873
883
  elif obj == 'owl:ObjectProperty':
874
- self._attributes[PropClassAttr.TYPE] = OwlPropertyType.OwlObjectProperty
884
+ self._attributes[PropClassAttr.TYPE].add(OwlPropertyType.OwlObjectProperty)
875
885
  elif obj == 'rdf:Property':
876
- self._attributes[PropClassAttr.TYPE] = OwlPropertyType.StatementProperty
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) != OwlPropertyType.StatementProperty:
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) == OwlPropertyType.StatementProperty:
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] == OwlPropertyType.OwlDataProperty:
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] == OwlPropertyType.OwlObjectProperty:
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
- cache.set(property.property_class_iri, property)
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].value}'
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) == OwlPropertyType.OwlDataProperty:
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) == OwlPropertyType.OwlObjectProperty:
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] == OwlPropertyType.OwlDataProperty:
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] == OwlPropertyType.OwlObjectProperty:
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].changeset_clear()
1617
+ self._attributes[prop].clear_changeset()
1578
1618
  self._changeset = {}
1579
1619
  cache = CacheSingletonRedis()
1580
1620
  cache.set(self._property_class_iri, self)
@@ -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 owlclass_iri.prefix != "oldap":
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 changeset_clear(self) -> None:
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.changeset_clear()
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.changeset_clear()
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.changeset_clear()
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.changeset_clear()
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.2"
1
+ __version__ = "0.3.4"
oldaplib/src/xsd/iri.py CHANGED
@@ -169,6 +169,9 @@ class Iri(Xsd):
169
169
  """
170
170
  return hash(self.__value)
171
171
 
172
+ def startswith(self, value, start=None, end=None):
173
+ return self.__value.startswith(value, start, end)
174
+
172
175
  @property
173
176
  def toRdf(self) -> str:
174
177
  """
@@ -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:
@@ -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
- fragment = Xsd_NCName(fragment, validate=validate)
62
- self._value = f'{prefix}:{fragment}'
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
  """
@@ -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#> .