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