oldaplib 0.4.5__tar.gz → 0.4.6__tar.gz

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 (169) hide show
  1. {oldaplib-0.4.5 → oldaplib-0.4.6}/PKG-INFO +1 -1
  2. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/enums/language.py +5 -0
  3. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/model.py +4 -1
  4. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/objectfactory.py +154 -150
  5. oldaplib-0.4.6/oldaplib/src/version.py +1 -0
  6. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/test/test_objectfactory.py +45 -6
  7. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/testdata/instances_test.trig +26 -1
  8. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/testdata/objectfactory_test.trig +11 -0
  9. {oldaplib-0.4.5 → oldaplib-0.4.6}/pyproject.toml +1 -1
  10. oldaplib-0.4.5/oldaplib/src/version.py +0 -1
  11. {oldaplib-0.4.5 → oldaplib-0.4.6}/README.md +0 -0
  12. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/__init__.py +0 -0
  13. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/ontologies/admin-testing.trig +0 -0
  14. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/ontologies/admin.trig +0 -0
  15. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/ontologies/example.trig +0 -0
  16. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/ontologies/oldap.trig +0 -0
  17. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/ontologies/oldap.ttl +0 -0
  18. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/ontologies/shared.trig +0 -0
  19. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/ontologies/standard/.gitsave +0 -0
  20. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/ontologies/standard/dcterms.ttl +0 -0
  21. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/ontologies/standard/schemaorg.ttl +0 -0
  22. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/ontologies/standard/skos.ttl +0 -0
  23. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/ontologies/standard/skos.xml +0 -0
  24. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/__init__.py +0 -0
  25. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/cachesingleton.py +0 -0
  26. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/connection.py +0 -0
  27. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/datamodel.py +0 -0
  28. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/dtypes/__init__.py +0 -0
  29. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/dtypes/bnode.py +0 -0
  30. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/dtypes/languagein.py +0 -0
  31. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/dtypes/namespaceiri.py +0 -0
  32. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/dtypes/rdfset.py +0 -0
  33. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/dtypes/xsdset.py +0 -0
  34. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/enums/__init__.py +0 -0
  35. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/enums/action.py +0 -0
  36. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/enums/adminpermissions.py +0 -0
  37. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/enums/attributeclass.py +0 -0
  38. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/enums/datapermissions.py +0 -0
  39. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/enums/editor.py +0 -0
  40. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/enums/externalontologyattr.py +0 -0
  41. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/enums/haspropertyattr.py +0 -0
  42. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/enums/oldaplistattr.py +0 -0
  43. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/enums/oldaplistnodeattr.py +0 -0
  44. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/enums/owlpropertytype.py +0 -0
  45. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/enums/projectattr.py +0 -0
  46. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/enums/propertyclassattr.py +0 -0
  47. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/enums/resourceclassattr.py +0 -0
  48. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/enums/roleattr.py +0 -0
  49. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/enums/sparql_result_format.py +0 -0
  50. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/enums/userattr.py +0 -0
  51. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/enums/xsd_datatypes.py +0 -0
  52. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/externalontology.py +0 -0
  53. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/globalconfig.py +0 -0
  54. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/hasproperty.py +0 -0
  55. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/helpers/Notify.py +0 -0
  56. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/helpers/__init__.py +0 -0
  57. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/helpers/attributechange.py +0 -0
  58. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/helpers/context.py +0 -0
  59. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/helpers/convert2datatype.py +0 -0
  60. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/helpers/irincname.py +0 -0
  61. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/helpers/json_encoder.py +0 -0
  62. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/helpers/langstring.py +0 -0
  63. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/helpers/numeric.py +0 -0
  64. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/helpers/observable_dict.py +0 -0
  65. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/helpers/observable_set.py +0 -0
  66. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/helpers/oldaperror.py +0 -0
  67. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/helpers/query_processor.py +0 -0
  68. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/helpers/semantic_version.py +0 -0
  69. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/helpers/serializeableset.py +0 -0
  70. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/helpers/serializer.py +0 -0
  71. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/helpers/singletonmeta.py +0 -0
  72. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/helpers/tools.py +0 -0
  73. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/iconnection.py +0 -0
  74. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/in_project.py +0 -0
  75. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/oldaplist.py +0 -0
  76. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/oldaplist_helpers.py +0 -0
  77. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/oldaplistnode.py +0 -0
  78. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/oldaplogging.py +0 -0
  79. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/project.py +0 -0
  80. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/propertyclass.py +0 -0
  81. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/resourceclass.py +0 -0
  82. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/role.py +0 -0
  83. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/user.py +0 -0
  84. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/userdataclass.py +0 -0
  85. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/__init__.py +0 -0
  86. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/floatingpoint.py +0 -0
  87. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/iri.py +0 -0
  88. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/xsd.py +0 -0
  89. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/xsd_anyuri.py +0 -0
  90. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/xsd_base64binary.py +0 -0
  91. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/xsd_boolean.py +0 -0
  92. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/xsd_byte.py +0 -0
  93. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/xsd_date.py +0 -0
  94. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/xsd_datetime.py +0 -0
  95. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/xsd_datetimestamp.py +0 -0
  96. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/xsd_decimal.py +0 -0
  97. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/xsd_double.py +0 -0
  98. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/xsd_duration.py +0 -0
  99. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/xsd_float.py +0 -0
  100. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/xsd_gday.py +0 -0
  101. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/xsd_gmonth.py +0 -0
  102. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/xsd_gmonthday.py +0 -0
  103. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/xsd_gyear.py +0 -0
  104. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/xsd_gyearmonth.py +0 -0
  105. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/xsd_hexbinary.py +0 -0
  106. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/xsd_id.py +0 -0
  107. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/xsd_idref.py +0 -0
  108. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/xsd_int.py +0 -0
  109. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/xsd_integer.py +0 -0
  110. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/xsd_language.py +0 -0
  111. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/xsd_long.py +0 -0
  112. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/xsd_name.py +0 -0
  113. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/xsd_ncname.py +0 -0
  114. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/xsd_negativeinteger.py +0 -0
  115. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/xsd_nmtoken.py +0 -0
  116. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/xsd_nonnegativeinteger.py +0 -0
  117. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/xsd_nonpositiveinteger.py +0 -0
  118. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/xsd_normalizedstring.py +0 -0
  119. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/xsd_positiveinteger.py +0 -0
  120. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/xsd_qname.py +0 -0
  121. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/xsd_short.py +0 -0
  122. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/xsd_string.py +0 -0
  123. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/xsd_time.py +0 -0
  124. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/xsd_token.py +0 -0
  125. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/xsd_unsignedbyte.py +0 -0
  126. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/xsd_unsignedint.py +0 -0
  127. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/xsd_unsignedlong.py +0 -0
  128. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/src/xsd/xsd_unsignedshort.py +0 -0
  129. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/test/__init__.py +0 -0
  130. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/test/test_cache.py +0 -0
  131. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/test/test_connection.py +0 -0
  132. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/test/test_context.py +0 -0
  133. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/test/test_datamodel.py +0 -0
  134. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/test/test_dtypes.py +0 -0
  135. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/test/test_externalontologies.py +0 -0
  136. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/test/test_hasproperty.py +0 -0
  137. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/test/test_in_project.py +0 -0
  138. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/test/test_langstring.py +0 -0
  139. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/test/test_language_in.py +0 -0
  140. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/test/test_observable_dict.py +0 -0
  141. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/test/test_observable_set.py +0 -0
  142. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/test/test_oldaplist.py +0 -0
  143. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/test/test_oldaplist_helpers.py +0 -0
  144. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/test/test_oldaplistnode.py +0 -0
  145. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/test/test_project.py +0 -0
  146. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/test/test_propertyclass.py +0 -0
  147. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/test/test_resourceclass.py +0 -0
  148. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/test/test_role.py +0 -0
  149. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/test/test_semantic_version.py +0 -0
  150. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/test/test_user.py +0 -0
  151. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/test/test_xsd_datatypes.py +0 -0
  152. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/test/xxxx_fasnacht.py +0 -0
  153. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/testdata/Gender.yaml +0 -0
  154. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/testdata/collections_type.yaml +0 -0
  155. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/testdata/connection_test.trig +0 -0
  156. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/testdata/datamodel_test.trig +0 -0
  157. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/testdata/event_type.yaml +0 -0
  158. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/testdata/hlist_schema.yaml +0 -0
  159. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/testdata/institution_or_building_type.yaml +0 -0
  160. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/testdata/language.yaml +0 -0
  161. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/testdata/location_type.yaml +0 -0
  162. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/testdata/means_of_transportation.yaml +0 -0
  163. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/testdata/playground_list.yaml +0 -0
  164. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/testdata/role.yaml +0 -0
  165. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/testdata/source_type-1.yaml +0 -0
  166. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/testdata/source_type.yaml +0 -0
  167. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/testdata/test_move_left_of_toL.yaml +0 -0
  168. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/testdata/testlist.yaml +0 -0
  169. {oldaplib-0.4.5 → oldaplib-0.4.6}/oldaplib/testit.http +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: oldaplib
