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/datamodel.py CHANGED
@@ -4,7 +4,9 @@ from datetime import datetime
4
4
  from typing import Dict, List, Optional, Union, Any, Self
5
5
 
6
6
  from oldaplib.src.cachesingleton import CacheSingleton, CacheSingletonRedis
7
+ from oldaplib.src.dtypes.namespaceiri import NamespaceIRI
7
8
  from oldaplib.src.enums.adminpermissions import AdminPermission
9
+ from oldaplib.src.externalontology import ExternalOntology
8
10
  from oldaplib.src.helpers.context import Context
9
11
  from oldaplib.src.enums.action import Action
10
12
  from oldaplib.src.helpers.irincname import IriOrNCName
@@ -23,6 +25,10 @@ from oldaplib.src.propertyclass import PropertyClass
23
25
  from oldaplib.src.resourceclass import ResourceClass
24
26
  from oldaplib.src.xsd.xsd_qname import Xsd_QName
25
27
 
28
+ @dataclass
29
+ class ExternalOntologyChange:
30
+ old_value: ExternalOntology | None
31
+ action: Action
26
32
 
27
33
  @dataclass
28
34
  class ResourceClassChange:
@@ -71,8 +77,10 @@ class DataModel(Model):
71
77
  _project: Project
72
78
  __context: Context
73
79
  __version: SemanticVersion
80
+ __extontos: dict[Xsd_QName, ExternalOntology]
74
81
  __propclasses: dict[Xsd_QName, PropertyClass | None]
75
82
  __resclasses: dict[Xsd_QName, ResourceClass | None]
83
+ __extontos_changeset: dict[Xsd_QName, ExternalOntologyChange]
76
84
  __resclasses_changeset: dict[Xsd_QName, ResourceClassChange]
77
85
  __propclasses_changeset: dict[Xsd_QName, PropertyClassChange]
78
86
 
@@ -83,6 +91,7 @@ class DataModel(Model):
83
91
  contributor: Iri | None = None,
84
92
  modified: Xsd_dateTime | datetime | str | None = None,
85
93
  project: Project | Iri | Xsd_NCName | str,
94
+ extontos: list[ExternalOntology] | None = None,
86
95
  propclasses: list[PropertyClass] | None = None,
87
96
  resclasses: list[ResourceClass] | None = None,
88
97
  validate: bool = False) -> None:
@@ -152,6 +161,10 @@ class DataModel(Model):
152
161
  self.__context.use(self._project.projectShortName)
153
162
  self.__graph = self._project.projectShortName
154
163
 
164
+ self.__extontos = {}
165
+ if extontos is not None:
166
+ for e in extontos:
167
+ self.__extontos[e.extonto_qname] = e
155
168
  self.__propclasses = {}
156
169
  if propclasses is not None:
157
170
  for p in propclasses:
@@ -160,12 +173,14 @@ class DataModel(Model):
160
173
  if resclasses is not None:
161
174
  for r in resclasses:
162
175
  self.__resclasses[r.owl_class_iri] = r
176
+ self.__extontos_changeset = {}
163
177
  self.__propclasses_changeset = {}
164
178
  self.__resclasses_changeset = {}
165
179
 
166
180
  def _as_dict(self):
167
181
  return {x.fragment: y for x, y in self._attributes.items()} | super()._as_dict() | {
168
182
  'project': self._project.projectShortName,
183
+ **({'extontos': [x for x in self.__extontos.values()]} if self.__extontos else {}),
169
184
  **({'propclasses': [x for x in self.__propclasses.values()]} if self.__propclasses else {}),
170
185
  **({'resclasses': [x for x in self.__resclasses.values()]} if self.__resclasses else {}),
171
186
  }
@@ -213,13 +228,16 @@ class DataModel(Model):
213
228
  instance.__context = deepcopy(self.__context, memo)
214
229
  instance.__version = deepcopy(self.__version, memo)
215
230
  instance._project = deepcopy(self._project, memo)
231
+ instance.__extontos = deepcopy(self.__extontos, memo)
216
232
  instance.__propclasses = deepcopy(self.__propclasses, memo)
217
233
  instance.__resclasses = deepcopy(self.__resclasses, memo)
218
234
  instance.__resclasses_changeset = deepcopy(self.__resclasses_changeset, memo)
219
235
  instance.__propclasses_changeset = deepcopy(self.__propclasses_changeset, memo)
220
236
  return instance
221
237
 
