dsp-tools 0.9.13__py3-none-any.whl → 18.3.0.post13__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 (348) hide show
  1. dsp_tools/__init__.py +5 -0
  2. dsp_tools/cli/args.py +47 -0
  3. dsp_tools/cli/call_action.py +85 -0
  4. dsp_tools/cli/call_action_files_only.py +101 -0
  5. dsp_tools/cli/call_action_with_network.py +207 -0
  6. dsp_tools/cli/create_parsers.py +479 -0
  7. dsp_tools/cli/entry_point.py +322 -0
  8. dsp_tools/cli/utils.py +87 -0
  9. dsp_tools/clients/CLAUDE.md +420 -0
  10. dsp_tools/clients/authentication_client.py +14 -0
  11. dsp_tools/clients/authentication_client_live.py +66 -0
  12. dsp_tools/clients/connection.py +35 -0
  13. dsp_tools/clients/connection_live.py +233 -0
  14. dsp_tools/clients/fuseki_metrics.py +60 -0
  15. dsp_tools/clients/group_user_clients.py +35 -0
  16. dsp_tools/clients/group_user_clients_live.py +181 -0
  17. dsp_tools/clients/legal_info_client.py +23 -0
  18. dsp_tools/clients/legal_info_client_live.py +132 -0
  19. dsp_tools/clients/list_client.py +49 -0
  20. dsp_tools/clients/list_client_live.py +166 -0
  21. dsp_tools/clients/metadata_client.py +24 -0
  22. dsp_tools/clients/metadata_client_live.py +47 -0
  23. dsp_tools/clients/ontology_clients.py +49 -0
  24. dsp_tools/clients/ontology_create_client_live.py +166 -0
  25. dsp_tools/clients/ontology_get_client_live.py +80 -0
  26. dsp_tools/clients/permissions_client.py +68 -0
  27. dsp_tools/clients/project_client.py +16 -0
  28. dsp_tools/clients/project_client_live.py +66 -0
  29. dsp_tools/commands/create/communicate_problems.py +24 -0
  30. dsp_tools/commands/create/create.py +134 -0
  31. dsp_tools/commands/create/create_on_server/cardinalities.py +111 -0
  32. dsp_tools/commands/create/create_on_server/classes.py +99 -0
  33. dsp_tools/commands/create/create_on_server/complete_ontologies.py +116 -0
  34. dsp_tools/commands/create/create_on_server/default_permissions.py +134 -0
  35. dsp_tools/commands/create/create_on_server/group_users.py +165 -0
  36. dsp_tools/commands/create/create_on_server/lists.py +163 -0
  37. dsp_tools/commands/create/create_on_server/mappers.py +12 -0
  38. dsp_tools/commands/create/create_on_server/onto_utils.py +74 -0
  39. dsp_tools/commands/create/create_on_server/ontology.py +52 -0
  40. dsp_tools/commands/create/create_on_server/project.py +68 -0
  41. dsp_tools/commands/create/create_on_server/properties.py +119 -0
  42. dsp_tools/commands/create/exceptions.py +29 -0
  43. dsp_tools/commands/create/lists_only.py +66 -0
  44. dsp_tools/commands/create/models/create_problems.py +87 -0
  45. dsp_tools/commands/create/models/parsed_ontology.py +88 -0
  46. dsp_tools/commands/create/models/parsed_project.py +81 -0
  47. dsp_tools/commands/create/models/rdf_ontology.py +12 -0
  48. dsp_tools/commands/create/models/server_project_info.py +100 -0
  49. dsp_tools/commands/create/parsing/parse_lists.py +45 -0
  50. dsp_tools/commands/create/parsing/parse_ontology.py +243 -0
  51. dsp_tools/commands/create/parsing/parse_project.py +149 -0
  52. dsp_tools/commands/create/parsing/parsing_utils.py +40 -0
  53. dsp_tools/commands/create/project_validate.py +595 -0
  54. dsp_tools/commands/create/serialisation/ontology.py +119 -0
  55. dsp_tools/commands/create/serialisation/project.py +44 -0
  56. dsp_tools/commands/excel2json/CLAUDE.md +101 -0
  57. dsp_tools/commands/excel2json/json_header.py +321 -0
  58. dsp_tools/commands/excel2json/lists/__init__.py +0 -0
  59. dsp_tools/commands/excel2json/lists/compliance_checks.py +292 -0
  60. dsp_tools/commands/excel2json/lists/make_lists.py +247 -0
  61. dsp_tools/commands/excel2json/lists/models/__init__.py +0 -0
  62. dsp_tools/commands/excel2json/lists/models/deserialise.py +30 -0
  63. dsp_tools/commands/excel2json/lists/models/input_error.py +216 -0
  64. dsp_tools/commands/excel2json/lists/models/serialise.py +57 -0
  65. dsp_tools/commands/excel2json/lists/utils.py +81 -0
  66. dsp_tools/commands/excel2json/models/__init__.py +0 -0
  67. dsp_tools/commands/excel2json/models/input_error.py +416 -0
  68. dsp_tools/commands/excel2json/models/json_header.py +175 -0
  69. dsp_tools/commands/excel2json/models/list_node_name.py +16 -0
  70. dsp_tools/commands/excel2json/models/ontology.py +76 -0
  71. dsp_tools/commands/excel2json/old_lists.py +328 -0
  72. dsp_tools/commands/excel2json/project.py +280 -0
  73. dsp_tools/commands/excel2json/properties.py +370 -0
  74. dsp_tools/commands/excel2json/resources.py +336 -0
  75. dsp_tools/commands/excel2json/utils.py +352 -0
  76. dsp_tools/commands/excel2xml/__init__.py +7 -0
  77. dsp_tools/commands/excel2xml/excel2xml_cli.py +523 -0
  78. dsp_tools/commands/excel2xml/excel2xml_lib.py +1953 -0
  79. dsp_tools/commands/excel2xml/propertyelement.py +47 -0
  80. dsp_tools/commands/get/__init__.py +0 -0
  81. dsp_tools/commands/get/get.py +166 -0
  82. dsp_tools/commands/get/get_permissions.py +257 -0
  83. dsp_tools/commands/get/get_permissions_legacy.py +89 -0
  84. dsp_tools/commands/get/legacy_models/__init__.py +0 -0
  85. dsp_tools/commands/get/legacy_models/context.py +318 -0
  86. dsp_tools/commands/get/legacy_models/group.py +241 -0
  87. dsp_tools/commands/get/legacy_models/helpers.py +47 -0
  88. dsp_tools/commands/get/legacy_models/listnode.py +390 -0
  89. dsp_tools/commands/get/legacy_models/model.py +12 -0
  90. dsp_tools/commands/get/legacy_models/ontology.py +324 -0
  91. dsp_tools/commands/get/legacy_models/project.py +366 -0
  92. dsp_tools/commands/get/legacy_models/propertyclass.py +417 -0
  93. dsp_tools/commands/get/legacy_models/resourceclass.py +676 -0
  94. dsp_tools/commands/get/legacy_models/user.py +438 -0
  95. dsp_tools/commands/get/models/__init__.py +0 -0
  96. dsp_tools/commands/get/models/permissions_models.py +10 -0
  97. dsp_tools/commands/id2iri.py +258 -0
  98. dsp_tools/commands/ingest_xmlupload/__init__.py +0 -0
  99. dsp_tools/commands/ingest_xmlupload/bulk_ingest_client.py +178 -0
  100. dsp_tools/commands/ingest_xmlupload/create_resources/__init__.py +0 -0
  101. dsp_tools/commands/ingest_xmlupload/create_resources/apply_ingest_id.py +69 -0
  102. dsp_tools/commands/ingest_xmlupload/create_resources/upload_xml.py +166 -0
  103. dsp_tools/commands/ingest_xmlupload/create_resources/user_information.py +121 -0
  104. dsp_tools/commands/ingest_xmlupload/ingest_files/__init__.py +0 -0
  105. dsp_tools/commands/ingest_xmlupload/ingest_files/ingest_files.py +64 -0
  106. dsp_tools/commands/ingest_xmlupload/upload_files/__init__.py +0 -0
  107. dsp_tools/commands/ingest_xmlupload/upload_files/filechecker.py +20 -0
  108. dsp_tools/commands/ingest_xmlupload/upload_files/input_error.py +57 -0
  109. dsp_tools/commands/ingest_xmlupload/upload_files/upload_failures.py +66 -0
  110. dsp_tools/commands/ingest_xmlupload/upload_files/upload_files.py +67 -0
  111. dsp_tools/commands/resume_xmlupload/__init__.py +0 -0
  112. dsp_tools/commands/resume_xmlupload/resume_xmlupload.py +96 -0
  113. dsp_tools/commands/start_stack.py +428 -0
  114. dsp_tools/commands/update_legal/CLAUDE.md +344 -0
  115. dsp_tools/commands/update_legal/__init__.py +0 -0
  116. dsp_tools/commands/update_legal/core.py +182 -0
  117. dsp_tools/commands/update_legal/csv_operations.py +135 -0
  118. dsp_tools/commands/update_legal/models.py +87 -0
  119. dsp_tools/commands/update_legal/xml_operations.py +247 -0
  120. dsp_tools/commands/validate_data/CLAUDE.md +159 -0
  121. dsp_tools/commands/validate_data/__init__.py +0 -0
  122. dsp_tools/commands/validate_data/constants.py +59 -0
  123. dsp_tools/commands/validate_data/mappers.py +143 -0
  124. dsp_tools/commands/validate_data/models/__init__.py +0 -0
  125. dsp_tools/commands/validate_data/models/api_responses.py +45 -0
  126. dsp_tools/commands/validate_data/models/input_problems.py +119 -0
  127. dsp_tools/commands/validate_data/models/rdf_like_data.py +117 -0
  128. dsp_tools/commands/validate_data/models/validation.py +106 -0
  129. dsp_tools/commands/validate_data/prepare_data/__init__.py +0 -0
  130. dsp_tools/commands/validate_data/prepare_data/get_rdf_like_data.py +296 -0
  131. dsp_tools/commands/validate_data/prepare_data/make_data_graph.py +91 -0
  132. dsp_tools/commands/validate_data/prepare_data/prepare_data.py +184 -0
  133. dsp_tools/commands/validate_data/process_validation_report/__init__.py +0 -0
  134. dsp_tools/commands/validate_data/process_validation_report/get_user_validation_message.py +358 -0
  135. dsp_tools/commands/validate_data/process_validation_report/query_validation_result.py +507 -0
  136. dsp_tools/commands/validate_data/process_validation_report/reformat_validation_results.py +150 -0
  137. dsp_tools/commands/validate_data/shacl_cli_validator.py +70 -0
  138. dsp_tools/commands/validate_data/sparql/__init__.py +0 -0
  139. dsp_tools/commands/validate_data/sparql/cardinality_shacl.py +209 -0
  140. dsp_tools/commands/validate_data/sparql/construct_shacl.py +92 -0
  141. dsp_tools/commands/validate_data/sparql/legal_info_shacl.py +36 -0
  142. dsp_tools/commands/validate_data/sparql/value_shacl.py +357 -0
  143. dsp_tools/commands/validate_data/utils.py +59 -0
  144. dsp_tools/commands/validate_data/validate_data.py +283 -0
  145. dsp_tools/commands/validate_data/validation/__init__.py +0 -0
  146. dsp_tools/commands/validate_data/validation/check_duplicate_files.py +55 -0
  147. dsp_tools/commands/validate_data/validation/check_for_unknown_classes.py +67 -0
  148. dsp_tools/commands/validate_data/validation/get_validation_report.py +94 -0
  149. dsp_tools/commands/validate_data/validation/validate_ontology.py +107 -0
  150. dsp_tools/commands/xmlupload/CLAUDE.md +292 -0
  151. dsp_tools/commands/xmlupload/__init__.py +0 -0
  152. dsp_tools/commands/xmlupload/iri_resolver.py +21 -0
  153. dsp_tools/commands/xmlupload/make_rdf_graph/__init__.py +0 -0
  154. dsp_tools/commands/xmlupload/make_rdf_graph/constants.py +63 -0
  155. dsp_tools/commands/xmlupload/make_rdf_graph/jsonld_utils.py +44 -0
  156. dsp_tools/commands/xmlupload/make_rdf_graph/make_file_value.py +77 -0
  157. dsp_tools/commands/xmlupload/make_rdf_graph/make_resource_and_values.py +114 -0
  158. dsp_tools/commands/xmlupload/make_rdf_graph/make_values.py +262 -0
  159. dsp_tools/commands/xmlupload/models/__init__.py +0 -0
  160. dsp_tools/commands/xmlupload/models/bitstream_info.py +18 -0
  161. dsp_tools/commands/xmlupload/models/formatted_text_value.py +10 -0
  162. dsp_tools/commands/xmlupload/models/ingest.py +143 -0
  163. dsp_tools/commands/xmlupload/models/input_problems.py +58 -0
  164. dsp_tools/commands/xmlupload/models/lookup_models.py +21 -0
  165. dsp_tools/commands/xmlupload/models/permission.py +45 -0
  166. dsp_tools/commands/xmlupload/models/permissions_parsed.py +93 -0
  167. dsp_tools/commands/xmlupload/models/processed/__init__.py +0 -0
  168. dsp_tools/commands/xmlupload/models/processed/file_values.py +29 -0
  169. dsp_tools/commands/xmlupload/models/processed/res.py +27 -0
  170. dsp_tools/commands/xmlupload/models/processed/values.py +101 -0
  171. dsp_tools/commands/xmlupload/models/rdf_models.py +26 -0
  172. dsp_tools/commands/xmlupload/models/upload_clients.py +14 -0
  173. dsp_tools/commands/xmlupload/models/upload_state.py +20 -0
  174. dsp_tools/commands/xmlupload/prepare_xml_input/__init__.py +0 -0
  175. dsp_tools/commands/xmlupload/prepare_xml_input/ark2iri.py +55 -0
  176. dsp_tools/commands/xmlupload/prepare_xml_input/get_processed_resources.py +252 -0
  177. dsp_tools/commands/xmlupload/prepare_xml_input/iiif_uri_validator.py +50 -0
  178. dsp_tools/commands/xmlupload/prepare_xml_input/list_client.py +120 -0
  179. dsp_tools/commands/xmlupload/prepare_xml_input/prepare_xml_input.py +67 -0
  180. dsp_tools/commands/xmlupload/prepare_xml_input/read_validate_xml_file.py +58 -0
  181. dsp_tools/commands/xmlupload/prepare_xml_input/transform_input_values.py +118 -0
  182. dsp_tools/commands/xmlupload/resource_create_client.py +25 -0
  183. dsp_tools/commands/xmlupload/richtext_id2iri.py +37 -0
  184. dsp_tools/commands/xmlupload/stash/__init__.py +0 -0
  185. dsp_tools/commands/xmlupload/stash/analyse_circular_reference_graph.py +236 -0
  186. dsp_tools/commands/xmlupload/stash/create_info_for_graph.py +53 -0
  187. dsp_tools/commands/xmlupload/stash/graph_models.py +87 -0
  188. dsp_tools/commands/xmlupload/stash/stash_circular_references.py +68 -0
  189. dsp_tools/commands/xmlupload/stash/stash_models.py +109 -0
  190. dsp_tools/commands/xmlupload/stash/upload_stashed_resptr_props.py +106 -0
  191. dsp_tools/commands/xmlupload/stash/upload_stashed_xml_texts.py +196 -0
  192. dsp_tools/commands/xmlupload/upload_config.py +76 -0
  193. dsp_tools/commands/xmlupload/write_diagnostic_info.py +27 -0
  194. dsp_tools/commands/xmlupload/xmlupload.py +516 -0
  195. dsp_tools/config/__init__.py +0 -0
  196. dsp_tools/config/logger_config.py +69 -0
  197. dsp_tools/config/warnings_config.py +32 -0
  198. dsp_tools/error/__init__.py +0 -0
  199. dsp_tools/error/custom_warnings.py +39 -0
  200. dsp_tools/error/exceptions.py +204 -0
  201. dsp_tools/error/problems.py +10 -0
  202. dsp_tools/error/xmllib_errors.py +20 -0
  203. dsp_tools/error/xmllib_warnings.py +54 -0
  204. dsp_tools/error/xmllib_warnings_util.py +159 -0
  205. dsp_tools/error/xsd_validation_error_msg.py +19 -0
  206. dsp_tools/legacy_models/__init__.py +0 -0
  207. dsp_tools/legacy_models/datetimestamp.py +81 -0
  208. dsp_tools/legacy_models/langstring.py +253 -0
  209. dsp_tools/legacy_models/projectContext.py +49 -0
  210. dsp_tools/py.typed +0 -0
  211. dsp_tools/resources/schema/data.xsd +648 -0
  212. dsp_tools/resources/schema/lists-only.json +72 -0
  213. dsp_tools/resources/schema/project.json +1258 -0
  214. dsp_tools/resources/schema/properties-only.json +874 -0
  215. dsp_tools/resources/schema/resources-only.json +140 -0
  216. dsp_tools/resources/start-stack/docker-compose.override-host.j2 +11 -0
  217. dsp_tools/resources/start-stack/docker-compose.override.yml +11 -0
  218. dsp_tools/resources/start-stack/docker-compose.yml +88 -0
  219. dsp_tools/resources/start-stack/dsp-app-config.json +45 -0
  220. dsp_tools/resources/start-stack/dsp-app-config.override-host.j2 +26 -0
  221. dsp_tools/resources/validate_data/api-shapes-resource-cardinalities.ttl +191 -0
  222. dsp_tools/resources/validate_data/api-shapes.ttl +804 -0
  223. dsp_tools/resources/validate_data/shacl-cli-image.yml +4 -0
  224. dsp_tools/resources/validate_data/validate-ontology.ttl +99 -0
  225. dsp_tools/utils/__init__.py +0 -0
  226. dsp_tools/utils/ansi_colors.py +32 -0
  227. dsp_tools/utils/data_formats/__init__.py +0 -0
  228. dsp_tools/utils/data_formats/date_util.py +166 -0
  229. dsp_tools/utils/data_formats/iri_util.py +30 -0
  230. dsp_tools/utils/data_formats/shared.py +81 -0
  231. dsp_tools/utils/data_formats/uri_util.py +76 -0
  232. dsp_tools/utils/fuseki_bloating.py +63 -0
  233. dsp_tools/utils/json_parsing.py +22 -0
  234. dsp_tools/utils/rdf_constants.py +42 -0
  235. dsp_tools/utils/rdflib_utils.py +10 -0
  236. dsp_tools/utils/replace_id_with_iri.py +66 -0
  237. dsp_tools/utils/request_utils.py +238 -0
  238. dsp_tools/utils/xml_parsing/__init__.py +0 -0
  239. dsp_tools/utils/xml_parsing/get_lookups.py +32 -0
  240. dsp_tools/utils/xml_parsing/get_parsed_resources.py +325 -0
  241. dsp_tools/utils/xml_parsing/models/__init__.py +0 -0
  242. dsp_tools/utils/xml_parsing/models/parsed_resource.py +76 -0
  243. dsp_tools/utils/xml_parsing/parse_clean_validate_xml.py +137 -0
  244. dsp_tools/xmllib/CLAUDE.md +302 -0
  245. dsp_tools/xmllib/__init__.py +49 -0
  246. dsp_tools/xmllib/general_functions.py +877 -0
  247. dsp_tools/xmllib/internal/__init__.py +0 -0
  248. dsp_tools/xmllib/internal/checkers.py +162 -0
  249. dsp_tools/xmllib/internal/circumvent_circular_imports.py +36 -0
  250. dsp_tools/xmllib/internal/constants.py +46 -0
  251. dsp_tools/xmllib/internal/input_converters.py +155 -0
  252. dsp_tools/xmllib/internal/serialise_file_value.py +57 -0
  253. dsp_tools/xmllib/internal/serialise_resource.py +177 -0
  254. dsp_tools/xmllib/internal/serialise_values.py +152 -0
  255. dsp_tools/xmllib/internal/type_aliases.py +11 -0
  256. dsp_tools/xmllib/models/__init__.py +0 -0
  257. dsp_tools/xmllib/models/config_options.py +28 -0
  258. dsp_tools/xmllib/models/date_formats.py +48 -0
  259. dsp_tools/xmllib/models/dsp_base_resources.py +1542 -0
  260. dsp_tools/xmllib/models/internal/__init__.py +0 -0
  261. dsp_tools/xmllib/models/internal/file_values.py +172 -0
  262. dsp_tools/xmllib/models/internal/geometry.py +162 -0
  263. dsp_tools/xmllib/models/internal/migration_metadata.py +55 -0
  264. dsp_tools/xmllib/models/internal/serialise_permissions.py +66 -0
  265. dsp_tools/xmllib/models/internal/values.py +342 -0
  266. dsp_tools/xmllib/models/licenses/__init__.py +0 -0
  267. dsp_tools/xmllib/models/licenses/other.py +59 -0
  268. dsp_tools/xmllib/models/licenses/recommended.py +107 -0
  269. dsp_tools/xmllib/models/permissions.py +41 -0
  270. dsp_tools/xmllib/models/res.py +1782 -0
  271. dsp_tools/xmllib/models/root.py +348 -0
  272. dsp_tools/xmllib/value_checkers.py +434 -0
  273. dsp_tools/xmllib/value_converters.py +777 -0
  274. dsp_tools-18.3.0.post13.dist-info/METADATA +90 -0
  275. dsp_tools-18.3.0.post13.dist-info/RECORD +286 -0
  276. dsp_tools-18.3.0.post13.dist-info/WHEEL +4 -0
  277. dsp_tools-18.3.0.post13.dist-info/entry_points.txt +3 -0
  278. dsp_tools-0.9.13.dist-info/LICENSE +0 -674
  279. dsp_tools-0.9.13.dist-info/METADATA +0 -144
  280. dsp_tools-0.9.13.dist-info/RECORD +0 -71
  281. dsp_tools-0.9.13.dist-info/WHEEL +0 -5
  282. dsp_tools-0.9.13.dist-info/entry_points.txt +0 -3
  283. dsp_tools-0.9.13.dist-info/top_level.txt +0 -1
  284. dsplib/models/connection.py +0 -272
  285. dsplib/models/group.py +0 -296
  286. dsplib/models/helpers.py +0 -505
  287. dsplib/models/langstring.py +0 -277
  288. dsplib/models/listnode.py +0 -578
  289. dsplib/models/model.py +0 -20
  290. dsplib/models/ontology.py +0 -448
  291. dsplib/models/permission.py +0 -112
  292. dsplib/models/project.py +0 -547
  293. dsplib/models/propertyclass.py +0 -505
  294. dsplib/models/resource.py +0 -366
  295. dsplib/models/resourceclass.py +0 -810
  296. dsplib/models/sipi.py +0 -30
  297. dsplib/models/user.py +0 -731
  298. dsplib/models/value.py +0 -1000
  299. dsplib/utils/knora-data-schema.xsd +0 -454
  300. dsplib/utils/knora-schema-lists.json +0 -83
  301. dsplib/utils/knora-schema.json +0 -434
  302. dsplib/utils/onto_commons.py +0 -24
  303. dsplib/utils/onto_create_lists.py +0 -73
  304. dsplib/utils/onto_create_ontology.py +0 -442
  305. dsplib/utils/onto_get.py +0 -58
  306. dsplib/utils/onto_validate.py +0 -33
  307. dsplib/utils/xml_upload.py +0 -539
  308. dsplib/widgets/doublepassword.py +0 -80
  309. knora/MLS-import-libraries.py +0 -84
  310. knora/dsp_tools.py +0 -96
  311. knora/dsplib/models/connection.py +0 -272
  312. knora/dsplib/models/group.py +0 -296
  313. knora/dsplib/models/helpers.py +0 -506
  314. knora/dsplib/models/langstring.py +0 -277
  315. knora/dsplib/models/listnode.py +0 -578
  316. knora/dsplib/models/model.py +0 -20
  317. knora/dsplib/models/ontology.py +0 -448
  318. knora/dsplib/models/permission.py +0 -112
  319. knora/dsplib/models/project.py +0 -583
  320. knora/dsplib/models/propertyclass.py +0 -505
  321. knora/dsplib/models/resource.py +0 -416
  322. knora/dsplib/models/resourceclass.py +0 -811
  323. knora/dsplib/models/sipi.py +0 -35
  324. knora/dsplib/models/user.py +0 -731
  325. knora/dsplib/models/value.py +0 -1000
  326. knora/dsplib/utils/knora-data-schema.xsd +0 -464
  327. knora/dsplib/utils/knora-schema-lists.json +0 -83
  328. knora/dsplib/utils/knora-schema.json +0 -444
  329. knora/dsplib/utils/onto_commons.py +0 -24
  330. knora/dsplib/utils/onto_create_lists.py +0 -73
  331. knora/dsplib/utils/onto_create_ontology.py +0 -451
  332. knora/dsplib/utils/onto_get.py +0 -58
  333. knora/dsplib/utils/onto_validate.py +0 -33
  334. knora/dsplib/utils/xml_upload.py +0 -540
  335. knora/dsplib/widgets/doublepassword.py +0 -80
  336. knora/knora.py +0 -2108
  337. knora/test.py +0 -99
  338. knora/testit.py +0 -76
  339. knora/xml2knora.py +0 -633
  340. {dsplib → dsp_tools/cli}/__init__.py +0 -0
  341. {dsplib/models → dsp_tools/clients}/__init__.py +0 -0
  342. {dsplib/utils → dsp_tools/commands}/__init__.py +0 -0
  343. {dsplib/widgets → dsp_tools/commands/create}/__init__.py +0 -0
  344. {knora → dsp_tools/commands/create/create_on_server}/__init__.py +0 -0
  345. {knora/dsplib → dsp_tools/commands/create/models}/__init__.py +0 -0
  346. {knora/dsplib/models → dsp_tools/commands/create/parsing}/__init__.py +0 -0
  347. {knora/dsplib/utils → dsp_tools/commands/create/serialisation}/__init__.py +0 -0
  348. {knora/dsplib/widgets → dsp_tools/commands/excel2json}/__init__.py +0 -0