3
- Version: 0.4.5
3
+ Version: 0.4.6
4
4
  Summary: Open Media Access Server Library (Linked Open Data middleware/RESTApi)
5
5
  License: GNU Affero General Public License version 3
6
6
  Author: Lukas Rosenthaler
@@ -211,6 +211,11 @@ class Language(Enum):
211
211
  def toRdf(self) -> str:
212
212
  return f'"{self.name.lower()}"^^xsd:string'
213
213
 
214
+ @property
215
+ def shortlang(self) -> str:
216
+ """Return the language short name in lowercase (e.g., 'de' for Language.DE)"""
217
+ return self.name.lower()
218
+
214
219
 
215
220
  if __name__ == "__main__":
216
221
  print(Language.EN.toRdf)
@@ -222,7 +222,10 @@ class Model:
222
222
  del self._attributes[attr]
223
223
  else:
224
224
  if not isinstance(value, attr.datatype):
225
- self._attributes[attr] = attr.datatype(value, validate=True)
225
+ try:
226
+ self._attributes[attr] = attr.datatype(value, validate=True)
227
+ except Exception as err:
228
+ raise Exception(f"Failed to set attribute '{attr}' with value '{value}': {err}")
226
229
  else:
227
230
  self._attributes[attr] = value
228
231
  if hasattr(self._attributes[attr], 'set_notifier') and hasattr(self, 'notifier'):