222
- def __getitem__(self, key: Xsd_QName) -> PropertyClass | ResourceClass:
238
+ def __getitem__(self, key: Xsd_QName) -> ExternalOntology | PropertyClass | ResourceClass:
239
+ if key in self.__extontos:
240
+ return self.__extontos[key]
223
241
  if key in self.__resclasses:
224
242
  return self.__resclasses[key]
225
243
  if key in self.__propclasses:
@@ -227,8 +245,14 @@ class DataModel(Model):
227
245
  else:
228
246
  raise KeyError(key)
229
247
 
230
- def __setitem__(self, key: Xsd_QName, value: PropertyClass | ResourceClass) -> None:
231
- if isinstance(value, PropertyClass):
248
+ def __setitem__(self, key: Xsd_QName, value: ExternalOntology | PropertyClass | ResourceClass) -> None:
249
+ if isinstance(value, ExternalOntology):
250
+ if self.__extontos.get(key) is None:
251
+ self.__extontos_changeset[key] = ExternalOntologyChange(None, Action.CREATE)
252
+ else:
253
+ raise OldapErrorAlreadyExists(f'The external ontology "{key}" already exists. It cannot be replaced. Update/delete it.')
254
+ self.__extontos[key] = value
255
+ elif isinstance(value, PropertyClass):
232
256
  if self.__propclasses.get(key) is None:
233
257
  self.__propclasses_changeset[key] = PropertyClassChange(None, Action.CREATE)
234
258
  else:
@@ -246,7 +270,10 @@ class DataModel(Model):
246
270
  def __delitem__(self, key: Xsd_QName | str) -> None:
247
271
  if not isinstance(key, Xsd_QName):
248
272
  key = Xsd_QName(key, validate=True)
249
- if key in self.__propclasses:
273
+ if key in self.__extontos:
274
+ self.__extontos_changeset[key] = ExternalOntologyChange(self.__extontos[key], Action.DELETE)
275
+ del self.__extontos[key]
276
+ elif key in self.__propclasses:
250
277
  self.__propclasses_changeset[key] = PropertyClassChange(self.__propclasses[key], Action.DELETE)
251
278
  del self.__propclasses[key]
252
279
  elif key in self.__resclasses:
@@ -255,7 +282,7 @@ class DataModel(Model):
255
282
  else:
256
283
  raise OldapErrorValue(f'"{key}" must be either PropertyClass or ResourceClass')
257
284
 
258
- def get(self, key: Xsd_QName | str) -> PropertyClass | ResourceClass | None:
285
+ def get(self, key: Xsd_QName | str) -> ExternalOntology | PropertyClass | ResourceClass | None:
259
286
  """
260
287
  Retrieves an instance of `PropertyClass` or `ResourceClass` associated with the
261
288
  specified `key`. The `key` can be either an `Iri` instance or a string. If the
@@ -272,13 +299,27 @@ class DataModel(Model):
272
299
  """
273
300
  if not isinstance(key, Xsd_QName):
274
301
  key = Xsd_QName(key, validate=True)
275
- if key in self.__propclasses:
302
+ if key in self.__extontos:
303
+ return self.__extontos[key]
304
+ elif key in self.__propclasses:
276
305
  return self.__propclasses[key]
277
306
  elif key in self.__resclasses:
278
307
  return self.__resclasses[key]
279
308
  else:
280
309
  return None
281
310
 
311
+ def get_extontos(self) -> list[Xsd_QName]:
312
+ """
313
+ Extract and return the list of extended ontologies.
314
+
315
+ This method retrieves the extended ontologies by iterating over an internal
316
+ attribute and returning the values.
317
+
318
+ :return: A list containing the extended ontologies.
319
+ :rtype: list[Xsd_QName]
320
+ """
321
+ return [x for x in self.__extontos]
322
+
282
323
  def get_propclasses(self) -> list[Xsd_QName]:
283
324
  """
284
325
  Get a list of the IRIs of the standalone property classes.
@@ -304,22 +345,43 @@ class DataModel(Model):
304
345
  return [x for x in self.__resclasses]
305
346
 
306
347
  @property
307
- def changeset(self) -> dict[Xsd_QName, PropertyClassChange | ResourceClassChange]:
308
- return self.__resclasses_changeset | self.__propclasses_changeset
348
+ def context(self) -> Context:
349
+ return self.__context
350
+
351
+ @property
352
+ def changeset(self) -> dict[Xsd_QName, ExternalOntologyChange | PropertyClassChange | ResourceClassChange]:
353
+ return self.__extontos_changeset | self.__resclasses_changeset | self.__propclasses_changeset
354
+
355
+ def clear_changeset(self) -> None:
356
+ """
357
+ Clears all recorded changes from the current changeset. The function iterates through
358
+ various internal tracking structures and resets or clears their state as needed. This
359
+ ensures that the current changeset reflects no modifications and all marked changes
360
+ are reverted or cleared.
309
361
 