@@ -0,0 +1,318 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Optional
4
+
5
+ import regex
6
+
7
+ from dsp_tools.commands.get.legacy_models.helpers import ContextType
8
+ from dsp_tools.commands.get.legacy_models.helpers import OntoIri
9
+ from dsp_tools.error.exceptions import BaseError
10
+ from dsp_tools.utils.data_formats.iri_util import is_iri
11
+
12
+
13
+ class ContextIterator:
14
+ _context: Context
15
+ _prefixes: list[str]
16
+ _index: int
17
+
18
+ def __init__(self, context: Context):
19
+ self._context = context
20
+ self._prefixes = list(self._context.context)
21
+ self._index = 0
22
+
23
+ def __next__(self) -> tuple[Optional[str], Optional[OntoIri]]:
24
+ if len(self._context.context) == 0 and self._index == 0:
25
+ return None, None
26
+ elif self._index < len(self._context.context):
27
+ tmp = self._prefixes[self._index]
28
+ self._index += 1
29
+ return tmp, self._context.context[tmp]
30
+ else:
31
+ raise StopIteration
32
+
33
+
34
+ class Context:
35
+ """
36
+ This class holds a JSON-LD context with the ontology IRI's and the associated prefixes
37
+ """
38
+
39
+ _context: ContextType
40
+ _rcontext: dict[str, str]
41
+
42
+ common_ontologies = ContextType(
43
+ {
44
+ "foaf": OntoIri("http://xmlns.com/foaf/0.1/", False),
45
+ "dc": OntoIri("http://purl.org/dc/elements/1.1/", False),
46
+ "dcterms": OntoIri("http://purl.org/dc/terms/", False),
47
+ "dcmi": OntoIri("http://purl.org/dc/dcmitype/", False),
48
+ "skos": OntoIri("http://www.w3.org/2004/02/skos/core", True),
49
+ "bibtex": OntoIri("http://purl.org/net/nknouf/ns/bibtex", True),
50
+ "bibo": OntoIri("http://purl.org/ontology/bibo/", False),
51
+ "cidoc": OntoIri("http://purl.org/NET/cidoc-crm/core", True),
52
+ "schema": OntoIri("https://schema.org/", False),
53
+ "edm": OntoIri("http://www.europeana.eu/schemas/edm/", False),
54
+ "ebucore": OntoIri("http://www.ebu.ch/metadata/ontologies/ebucore/ebucore", True),
55
+ }
56
+ )
57
+
58
+ knora_ontologies = ContextType(
59
+ {
60
+ "knora-api": OntoIri("http://api.knora.org/ontology/knora-api/v2", True),
61
+ "salsah-gui": OntoIri("http://api.knora.org/ontology/salsah-gui/v2", True),
62
+ }
63
+ )
64
+
65
+ base_ontologies = ContextType(
66
+ {
67
+ "rdf": OntoIri("http://www.w3.org/1999/02/22-rdf-syntax-ns", True),
68
+ "rdfs": OntoIri("http://www.w3.org/2000/01/rdf-schema", True),
69
+ "owl": OntoIri("http://www.w3.org/2002/07/owl", True),
70
+ "xsd": OntoIri("http://www.w3.org/2001/XMLSchema", True),
71
+ }
72
+ )
73
+
74
+ def __init__(self, context: Optional[dict[str, str]] = None):
75
+ """
76
+ THe Constructor of the Context. It takes one optional parameter which as a dict of
77
+ prefix - ontology-iri pairs. If the hashtag "#" is used to append element name, the
78
+ ontology-iri *must* end with "#"!
79
+ :param context: A dict of prefix - ontology-iri pairs
80
+ """
81
+ self._context = ContextType({})
82
+
83
+ # add ontologies from context, if any
84
+ if context:
85
+ for prefix, onto in context.items():
86
+ self._context[prefix] = OntoIri(onto.removesuffix("#"), onto.endswith(("#", "/v2")))
87
+
88
+ # add standard ontologies (rdf, rdfs, owl, xsl)
89
+ for k, v in self.base_ontologies.items():
90
+ if not self._context.get(k):
91
+ self._context[k] = v
92
+
93
+ # add DSP-API internal ontologies (knora-api, salsah-gui)
94
+ for k, v in self.knora_ontologies.items():
95
+ if not self._context.get(k):
96
+ self._context[k] = v
97
+
98
+ self._rcontext = {v.iri: k for k, v in self._context.items()}
99
+
100
+ def __getitem__(self, key: str) -> OntoIri:
101
+ return self._context[key]
102
+
103
+ def __contains__(self, key: str) -> bool:
104
+ return key in self._context
105
+
106
+ def __iter__(self) -> ContextIterator:
107
+ return ContextIterator(self)
108
+
109
+ def __str__(self) -> str:
110
+ output = "Context:\n"
111
+ for prefix, val in self._context.items():
112
+ output += " " + prefix + ": " + val.iri + "\n"
113
+ return output
114
+
115
+ #
116
+ # now we have a lot of getters/setters
117
+ #
118
+ @property
119
+ def context(self) -> ContextType:
120
+ return self._context
121
+
122
+ @context.setter
123
+ def context(self, value: ContextType) -> None:
124
+ """
125
+ Setter function for the context out of a dict in the form { prefix1 : iri1, prefix2, iri2, …}
126
+
127
+ :param value: Dictionary of context
128
+ :return: None
129
+ """
130
+ if value is not None and isinstance(value, dict):
131
+ self._context = value
132
+ else:
133
+ raise BaseError("Error in parameter to context setter")
134
+
135
+ def add_context(self, prefix: str, iri: Optional[str] = None) -> None:
136
+ """
137
+ Add a new context to a context instance
138
+
139
+ :param prefix: The prefix that should be used
140
+ :param iri: The IRI that belongs to this context prefix
141
+ :return: None
142
+ """
143
+ match iri:
144
+ case None:
145
+ if prefix in self.knora_ontologies:
146
+ return
147
+ elif prefix in self.base_ontologies:
148
+ return
149
+ elif prefix in self.common_ontologies:
150
+ self._context[prefix] = self.common_ontologies[prefix]
151
+ case str():
152
+ if iri.endswith("#"):
153
+ iri = iri[:-1]
154
+ self._context[prefix] = OntoIri(iri, True)
155
+ else:
156
+ self._context[prefix] = OntoIri(iri, False)
157
+ self._rcontext[iri] = prefix
158
+
159
+ def iri_from_prefix(self, prefix: str) -> Optional[str]:
160
+ """
161
+ Returns the full IRI belonging to this prefix, without trailing "#"!
162
+
163
+ :param prefix: Prefix of the context entry
164
+ :return: The full IRI without trailing "#"
165
+ """
166
+ # if self.__is_iri(prefix):
167
+ if is_iri(prefix):
168
+ return prefix
169
+ if self._context.get(prefix) is not None:
170
+ return self._context.get(prefix).iri
171
+ else:
172
+ return None
173
+
174
+ def prefix_from_iri(self, iri: str) -> Optional[str]:
175
+ """
176
+ Get the IRI from a full context that has or has not a trailing "#". It first searches in the normal list
177
+ of contexted. If the iri is not found there, it looks in the list common (external) ontologies. If the
178
+ ontology is found there, this ontology is added to the list of known ontology and it's prefix is returned.
179
+ If nothing is found, None is returns
180
+
181
+ :param iri: The full IRI with or without trailing "#", or
182
+ :return: the prefix of this context element, or None, if not found
183
+ """
184
+ # if not self.__is_iri(iri):
185
+ if not is_iri(iri):
186
+ raise BaseError("String does not conform to IRI patter: " + iri)
187
+ if iri.endswith("#"):
188
+ iri = iri[:-1]
189
+ result = self._rcontext.get(iri)
190
+ if result is None:
191
+ entrylist = list(filter(lambda x: x[1].iri == iri, self.common_ontologies.items()))
192
+ if len(entrylist) == 1:
193
+ entry = entrylist[0]
194
+ self._context[entry[0]] = entry[1] # add to list of prefixes used
195
+ self._rcontext[entry[1].iri] = entry[0]
196
+ result = entry[0]
197
+ else:
198
+ tmp = iri.split("/")
199
+ if tmp[-1] == "v2":
200
+ #
201
+ # we have a knora ontology name "http://server/ontology/shortcode/shortname/v2"
202
+ self._context[tmp[-2]] = OntoIri(iri, True) # add to list of prefixes used
203
+ self._rcontext[iri] = tmp[-2]
204
+ else:
205
+ raise BaseError("Iri cannot be resolved to a well-known prefix!")
206
+ return result
207
+
208
+ def get_qualified_iri(self, val: Optional[str]) -> Optional[str]:
209
+ """
210
+ Given an IRI, its fully qualified name is returned.
211
+
212
+ Args:
213
+ val: The input IRI
214
+
215
+ Returns:
216
+ the fully qualified IRI
217
+ """
218
+ if not val:
219
+ return None
220
+ if is_iri(val):
221
+ return val
222
+ tmp = val.split(":")
223
+ if len(tmp) < 2:
224
+ raise BaseError("There is no separator to identify the prefix: " + val)
225
+ iri_info = self._context.get(tmp[0])
226
+ if iri_info is None:
227
+ entrylist = list(filter(lambda x: x[1].iri == tmp[0], self.common_ontologies.items()))
228
+ if len(entrylist) == 1:
229
+ entry = entrylist[0]
230
+ self._context[entry[0]] = entry[1] # add to list of prefixes used
231
+ self._rcontext[entry[1].iri] = entry[0]
232
+ iri_info = entry[1]
233
+ else:
234
+ raise BaseError("Ontology not known! Cannot generate fully qualified IRI")
235
+ if iri_info.hashtag:
236
+ return iri_info.iri + "#" + tmp[1]
237
+ else:
238
+ return iri_info.iri + tmp[1]
239
+
240
+ def get_prefixed_iri(self, iri: Optional[str]) -> Optional[str]:
241
+ """
242
+ We reduce a fully qualified IRI to a short one in the form "prefix:name"
243
+
244
+ :param iri: Fully qualified IRI
245
+ :return: Return short from of IRI ("prefix:name")
246
+ """
247
+ if iri is None:
248
+ return None
249
+
250
+ # check if the iri already has the form "prefix:name"
251
+ m = regex.match("([\\w-]+):([\\w-]+)", iri)
252
+ if m and m.span()[1] == len(iri):
253
+ return iri
254
+
255
+ if not is_iri(iri):
256
+ raise BaseError(f"The IRI '{iri}' does not conform to the IRI pattern.")
257
+
258
+ split_point = iri.find("#")
259
+ if split_point == -1:
260
+ split_point = iri.rfind("/")
261
+ onto_part = iri[: split_point + 1]
262
+ element = iri[split_point + 1 :]
263
+ else:
264
+ onto_part = iri[:split_point]
265
+ element = iri[split_point + 1 :]
266
+
267
+ prefix = self._rcontext.get(onto_part)
268
+ if prefix is None:
269
+ entry_list = list(filter(lambda x: x[1].iri == onto_part, self.common_ontologies.items()))
270
+ if len(entry_list) == 1:
271
+ entry = entry_list[0]
272
+ self._context[entry[0]] = entry[1] # add to list of prefixes used
273
+ self._rcontext[entry[1].iri] = entry[0]
274
+ prefix = entry[0]
275
+ else:
276
+ return None
277
+ return prefix + ":" + element
278
+
279
+ def reduce_iri(self, iri_str: str, onto_name: Optional[str] = None) -> str:
280
+ """
281
+ Reduces an IRI to the form that is used within the definition JSON file. It expects the context object to have
282
+ entries (prefixes) for all IRIs:
283
+ - if it's an external IRI and the ontology can be extracted as prefix it returns: "prefix:name"
284
+ - if it's in the same ontology, it returns: ":name"
285
+ - if it's a system ontology ("knora-api" or "salsah-gui") it returns: "name"
286
+ - if the IRI can't be reduced, it's returned as is
287
+
288
+ Args:
289
+ iri_str: the IRI that should be reduced
290
+ onto_name: the name of the ontology
291
+
292
+ Returns:
293
+ The reduced IRI if possible otherwise the fully qualified IRI
294
+ """
295
+ knora_api = self.prefix_from_iri("http://api.knora.org/ontology/knora-api/v2#")
296
+ salsah_gui = self.prefix_from_iri("http://api.knora.org/ontology/salsah-gui/v2#")
297
+
298
+ if is_iri(iri_str):
299
+ if self.get_prefixed_iri(iri_str):
300
+ iri_str = self.get_prefixed_iri(iri_str)
301
+ tmp = iri_str.split(":")
302
+ if tmp[0] == knora_api or tmp[0] == salsah_gui:
303
+ return tmp[1]
304
+ elif tmp[0] == onto_name:
305
+ return ":" + tmp[1]
306
+ else:
307
+ return iri_str
308
+
309
+ def toJsonObj(self) -> dict[str, str]:
310
+ """
311
+ Return a python object that can be jsonfied...
312
+ :return: Object to be jsonfied
313
+ """
314
+ return {prefix: oinfo.iri + "#" if oinfo.hashtag else oinfo.iri for prefix, oinfo in self._context.items()}
315
+
316
+ def get_externals_used(self) -> dict[str, str]:
317
+ exclude = ["rdf", "rdfs", "owl", "xsd", "knora-api", "salsah-gui"]
318
+ return {prefix: onto.iri for prefix, onto in self._context.items() if prefix not in exclude}
@@ -0,0 +1,241 @@
1
+ """
2
+ This module implements the handling (CRUD) of DSP groups.
3
+
4
+ CREATE:
5
+ * Instantiate a new object of the class Group with all required parameters
6
+ * Call the ``create``-method on the instance
7
+
8
+ READ:
9
+ * Instantiate a new object with ``iri``(IRI of group) given
10
+ * Call the ``read``-method on the instance
11
+ * Access the information that has been provided to the instance
12
+
13
+ UPDATE:
14
+ * You need an instance of an existing Project by reading an instance
15
+ * Change the attributes by assigning the new values
16
+ * Call the ``update```method on the instance
17
+
18
+ DELETE
19
+ * Instantiate a new objects with ``iri``(IRI of group) given, or use any instance that has the iri set
20
+ * Call the ``delete``-method on the instance
21
+
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ from typing import Any
27
+ from typing import Optional
28
+ from typing import Union
29
+ from urllib.parse import quote_plus
30
+
31
+ from dsp_tools.clients.connection import Connection
32
+ from dsp_tools.commands.get.legacy_models.model import Model
33
+ from dsp_tools.commands.get.legacy_models.project import Project
34
+ from dsp_tools.error.exceptions import BaseError
35
+ from dsp_tools.legacy_models.langstring import LangString
36
+
37
+
38
+ class Group(Model):
39
+ """
40
+ This class represents a DSP group
41
+
42
+ Attributes
43
+ ----------
44
+
45
+ con : Connection
46
+ A connection instance to a DSP server
47
+
48
+ iri : str
49
+ IRI of the group [get only, cannot be modified after creation of instance]
50
+
51
+ name : str
52
+ Name of the group
53
+
54
+ descriptions : LangString
55
+ Group descriptions in a given language (Languages.EN, Languages.DE, Languages.FR, Languages.IT, Languages.RM).
56
+
57
+ project : str | project
58
+ either the IRI of a project [get only, cannot be modified after creation of instance]
59
+ or an valid Project instance
60
+
61
+ selfjoin : boolean
62
+ A flag indicating if selfjoin is allowed in this group
63
+
64
+ status : boolean
65
+ A flag indicating if the group is active (True) or inactive/makred deleted (False)
66
+
67
+ """
68
+
69
+ PROJECT_MEMBER_GROUP: str = "http://www.knora.org/ontology/knora-admin#ProjectMember"
70
+ PROJECT_ADMIN_GROUP: str = "http://www.knora.org/ontology/knora-admin#ProjectAdmin"
71
+ ROUTE: str = "/admin/groups"
72
+ ROUTE_SLASH: str = ROUTE + "/"
73
+
74
+ _iri: Optional[str]
75
+ _name: str | None
76
+ _descriptions: LangString
77
+ _project: str
78
+ _selfjoin: bool
79
+ _status: bool
80
+
81
+ def __init__(
82
+ self,
83
+ con: Connection,
84
+ iri: Optional[str] = None,
85
+ name: Optional[str] = None,
86
+ descriptions: Optional[LangString] = None,
87
+ project: Optional[Union[str, Project]] = None,
88
+ selfjoin: Optional[bool] = None,
89
+ status: Optional[bool] = None,
90
+ ) -> None:
91
+ super().__init__(con)
92
+ self._iri = iri
93
+ self._name = str(name) if name is not None else None
94
+ self._descriptions = LangString(descriptions)
95
+ if project is not None and isinstance(project, Project):
96
+ self._project = project.iri
97
+ else:
98
+ self._project = str(project) if project is not None else None
99
+ self._selfjoin = bool(selfjoin) if selfjoin is not None else None
100
+ self._status = bool(status) if status is not None else None
101
+
102
+ @property
103
+ def iri(self) -> Optional[str]:
104
+ return self._iri
105
+
106
+ @property
107
+ def name(self) -> Optional[str]:
108
+ return self._name
109
+
110
+ @name.setter
111
+ def name(self, value: str) -> None:
112
+ self._name = value
113
+ self._changed.add("name")
114
+
115
+ @property
116
+ def descriptions(self) -> LangString:
117
+ return self._descriptions
118
+
119
+ @descriptions.setter
120
+ def descriptions(self, value: Optional[LangString]) -> None:
121
+ self._descriptions = LangString(value)
122
+ self._changed.add("descriptions")
123
+
124
+ @property
125
+ def project(self) -> str:
126
+ return self._project
127
+
128
+ @property
129
+ def selfjoin(self) -> bool:
130
+ return self._selfjoin
131
+
132
+ @selfjoin.setter
133
+ def selfjoin(self, value: bool) -> None:
134
+ self._selfjoin = value
135
+ self._changed.add("selfjoin")
136
+
137
+ @property
138
+ def status(self) -> bool:
139
+ return self._status
140
+
141
+ @status.setter
142
+ def status(self, value: bool) -> None:
143
+ self._status = value
144
+ self._changed.add("status")
145
+
146
+ @classmethod
147
+ def fromJsonObj(cls, con: Connection, json_obj: Any) -> Group:
148
+ group_id = json_obj.get("id")
149
+ if group_id is None:
150
+ raise BaseError('Group "id" is missing')
151
+ name = json_obj.get("name")
152
+ if name is None:
153
+ raise BaseError('Group "name" is missing')
154
+ descriptions = LangString.fromJsonObj(json_obj.get("descriptions"))
155
+ tmp = json_obj.get("project")
156
+ if tmp is None:
157
+ raise BaseError('Group "project" is missing')
158
+ project = tmp.get("id")
159
+ if project is None:
160
+ raise BaseError('Group "project" has no "id"')
161
+ selfjoin = json_obj.get("selfjoin")
162
+ if selfjoin is None:
163
+ raise BaseError("selfjoin is missing")
164
+ status = json_obj.get("status")
165
+ if status is None:
166
+ raise BaseError("Status is missing")
167
+ return cls(
168
+ con=con,
169
+ name=name,
170
+ iri=group_id,
171
+ descriptions=descriptions,
172
+ project=project,
173
+ selfjoin=selfjoin,
174
+ status=status,
175
+ )
176
+
177
+ def create(self) -> Group:
178
+ jsonobj = self._toJsonObj_create()
179
+ result = self._con.post(Group.ROUTE, jsonobj)
180
+ return Group.fromJsonObj(self._con, result["group"])
181
+
182
+ def _toJsonObj_create(self):
183
+ tmp = {}
184
+ if self._name is None:
185
+ raise BaseError("There must be a valid name!")
186
+ tmp["name"] = self._name
187
+ if not self._descriptions.isEmpty():
188
+ tmp["descriptions"] = self._descriptions.toJsonObj()
189
+ if self._project is None:
190
+ raise BaseError("There must be a valid project!")
191
+ tmp["project"] = self._project
192
+ if self._selfjoin is None:
193
+ raise BaseError("There must be a valid value for selfjoin!")
194
+ tmp["selfjoin"] = self._selfjoin
195
+ if self._status is None:
196
+ raise BaseError("There must be a valid value for status!")
197
+ tmp["status"] = self._status
198
+ return tmp
199
+
200
+ def read(self) -> Group:
201
+ result = self._con.get(Group.ROUTE_SLASH + quote_plus(self._iri))
202
+ return Group.fromJsonObj(self._con, result["group"])
203
+
204
+ def update(self) -> Optional[Group]:
205
+ updated_group = None
206
+ jsonobj = self._toJsonObj_update()
207
+ if jsonobj:
208
+ result = self._con.put(Group.ROUTE_SLASH + quote_plus(self._iri), jsonobj)
209
+ updated_group = Group.fromJsonObj(self._con, result["group"])
210
+ if self._status is not None and "status" in self._changed:
211
+ jsonobj = {"status": self._status}
212
+ result = self._con.put(Group.ROUTE_SLASH + quote_plus(self._iri) + "/status", jsonobj)
213
+ updated_group = Group.fromJsonObj(self._con, result["group"])
214
+ return updated_group
215
+
216
+ def _toJsonObj_update(self) -> dict[str, Any]:
217
+ tmp = {}
218
+ if self._name is not None and "name" in self._changed:
219
+ tmp["name"] = self._name
220
+ if not self._descriptions.isEmpty() and "descriptions" in self._changed:
221
+ tmp["descriptions"] = self._descriptions.toJsonObj()
222
+ if self._selfjoin is not None and "selfjoin" in self._changed:
223
+ tmp["selfjoin"] = self._selfjoin
224
+ return tmp
225
+
226
+ @staticmethod
227
+ def getAllGroups(con: Connection) -> list[Group]:
228
+ result = con.get(Group.ROUTE)
229
+ return [Group.fromJsonObj(con, group_item) for group_item in result["groups"]]
230
+
231
+ @staticmethod
232
+ def getAllGroupsForProject(con: Connection, proj_iri: str) -> list[Group]:
233
+ return [g for g in Group.getAllGroups(con) if g.project == proj_iri]
234
+
235
+ def createDefinitionFileObj(self) -> dict[str, Any]:
236
+ return {
237
+ "name": self.name,
238
+ "descriptions": self.descriptions.createDefinitionFileObj(),
239
+ "selfjoin": self.selfjoin,
240
+ "status": self.status,
241
+ }
@@ -0,0 +1,47 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from enum import Enum
5
+ from enum import unique
6
+ from typing import Optional
7
+
8
+
9
+ @dataclass(frozen=True)
10
+ class OntoIri:
11
+ """
12
+ Holds an ontology IRI
13
+
14
+ Attributes:
15
+ iri: the ontology IRI
16
+ hashtag: True if "#" is used to separate elements, False if element name is appended after "/"
17
+ """
18
+
19
+ iri: str
20
+ hashtag: bool
21
+
22
+
23
+ ContextType = dict[str, OntoIri]
24
+
25
+
26
+ @unique
27
+ class Cardinality(Enum):
28
+ C_1 = "1"
29
+ C_0_1 = "0-1"
30
+ C_1_n = "1-n"
31
+ C_0_n = "0-n"
32
+
33
+
34
+ class WithId:
35
+ """
36
+ Class helper to get json-ld "@id" thingies
37
+ """
38
+
39
+ _tmp: str = None
40
+
41
+ def __init__(self, obj: Optional[dict[str, str]]):
42
+ if obj is None:
43
+ return
44
+ self._tmp = obj.get("@id")
45
+
46
+ def to_string(self) -> Optional[str]:
47
+ return self._tmp