@@ -1,11 +1,12 @@
1
1
  import re
2
2
  import textwrap
3
+ from dataclasses import dataclass
3
4
  from pprint import pprint
4
5
 
5
6
  import jwt
6
7
 
7
8
  from datetime import datetime, timedelta
8
- from enum import Flag, auto
9
+ from enum import Flag, auto, Enum
9
10
  from functools import partial
10
11
  from typing import Type, Any, Self, cast, Dict
11
12
 
@@ -24,7 +25,7 @@ from oldaplib.src.helpers.langstring import LangString
24
25
  from oldaplib.src.helpers.observable_dict import ObservableDict
25
26
  from oldaplib.src.helpers.observable_set import ObservableSet
26
27
  from oldaplib.src.helpers.oldaperror import OldapErrorNotFound, OldapErrorValue, OldapErrorInconsistency, \
27
- OldapErrorNoPermission, OldapError, OldapErrorUpdateFailed, OldapErrorInUse, OldapErrorAlreadyExists
28
+ OldapErrorNoPermission, OldapError, OldapErrorUpdateFailed, OldapErrorInUse, OldapErrorAlreadyExists, OldapErrorType
28
29
  from oldaplib.src.helpers.query_processor import QueryProcessor
29
30
  from oldaplib.src.iconnection import IConnection
30
31
  from oldaplib.src.project import Project
@@ -40,10 +41,14 @@ from oldaplib.src.xsd.xsd_string import Xsd_string
40
41
 
41
42
  ValueType = LangString | ObservableSet | Xsd | Dict[Xsd_QName, DataPermission] | ObservableDict
42
43
 
43
- class SortBy(Flag):
44
- PROPVAL = auto()
45
- CREATED = auto()
46
- LASTMOD = auto()
44
+ class SortDir(str, Enum):
45
+ asc = "asc"
46
+ desc = "desc"
47
+
48
+ @dataclass(frozen=True)
49
+ class SortBy:
50
+ property: Xsd_QName
51
+ dir: SortDir = SortDir.asc
47
52
 
48
53
 
49
54
  #@strict
@@ -167,7 +172,7 @@ class ResourceInstance:
167
172
  raise OldapErrorValue(f'{self.name}: Property {prop_iri} with MIN_COUNT={hasprop[HasPropertyAttr.MIN_COUNT]} is missing')
168
173
  elif isinstance(self._values[prop_iri], ObservableSet) and len(self._values[prop_iri]) < 1:
169
174
  raise OldapErrorValue(f'{self.name}: Property {prop_iri} with MIN_COUNT={hasprop[HasPropertyAttr.MIN_COUNT]} is missing')
170
- if hasprop.get(HasPropertyAttr.MAX_COUNT): # testing for MAX_COUNT conformance
175
+ if hasprop.get(HasPropertyAttr.MAX_COUNT) and hasprop[HasPropertyAttr.MAX_COUNT] > 0: # testing for MAX_COUNT conformance
171
176
  if isinstance(self._values.get(prop_iri), ObservableSet) and len(self._values[prop_iri]) > hasprop[HasPropertyAttr.MAX_COUNT]:
172
177
  raise OldapErrorValue(f'{self.name}: Property {prop_iri} with MAX_COUNT={hasprop[HasPropertyAttr.MIN_COUNT]} has to many values (n={len(self._values[prop_iri])})')
173
178
  if self._values.get(prop_iri):
@@ -375,12 +380,12 @@ class ResourceInstance:
375
380
  raise OldapErrorValue(
376
381
  f'{self.name}: Property {prop_iri} with MIN_COUNT={hasprop[HasPropertyAttr.MIN_COUNT]} has not enough values (n={n}).')
377
382
 
378
- if hasprop.get(HasPropertyAttr.MAX_COUNT): # testing for MAX_COUNT conformance
383
+ if hasprop.get(HasPropertyAttr.MAX_COUNT) and hasprop[HasPropertyAttr.MAX_COUNT] > 0: # testing for MAX_COUNT conformance
379
384
  n = len(self._values[prop_iri])
380
385
  if n > hasprop[HasPropertyAttr.MAX_COUNT]:
381
386
  self._values[prop_iri].undo()
382
387
  raise OldapErrorValue(
383
- f'{self.name}: Property {prop_iri} with MAX_COUNT={hasprop[HasPropertyAttr.MIN_COUNT]} has to many values (n={n}).')
388
+ f'{self.name}: Property {prop_iri} with MAX_COUNT={hasprop[HasPropertyAttr.MAX_COUNT]} has to many values (n={n}).')
384
389
 