310
- def changeset_clear(self) -> None:
362
+ Raises:
363
+ No explicit errors are raised by this function
364
+
365
+ Returns:
366
+ None
367
+ """
368
+ for onto, change in self.__extontos_changeset.items():
369
+ if change.action == Action.MODIFY:
370
+ self.__extontos[onto].clear_changeset()
311
371
  for prop, change in self.__propclasses_changeset.items():
312
372
  if change.action == Action.MODIFY:
313
373
  self.__propclasses[prop].clear_changeset()
314
374
  self.__propclasses_changeset = {}
315
375
  for res, change in self.__resclasses_changeset.items():
316
376
  if change.action == Action.MODIFY:
317
- self.__resclasses[res].changeset_clear()
377
+ self.__resclasses[res].clear_changeset()
318
378
  self.__resclasses_changeset = {}
319
- self.clear_changeset()
379
+ self._changeset = {}
320
380
 
321
381
  def notifier(self, what: Xsd_QName) -> None:
322
- if what in self.__propclasses:
382
+ if what in self.__extontos:
383
+ self.__extontos_changeset[what] = ExternalOntologyChange(None, Action.MODIFY)
384
+ elif what in self.__propclasses:
323
385
  self.__propclasses_changeset[what] = PropertyClassChange(None, Action.MODIFY)
324
386
  elif what in self.__resclasses:
325
387
  self.__resclasses_changeset[what] = ResourceClassChange(None, Action.MODIFY)
@@ -370,9 +432,10 @@ class DataModel(Model):
370
432
  query = cls.__context.sparql_context
371
433
  query += f"""
372
434
  SELECT ?version
373
- FROM {cls.__graph}:shacl
374
435
  WHERE {{
375
- {cls.__graph}:shapes schema:version ?version .
436
+ GRAPH {cls.__graph}:shacl {{
437
+ {cls.__graph}:shapes schema:version ?version .
438
+ }}
376
439
  }}