385
390
  self._changeset[prop_iri] = AttributeChange(None, Action.MODIFY)
386
391
 
@@ -454,7 +459,7 @@ class ResourceInstance:
454
459
  elif isinstance(value, (list, tuple, set, ObservableSet)) and len(value) < hasprop[HasPropertyAttr.MIN_COUNT]:
455
460
  raise OldapErrorValue(
456
461
  f'{self.name}: Property {attr} with MIN_COUNT={hasprop[HasPropertyAttr.MIN_COUNT]} has not enough values')
457
- if hasprop.get(HasPropertyAttr.MAX_COUNT): # testing for MAX_COUNT conformance
462
+ if hasprop.get(HasPropertyAttr.MAX_COUNT) and hasprop[HasPropertyAttr.MAX_COUNT] > 0: # testing for MAX_COUNT conformance
458
463
  if isinstance(value, (list, tuple, set, ObservableSet)) and len(value) > hasprop[HasPropertyAttr.MAX_COUNT]:
459
464
  raise OldapErrorValue(
460
465
  f'{self.name}: Property {attr} with MAX_COUNT={hasprop[HasPropertyAttr.MIN_COUNT]} has to many values (n={len(value)})')
@@ -1095,74 +1100,75 @@ class ResourceInstance:
1095
1100
  for r in res:
1096
1101
  roles[r['role']] = DataPermission.from_qname(r['dataperm'])
1097
1102
  data[Xsd_QName('oldap:attachedToRole')] = roles
1098
-
1103
+ #data[Xsd_QName('virtual:resourceIri')] = iri
1099
1104
  return data
1100
1105
 
1101
1106
  @staticmethod
1102
1107
  def search_fulltext(con: IConnection,
1103
1108
  projectShortName: Xsd_NCName | str,
1104
- s: str,
1109
+ searchstr: str,
1105
1110
  resClass: Xsd_QName | str | None = None,
1106
1111
  countOnly: bool = False,
1107
- sortBy: SortBy | None = None,
1112
+ sortBy: list[SortBy] = [],
1108
1113
  limit: int = 100,
1109
1114
  offset: int = 0,
1110
1115
  indent: int = 0, indent_inc: int = 4) -> int | dict[Iri, dict[str, Xsd]]:
1111
1116
  """
1112
- Search for entities in a project using a full-text search query, with optional filters and sorting
1113
- based on resource class and properties.
1114
-
1115
- :param con: The connection instance to interact with the SPARQL endpoint or database.
1116
- :type con: IConnection
1117
- :param projectShortName: The project short name (identifier). Can be provided as an Xsd_NCName or a
1118
- string. If a string, it is automatically validated and converted to an Xsd_NCName.
1119
- :type projectShortName: Xsd_NCName | str
1120
- :param s: The full-text search query to identify entities matching the specified string values.
1121
- :type s: str
1122
- :param resClass: An optional entity class (Xsd_QName) used to filter results. If provided as a string,
1123
- it is validated and converted to Xsd_QName.
1124
- :type resClass: Xsd_QName | str | None
1125
- :param countOnly: If True, only the count of matching entities will be returned. Defaults to False.
1126
- :type countOnly: bool
1127
- :param sortBy: Optional sorting criterion for the results, such as creation date, last modification date,
1128
- or property values. Defaults to None.
1129
- :type sortBy: SortBy | None
1130
- :param limit: The maximum number of records to return when count_only is False. Defaults to 100.
1131
- :type limit: int
1132
- :param offset: The number of records to skip for paginated queries. Defaults to 0.
1133
- :type offset: int
1134
- :param indent: Initial indentation level for the generated SPARQL query. Defaults to 0.
1135
- :type indent: int
1136
- :param indent_inc: Incremental indentation used during SPARQL query generation. Defaults to 4.
1137
- :type indent_inc: int
1138
- :return: If count_only is True, an integer representing the count of matching entities. If count_only is
1139
- False, a dictionary with entity IRIs as keys and their respective property-to-value mappings
1140
- as values.
1141
- :rtype: int | dict[Iri, dict[str, Xsd]]
1142
-
1143
- The SPARQL generated is as follows
1144
-
1145
- ```sparql
1146
- SELECT DISTINCT ?s ?t ?p ?o ?creationDate
1147
- WHERE {
1148
- GRAPH test:data {
1149
- ?s oldap:creationDate ?creationDate .
1150
- ?s ?p ?o .
1151
- ?s rdf:type ?t .
1152
- ?s rdf:type test:Book .
1153
- ?s oldap:grantsPermission ?permset .
1154
- }
1155
- FILTER(isLiteral(?o) && (datatype(?o) = xsd:string || datatype(?o) = rdf:langString || lang(?o) != ""))
1156
- FILTER(CONTAINS(LCASE(STR(?o)), "gaga")) # case-insensitive substring match
1157
- GRAPH oldap:admin {
1158
- <https://orcid.org/0000-0003-1681-4036> oldap:hasPermissions ?permset .
1159
- ?permset oldap:givesPermission ?DataPermission .
1160
- ?DataPermission oldap:permissionValue ?permval .
1161
- }
1162
- FILTER(?permval >= "2"^^xsd:integer)
1163
- }
1164
- ORDER BY ASC(?creationDate)LIMIT 100 OFFSET 0
1165
- ```
1117
+ Searches and retrieves data from the database using a full-text search query. This method allows
1118
+ users to optionally filter and sort the results based on specific criteria. It supports returning
1119
+ either the count of matching results or the results themselves in a structured format.
1120
+
1121
+ :param con: The connection object used to interact with the database.
1122
+ This must implement the IConnection interface.
1123
+ :type con: IConnection
1124
+
1125
+ :param projectShortName: The short name of the project for which the data
1126
+ needs to be queried.
1127
+ :type projectShortName: Xsd_NCName | str
1128
+
1129
+ :param searchstr: The search string used for full-text search, with case-insensitive
1130
+ substring matching.
1131
+ :type searchstr: str
1132
+
1133
+ :param resClass: An optional parameter specifying the resource class of the
1134
+ objects to be retrieved. If provided, the query will filter results for the
1135
+ specified class.
1136
+ :type resClass: Xsd_QName | str | None
1137
+
1138
+ :param countOnly: A boolean flag to indicate whether to return only the count
1139
+ of matching results. If set to True, only the count is returned;
1140
+ otherwise, matching records are retrieved.
1141
+ :type countOnly: bool
1142
+
1143
+ :param sortBy: A list of sorting criteria. Each criterion specifies the property
1144
+ and direction (ascending or descending) to sort the results. If not provided,
1145
+ no sorting is applied. SortBy has the following attributes:
1146
+ - property (Xsd_QName): The property to sort by. The property must be one of
1147
+ Xsd_QName('oldap:creationDate'), Xsd_QName('oldap:lastModificationDate') or
1148
+ Xsd_QName('oldap:propval') to sort according the property where the match has been found..
1149
+ - direction (str): The sorting direction, either 'asc' for ascending or 'desc' for descending.
1150
+ :type sortBy: list[SortBy]
1151
+
1152
+ :param limit: The maximum number of results to retrieve. Defaults to 100 if not specified.
1153
+ :type limit: int
1154
+
1155
+ :param offset: The number of records to skip before starting to retrieve results.
1156
+ This is useful for paginated responses.
1157
+ :type offset: int
1158
+
1159
+ :param indent: The base indentation level used for formatting the generated SPARQL
1160
+ query string.
1161
+ :type indent: int
1162
+
1163
+ :param indent_inc: The incremental value to add to the base indent for nested query
1164
+ components.
1165
+ :type indent_inc: int
1166
+
1167
+ :return: If `countOnly` is True, an integer representing the count of matching results
1168
+ is returned. Otherwise, a dictionary is returned where the keys are IRIs of the
1169
+ matching resources, and the values are dictionaries containing their respective
1170
+ properties and values.
1171
+ :rtype: int | dict[Iri, dict[str, Xsd]]
1166
1172
  """
1167
1173
  if not isinstance(projectShortName, Xsd_NCName):
1168
1174
  graph = Xsd_NCName(projectShortName, validate=True)
@@ -1180,10 +1186,12 @@ class ResourceInstance:
1180
1186
  else:
1181
1187
  sparql += f'{blank:{indent * indent_inc}}SELECT DISTINCT ?s ?t ?p ?o'
1182
1188
  if sortBy:
1183
- if sortBy == SortBy.CREATED:
1184
- sparql += ' ?creationDate'
1185
- if sortBy == SortBy.LASTMOD:
1186
- sparql += '?lastModificationDate'
1189
+ for sortby in sortBy:
1190
+ if sortby.property == Xsd_QName('oldap:creationDate'):
1191
+ sparql += ' ?creationDate'
1192
+ elif sortby.property == Xsd_QName('oldap:lastModificationDate'):
1193
+ sparql += '?lastModificationDate'
1194
+
1187
1195
  sparql += f'\n{blank:{indent * indent_inc}}WHERE {{'
1188
1196
  sparql += f'\n{blank:{(indent + 1) * indent_inc}}GRAPH oldap:admin {{'
1189
1197
  sparql += f'\n{blank:{(indent + 2) * indent_inc}}{con.userIri.toRdf} oldap:hasRole ?role .'
@@ -1191,11 +1199,12 @@ class ResourceInstance:
1191
1199
  sparql += f'\n{blank:{(indent + 2) * indent_inc}}FILTER(?permval >= {DataPermission.DATA_VIEW.numeric.toRdf})'
1192
1200
  sparql += f'\n{blank:{(indent + 1) * indent_inc}}}}'
1193
1201
  sparql += f'\n{blank:{(indent + 1) * indent_inc}}GRAPH {graph}:data {{'