377
440
  """
378
441
  jsonobj = con.query(query)
@@ -380,6 +443,7 @@ class DataModel(Model):
380
443
  if len(res) == 0:
381
444
  raise OldapErrorNotFound(f'Datamodel "{cls.__graph}:shacl" not found')
382
445
  cls.__version = SemanticVersion.fromString(res[0]['version'])
446
+
383
447
  #
384
448
  # now we read the OWL ontology metadata
385
449
  #
@@ -399,6 +463,13 @@ class DataModel(Model):
399
463
  if version != cls.__version:
400
464
  raise OldapErrorInconsistency(f'Versionnumber of SHACL ({cls.__version}) and OWL ({version}) do not match')
401
465
  #
466
+ # now read the external ontologies that are used in this datamodel
467
+ #
468
+ extontos = ExternalOntology.search(con=con, projectShortName=project.projectShortName)
469
+ for onto in extontos:
470
+ cls.__context[onto.prefix] = NamespaceIRI(str(onto.namespaceIri))
471
+
472
+ #
402
473
  # now get the QNames of all standalone properties within the data model
403
474
  #
404
475
  query = cls.__context.sparql_context
@@ -425,6 +496,7 @@ class DataModel(Model):
425
496
  propclass.force_external()
426
497
  propclasses.append(propclass)
427
498
  sa_props = {x.property_class_iri: x for x in propclasses}
499
+
428
500
  #
429
501
  # now get all resources defined in the data model
430
502
  #
@@ -456,7 +528,9 @@ class DataModel(Model):
456
528
  print(f'Error reading resource class {resclassiri}: {er}')
457
529
 
458
530
  resclasses.append(resclass)
459
- instance = cls(project=project, con=con, propclasses=propclasses, resclasses=resclasses)
531
+ instance = cls(project=project, con=con, propclasses=propclasses, resclasses=resclasses, extontos=extontos)
532
+ for qname in instance.get_extontos():
533
+ instance[qname].set_notifier(instance.notifier, qname)
460
534
  for qname in instance.get_propclasses():
461
535
  instance[qname].set_notifier(instance.notifier, qname)
462
536
  for qname in instance.get_resclasses():
@@ -464,7 +538,7 @@ class DataModel(Model):
464
538
 
465
539
  cache.set(Xsd_QName(project.projectShortName, 'shacl'), instance)
466
540
 
467
- instance.changeset_clear()
541
+ instance.clear_changeset()
468
542
  return instance
469
543
 
470
544
  def create(self, indent: int = 0, indent_inc: int = 4) -> None:
@@ -511,6 +585,10 @@ class DataModel(Model):
511
585
  sparql += f'{blank:{(indent + 2) * indent_inc}}{self.__graph}:shapes schema:version {self.__version.toRdf} .\n'
512
586
  sparql += '\n'
513
587
 
588
+ for qname, onto in self.__extontos.items():
589
+ sparql += onto.create_shacl(timestamp=timestamp, indent=2)
590
+ sparql += '\n'
591
+
514
592
  for propiri, propclass in self.__propclasses.items():
515
593
  if propclass.internal:
516
594
  raise OldapErrorInconsistency(f"Property class {propclass.property_class_iri} is internal and cannot be used here.")
@@ -531,6 +609,8 @@ class DataModel(Model):
531
609
  sparql += f'{blank:{(indent + 2) * indent_inc}}owl:versionIRI <http://oldap.org/ontology/{self.__graph}/version/{str(self.__version)}> .\n'
532
610
  sparql += '\n'
533
611
 
612
+ # no OWL for ExternalOntologies
613
+
534
614
  for propiri, propclass in self.__propclasses.items():
535
615
  sparql += propclass.create_owl_part1(timestamp=timestamp, indent=2)
536
616
 
@@ -575,6 +655,15 @@ class DataModel(Model):
575
655
  if not result:
576
656
  raise OldapErrorNoPermission(message)
577
657
 
658
+ for qname, change in self.__extontos_changeset.items():
659
+ match(change.action):
660
+ case Action.CREATE:
661
+ self.__extontos[qname].create()
662
+ case Action.MODIFY:
663
+ self.__extontos[qname].update()
664
+ case Action.DELETE:
665
+ change.old_value.delete()
666
+
578
667
  for qname, change in self.__propclasses_changeset.items():
579
668
  match(change.action):
580
669
  case Action.CREATE:
@@ -584,6 +673,7 @@ class DataModel(Model):
584
673
  case Action.DELETE:
585
674
  #self.__propclasses[qname].delete()
586
675
  change.old_value.delete()
676
+
587
677
  for qname, change in self.__resclasses_changeset.items():
588
678
  match (change.action):
589
679
  case Action.CREATE:
@@ -593,7 +683,7 @@ class DataModel(Model):
593
683
  case Action.DELETE:
594
684
  #self.__resclasses[qname].delete()
595
685
  change.old_value.delete()
596
- self.changeset_clear()
686
+ self.clear_changeset()
597
687
  cache = CacheSingletonRedis()
598
688
  cache.delete(Xsd_QName(self._project.projectShortName, 'shacl'))
599
689
  #cache.set(Xsd_QName(self._project.projectShortName, 'shacl'), self)
@@ -665,6 +755,8 @@ class DataModel(Model):
665
755
  f.write(f'\n{blank:{indent * indent_inc}}{self.__graph}:shacl {{\n')
666
756
  f.write(f'{blank:{(indent + 2) * indent_inc}}{self.__graph}:shapes schema:version {self.__version.toRdf} .\n')
667
757
  f.write('\n')
758
+ for qname, onto in self.__extontos.items():
759
+ f.write(onto.create_shacl(timestamp=timestamp, indent=1))
668
760
  for iri, prop in self.__propclasses.items():
669
761
  if not prop.internal:
670
762
  f.write(prop.create_shacl(timestamp=timestamp, indent=1))
@@ -48,7 +48,12 @@ class NamespaceIRI(Xsd_anyURI):
48
48
  def __add__(self, other: str) -> Xsd_anyURI:
49
49
  return Xsd_anyURI(self._value + other)
50
50
 
51
- def expand(self, name: Xsd_NCName):
51
+
52
+ @property
53
+ def toRdf(self) -> str:
54
+ return f'<{self._value}>'
55
+
56
+ def expand(self, name: Xsd_NCName | str):
52
57
  """
53
58
  Expands the current namespace URI by appending a name to it, separated by a '/'
54
59
  and followed by a '#'. The expanded URI is then returned as a new NamespaceIRI
@@ -58,5 +63,15 @@ class NamespaceIRI(Xsd_anyURI):
58
63
  the current namespace's URI.
59
64
  :return: A new NamespaceIRI object with the expanded URI.