1194
- if sortBy:
1195
- if sortBy == SortBy.CREATED:
1196
- sparql += f'\n{blank:{(indent + 2) * indent_inc}}?s oldap:creationDate ?creationDate .'
1197
- if sortBy == SortBy.LASTMOD:
1198
- sparql += f'\n{blank:{(indent + 2) * indent_inc}}?s oldap:lastModificationDate ?lastModificationDate .'
1202
+ if not countOnly and sortBy:
1203
+ for sortby in sortBy:
1204
+ if sortby.property == Xsd_QName('oldap:creationDate'):
1205
+ sparql += f'\n{blank:{(indent + 2) * indent_inc}}?s oldap:creationDate ?creationDate .'
1206
+ elif sortby.property == Xsd_QName('oldap:lastModificationDate'):
1207
+ sparql += f'\n{blank:{(indent + 2) * indent_inc}}?s oldap:lastModificationDate ?lastModificationDate .'
1199
1208
  sparql += f'\n{blank:{(indent + 2) * indent_inc}}?s ?p ?o .'
1200
1209
  sparql += f'\n{blank:{(indent + 2) * indent_inc}}?s rdf:type ?t .'
1201
1210
  if resClass:
@@ -1203,17 +1212,27 @@ class ResourceInstance:
1203
1212
  sparql += f'\n{blank:{(indent + 2) * indent_inc}}?s oldap:attachedToRole ?role .'
1204
1213
  sparql += f'\n{blank:{(indent + 2) * indent_inc}}<< ?s oldap:attachedToRole ?role >> oldap:hasDataPermission ?DataPermission .'
1205
1214
  sparql += f'\n{blank:{(indent + 2) * indent_inc}}FILTER(isLiteral(?o) && (datatype(?o) = xsd:string || datatype(?o) = rdf:langString || lang(?o) != ""))'
1206
- sparql += f'\n{blank:{(indent + 2) * indent_inc}}FILTER(CONTAINS(LCASE(STR(?o)), "{s}")) # case-insensitive substring match'
1215
+ sparql += f'\n{blank:{(indent + 2) * indent_inc}}FILTER(CONTAINS(LCASE(STR(?o)), "{searchstr}")) # case-insensitive substring match'
1207
1216
  sparql += f'\n{blank:{(indent + 1) * indent_inc}}}}'
1208
1217
  sparql += f'\n{blank:{indent * indent_inc}}}}'
1209
1218
 
1210
- if sortBy:
1211
- if sortBy == SortBy.CREATED:
1212
- sparql += f'\n{blank:{indent * indent_inc}}ORDER BY ASC(?creationDate)'
1213
- if sortBy == SortBy.LASTMOD:
1214
- sparql += f'\n{blank:{indent * indent_inc}}ORDER BY ASC(?lastModificationDate)'
1215
- if sortBy == SortBy.PROPVAL:
1216
- sparql += f'\n{blank:{indent * indent_inc}}ORDER BY ASC(LCASE(STR(?o)))'
1219
+ if not countOnly and sortBy:
1220
+ for sortby in sortBy:
1221
+ if sortby.property == Xsd_QName('oldap:creationDate'):
1222
+ if sortby.dir == SortDir.asc:
1223
+ sparql += f'\n{blank:{indent * indent_inc}}ORDER BY ASC(?creationDate)'
1224
+ else:
1225
+ sparql += f'\n{blank:{indent * indent_inc}}ORDER BY DESC(?creationDate)'
1226
+ elif sortby.property == Xsd_QName('oldap:lastModificationDate'):
1227
+ if sortby.dir == SortDir.asc:
1228
+ sparql += f'\n{blank:{indent * indent_inc}}ORDER BY ASC(?lastModificationDate)'
1229
+ else:
1230
+ sparql += f'\n{blank:{indent * indent_inc}}ORDER BY DESC(?lastModificationDate)'
1231
+ elif sortby.property == Xsd_QName('oldap:propval'):
1232
+ if sortby.dir == SortDir.asc:
1233
+ sparql += f'\n{blank:{indent * indent_inc}}ORDER BY ASC(?o)'
1234
+ else:
1235
+ sparql += f'\n{blank:{indent * indent_inc}}ORDER BY DESC(?o)'
1217
1236
 
1218
1237
  if not countOnly:
1219
1238
  sparql += f'\n{blank:{indent * indent_inc}}LIMIT {limit} OFFSET {offset}'
@@ -1235,10 +1254,11 @@ class ResourceInstance:
1235
1254
  Xsd_QName('owl:Class'): r['t']
1236
1255
  }
1237
1256
  if sortBy:
1238
- if sortBy == SortBy.CREATED:
1239
- tmp['oldap:creationDate'] = r['creationDate'],
1240
- if sortBy == SortBy.LASTMOD:
1241
- tmp['oldap:lastModificationDate'] = r['lastModificationDate']
1257
+ for sortby in sortBy:
1258
+ if sortby.property == Xsd_QName('oldap:creationDate'):
1259
+ tmp['oldap:creationDate'] = r['creationDate'],
1260
+ elif sortby.property == Xsd_QName('oldap:lastModificationDate'):
1261
+ tmp['oldap:lastModificationDate'] = r['lastModificationDate']
1242
1262
  result[r['s']] = tmp
1243
1263
  return result
1244
1264
 
@@ -1247,43 +1267,19 @@ class ResourceInstance:
1247
1267
  projectShortName: Xsd_NCName | str,
1248
1268
  resClass: Xsd_QName | str,
1249
1269
  includeProperties: list[Xsd_QName] | None = None,
1250
- count_only: bool = False,
1251
- sortBy: SortBy | None = None,
1270
+ countOnly: bool = False,
1271
+ sortBy: list[SortBy] = [],
1252
1272
  limit: int = 100,
1253
1273
  offset: int = 0,
1254
1274
  indent: int = 0, indent_inc: int = 4) -> dict[Iri, dict[str, Xsd]]:
1255
- # TODO: PROBLEM: does not work for properties which use MAX_COUNT > 1 !!!!!!!
1256
1275
  """
1257
1276
  Retrieves all resources matching the specified parameters from a data store using a SPARQL query.
1258
1277
  Depending on the `count_only` flag, it can return either a count of matching resources or detailed
1259
1278
  information about each resource.
1260
-
1261
- :param con: The connection object used to execute the SPARQL query.
1262
- :type con: IConnection
1263
- :param projectShortName: The short name of the project being queried.
1264
- :type projectShortName: Xsd_NCName | str
1265
- :param resClass: The resource class to filter the results by.
1266
- :type resClass: Xsd_QName | str
1267
- :param includeProperties: A list of resource properties to include in the query and results.
1268
- :type includeProperties: list[Xsd_QName] | None
1269
- :param count_only: If True, returns only the count of matching resources. Defaults to False.
1270
- :type count_only: bool
1271
- :param sortBy: The sorting criterion for the results. Defaults to None.
1272
- :type sortBy: SortBy | None
1273
- :param limit: The maximum number of results to retrieve. Defaults to 100.
1274
- :type limit: int
1275
- :param offset: The starting point for result retrieval in pagination. Defaults to 0.
1276
- :type offset: int
1277
- :param indent: The initial level of indentation for the SPARQL query strings. Defaults to 0.
1278
- :type indent: int
1279
- :param indent_inc: The incremental level of indentation for the SPARQL query strings. Defaults to 4.
1280
- :type indent_inc: int
1281
- :return: A dictionary where keys are resource IRIs and values are dictionaries containing resource
1282
- properties and their corresponding values. If `count_only` is True, returns the total count of
1283
- matching resources as an integer.
1284
- :rtype: dict[Iri, dict[str, Xsd]]
1285
- :raises OldapError: If there is an error during query execution in the connection object.
1286
1279
  """
1280
+ # --- ensure re is imported ---
1281
+ import re
1282
+
1287
1283
  if not isinstance(projectShortName, Xsd_NCName):
1288
1284
  graph = Xsd_NCName(projectShortName, validate=True)
1289
1285
  else:
@@ -1291,22 +1287,26 @@ class ResourceInstance:
1291
1287
  if not isinstance(resClass, Xsd_QName):
1292
1288
  resClass = Xsd_QName(resClass, validate=True)
1293
1289
 
1290
+ if sortBy:
1291
+ for s in sortBy:
1292
+ if not isinstance(s, SortBy):
1293
+ raise OldapErrorType(f'Expected SortBy, got {type(s)}')
1294
+ if not includeProperties or s.property not in includeProperties:
1295
+ if not includeProperties:
1296
+ includeProperties = []
1297
+ includeProperties.append(s.property)
1298
+
1294
1299
  blank = ''
1295
1300
  context = Context(name=con.context_name)
1296
1301
  sparql = context.sparql_context
1297
1302
 
1298
- if (count_only):
1303
+ if (countOnly):
1299
1304
  sparql += f'{blank:{indent * indent_inc}}SELECT (COUNT(DISTINCT ?s) as ?numResult)'
1300
1305
  else:
1301
1306
  sparql += f'{blank:{indent * indent_inc}}SELECT DISTINCT ?s'
1302
1307
  if includeProperties:
1303
1308
  for index, item in enumerate(includeProperties):
1304
1309
  sparql += f' ?o{index}'
1305
- if sortBy:
1306
- if sortBy == SortBy.CREATED:
1307
- sparql += ' ?creationDate'
1308
- if sortBy == SortBy.LASTMOD:
1309
- sparql += '?lastModificationDate'
1310
1310
  sparql += f'\n{blank:{indent * indent_inc}}WHERE {{'
1311
1311
  sparql += f'\n{blank:{(indent + 1) * indent_inc}}GRAPH oldap:admin {{'
1312
1312
  sparql += f'\n{blank:{(indent + 2) * indent_inc}}{con.userIri.toRdf} oldap:hasRole ?role .'