60
65
  """
66
+ if not isinstance(name, Xsd_NCName):
67
+ name = Xsd_NCName(name)
61
68
  return NamespaceIRI(self.value[:-1] + '/' + name.value + '#')
62
69
 
70
+
71
+ if __name__ == '__main__':
72
+ ns = NamespaceIRI('http://example.com/ns/')
73
+ print(ns)
74
+ print(ns + 'foo')
75
+ print(ns.expand('foo'))
76
+ print(ns.toRdf)
77
+
@@ -0,0 +1,22 @@
1
+ from enum import unique
2
+
3
+ from oldaplib.src.dtypes.namespaceiri import NamespaceIRI
4
+ from oldaplib.src.enums.attributeclass import AttributeClass
5
+ from oldaplib.src.helpers.langstring import LangString
6
+ from oldaplib.src.xsd.xsd_ncname import Xsd_NCName
7
+ from oldaplib.src.xsd.iri import Iri
8
+
9
+
10
+ @unique
11
+ class ExternalOntologyAttr(AttributeClass):
12
+ """Enumeration of attributes from external ontologies.
13
+
14
+ Attributes are defined as tuples with the following structure:
15
+ (QName, mandatory, immutable, datatype)
16
+ """
17
+ # order: (QName, mandatory, immutable, datatype)
18
+ PREFIX = ('oldap:prefix', True, False, Xsd_NCName)
19
+ NAMESPACE_IRI = ('oldap:namespaceIri', True, True, NamespaceIRI)
20
+ LABEL = ('rdfs:label', False, False, LangString)
21
+ COMMENT = ('rdfs:comment', False, False, LangString)
22
+
@@ -13,6 +13,16 @@ class OwlPropertyType(Enum):
13
13
  OwlDataProperty = 'owl:DatatypeProperty'
14
14
  OwlObjectProperty = 'owl:ObjectProperty'
15
15
  StatementProperty = 'rdf:Property'
16
+ TransitiveProperty = 'owl:TransitiveProperty'
17
+ SymmetricProperty = 'owl:SymmetricProperty'
18
+ ReflexiveProperty = 'owl:ReflexiveProperty'
19
+ IrreflexiveProperty = 'owl:IrreflexiveProperty'
20
+ FunctionalProperty = 'owl:FunctionalProperty'
21
+ InverseFunctionalProperty = 'owl:InverseFunctionalProperty'
22
+ # InverseOfProperty = 'owl:InverseOfProperty'
23
+ # EquivalentProperty = 'owl:EquivalentProperty'
24
+ # _owl_inverseOf: Xsd_QName # :hasParent owl:inverseOf :hasChild .
25
+ # _owl_equivalent: Xsd_QName # :hasSpouse owl:equivalentProperty :marriedTo .
16
26
 
17
27
  @property
18
28
  def toRdf(self):
@@ -24,4 +24,3 @@ class ProjectAttr(AttributeClass):
24
24
  NAMESPACE_IRI = ('oldap:namespaceIri', True, True, NamespaceIRI)
25
25
  PROJECT_START = ('oldap:projectStart', False, False, Xsd_date)
26
26
  PROJECT_END = ('oldap:projectEnd', False, False, Xsd_date)
27
- USES_EXTERNAL_ONTOLOGY = ('oldap:usesExternalOntology', False, False, ObservableDict)
@@ -8,6 +8,7 @@ from oldaplib.src.enums.xsd_datatypes import XsdDatatypes
8
8
  from oldaplib.src.helpers.langstring import LangString
9
9
  from oldaplib.src.helpers.numeric import Numeric
10
10
  from oldaplib.src.enums.owlpropertytype import OwlPropertyType
11
+ from oldaplib.src.helpers.observable_set import ObservableSet
11
12
  from oldaplib.src.xsd.iri import Iri
12
13
  from oldaplib.src.xsd.xsd_boolean import Xsd_boolean
13
14
  from oldaplib.src.xsd.xsd_integer import Xsd_integer
@@ -36,8 +37,9 @@ class PropClassAttr(AttributeClass):
36
37
  """
37
38
  # order: (QName, mandatory, immutable, datatype)
38
39
  SUBPROPERTY_OF = ('rdfs:subPropertyOf', False, False, Iri)
39
- TYPE = ('rdf:type', False, True, OwlPropertyType)
40
+ TYPE = ('rdf:type', False, False, ObservableSet)
40
41
  CLASS = ('sh:class', False, False, Iri)
42
+ NODEKIND = ('sh:nodeKind', False, False, Iri)
41
43
  DATATYPE = ('sh:datatype', False, False, XsdDatatypes)
42
44
  NAME = ('sh:name', False, False, LangString) # needs notifier
43
45
  DESCRIPTION = ('sh:description', False, False, LangString) # needs notifier