@@ -1316,30 +1316,23 @@ class ResourceInstance:
1316
1316
  sparql += f'\n{blank:{(indent + 1) * indent_inc}}GRAPH {graph}:data {{'
1317
1317
  sparql += f'\n{blank:{(indent + 2) * indent_inc}}?s oldap:attachedToRole ?role .'
1318
1318
  sparql += f'\n{blank:{(indent + 2) * indent_inc}}<< ?s oldap:attachedToRole ?role >> oldap:hasDataPermission ?dataperm .'
1319
- if sortBy:
1320
- if sortBy == SortBy.CREATED:
1321
- sparql += f'\n{blank:{(indent + 2) * indent_inc}}?s oldap:creationDate ?creationDate .'
1322
- if sortBy == SortBy.LASTMOD:
1323
- sparql += f'\n{blank:{(indent + 2) * indent_inc}}?s oldap:lastModificationDate ?lastModificationDate .'
1324
1319
  if includeProperties:
1325
1320
  for index, prop in enumerate(includeProperties):
1326
- sparql += f'\n{blank:{(indent + 2) * indent_inc}}?s {prop} ?o{index} .'
1321
+ sparql += f'\n{blank:{(indent + 2) * indent_inc}}OPTIONAL {{ ?s {prop} ?o{index} . }}'
1327
1322
  sparql += f'\n{blank:{(indent + 2) * indent_inc}}?s rdf:type {resClass} .'
1328
1323
  sparql += f'\n{blank:{(indent + 1) * indent_inc}}}}'
1329
1324
  sparql += f'\n{blank:{indent * indent_inc}}}}'
1330
1325
 
1331
- if sortBy:
1332
- if sortBy == SortBy.CREATED:
1333
- sparql += f'\n{blank:{indent * indent_inc}}ORDER BY ASC(?creationDate)'
1334
- if sortBy == SortBy.LASTMOD:
1335
- sparql += f'\n{blank:{indent * indent_inc}}ORDER BY ASC(?lastModificationDate)'
1336
- if sortBy == SortBy.PROPVAL:
1337
- sparql += f'\n{blank:{indent * indent_inc}}ORDER BY'
1338
- if includeProperties:
1339
- for index, item in enumerate(includeProperties):
1340
- sparql += f'\n{blank:{indent * indent_inc}} ?o{index}'
1326
+ if not countOnly and sortBy:
1327
+ sparql += f'\n{blank:{indent * indent_inc}}ORDER BY'
1328
+ for s in sortBy:
1329
+ if s.dir == SortDir.asc:
1330
+ sparql += f' ASC(?o{includeProperties.index(s.property)})'
1331
+ else:
1332
+ sparql += f' DESC(?o{includeProperties.index(s.property)})'
1333
+ sparql += '\n'
1341
1334
 
1342
- if not count_only:
1335
+ if not countOnly:
1343
1336
  sparql += f'\n{blank:{indent * indent_inc}}LIMIT {limit} OFFSET {offset}'
1344
1337
  sparql += '\n'
1345
1338
 
@@ -1349,22 +1342,33 @@ class ResourceInstance:
1349
1342
  print(sparql)
1350
1343
  raise
1351
1344
  res = QueryProcessor(context, jsonres)
1352
- if count_only:
1345
+ if countOnly:
1353
1346
  return res[0]['numResult']
1354
1347
  else:
1355
1348
  result = {}
1356
1349
  for r in res:
1357
- tmp = {}
1350
+ if result.get(r['s'], None) is None:
1351
+ result[r['s']] = {}
1358
1352
  if includeProperties:
1359
- for index, item in enumerate(includeProperties):
1360
- tmp[item] = r[f'o{index}']
1353
+ for index, property in enumerate(includeProperties):
1354
+ if result[r['s']].get(property, None) is None:
1355
+ result[r['s']][property] = []
1356
+ raw = r.get(f'o{index}')
1357
+ if raw is None:
1358
+ continue
1359
+ if raw not in result[r['s']][property]:
1360
+ result[r['s']][property].append(raw)
1361
+ if includeProperties:
1362
+ for k, v in result.items():
1363
+ for index, property in enumerate(includeProperties):
1364
+ is_langstring = False
1365
+ for val in v[property]:
1366
+ if isinstance(val, Xsd_string) and val.lang is not None:
1367
+ is_langstring = True
1368
+ break
1369
+ if is_langstring:
1370
+ v[property] = LangString(v[property])
1361
1371
 
1362
- if sortBy:
1363
- if sortBy == SortBy.CREATED:
1364
- tmp[Xsd_QName('oldap:creationDate')] = r['creationDate']
1365
- if sortBy == SortBy.LASTMOD:
1366
- tmp[Xsd_QName('oldap:lastModificationDate')] = r['lastModificationDate']
1367
- result[r['s']] = tmp
1368
1372
  return result
1369
1373
 
1370
1374
  @staticmethod
@@ -0,0 +1 @@
1
+ __version__ = "0.4.6"