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,27 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+
5
+ from dsp_tools.commands.xmlupload.models.permission import Permissions
6
+ from dsp_tools.commands.xmlupload.models.processed.file_values import ProcessedFileValue
7
+ from dsp_tools.commands.xmlupload.models.processed.file_values import ProcessedIIIFUri
8
+ from dsp_tools.commands.xmlupload.models.processed.values import ProcessedValue
9
+ from dsp_tools.legacy_models.datetimestamp import DateTimeStamp
10
+
11
+
12
+ @dataclass
13
+ class ProcessedResource:
14
+ res_id: str
15
+ type_iri: str
16
+ label: str
17
+ permissions: Permissions | None
18
+ values: list[ProcessedValue]
19
+ file_value: ProcessedFileValue | None = None
20
+ iiif_uri: ProcessedIIIFUri | None = None
21
+ migration_metadata: MigrationMetadata | None = None
22
+
23
+
24
+ @dataclass
25
+ class MigrationMetadata:
26
+ iri_str: str | None
27
+ creation_date: DateTimeStamp | None
@@ -0,0 +1,101 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+ from typing import Union
5
+
6
+ from dsp_tools.commands.xmlupload.models.formatted_text_value import FormattedTextValue
7
+ from dsp_tools.commands.xmlupload.models.permission import Permissions
8
+ from dsp_tools.utils.data_formats.date_util import Date
9
+
10
+
11
+ @dataclass
12
+ class IntervalFloats:
13
+ start: float
14
+ end: float
15
+
16
+
17
+ type ProcessedValueTypes = Union[bool, str, float, int, FormattedTextValue, Date, IntervalFloats]
18
+
19
+
20
+ @dataclass
21
+ class ProcessedValue:
22
+ value: ProcessedValueTypes
23
+ prop_iri: str
24
+ comment: str | None
25
+ permissions: Permissions | None
26
+
27
+
28
+ @dataclass
29
+ class ProcessedBoolean(ProcessedValue):
30
+ value: bool
31
+
32
+
33
+ @dataclass
34
+ class ProcessedColor(ProcessedValue):
35
+ value: str
36
+
37
+
38
+ @dataclass
39
+ class ProcessedDate(ProcessedValue):
40
+ value: Date
41
+
42
+
43
+ @dataclass
44
+ class ProcessedDecimal(ProcessedValue):
45
+ value: float
46
+
47
+
48
+ @dataclass
49
+ class ProcessedGeoname(ProcessedValue):
50
+ value: str
51
+
52
+
53
+ @dataclass
54
+ class ProcessedGeometry(ProcessedValue):
55
+ value: str
56
+
57
+
58
+ @dataclass
59
+ class ProcessedInt(ProcessedValue):
60
+ value: int
61
+
62
+
63
+ @dataclass
64
+ class ProcessedInterval(ProcessedValue):
65
+ value: IntervalFloats
66
+
67
+
68
+ @dataclass
69
+ class ProcessedLink(ProcessedValue):
70
+ value: str
71
+ value_uuid: str
72
+
73
+ def __post_init__(self) -> None:
74
+ self.prop_iri = f"{self.prop_iri}Value"
75
+
76
+
77
+ @dataclass
78
+ class ProcessedList(ProcessedValue):
79
+ value: str
80
+
81
+
82
+ @dataclass
83
+ class ProcessedSimpleText(ProcessedValue):
84
+ value: str
85
+
86
+
87
+ @dataclass
88
+ class ProcessedRichtext(ProcessedValue):
89
+ value: FormattedTextValue
90
+ resource_references: set[str]
91
+ value_uuid: str
92
+
93
+
94
+ @dataclass
95
+ class ProcessedTime(ProcessedValue):
96
+ value: str
97
+
98
+
99
+ @dataclass
100
+ class ProcessedUri(ProcessedValue):
101
+ value: str
@@ -0,0 +1,26 @@
1
+ from __future__ import annotations
2
+
3
+ from dataclasses import dataclass
4
+
5
+ from rdflib import URIRef
6
+
7
+
8
+ @dataclass
9
+ class RDFPropTypeInfo:
10
+ knora_type: URIRef
11
+ knora_prop: URIRef
12
+ xsd_type: URIRef | None = None
13
+
14
+
15
+ @dataclass
16
+ class AbstractFileValue:
17
+ value: str
18
+ metadata: FileValueMetadata
19
+
20
+
21
+ @dataclass
22
+ class FileValueMetadata:
23
+ license_iri: str
24
+ copyright_holder: str
25
+ authorships: list[str]
26
+ permissions: str | None
@@ -0,0 +1,14 @@
1
+ from dataclasses import dataclass
2
+
3
+ from dsp_tools.clients.legal_info_client import LegalInfoClient
4
+ from dsp_tools.commands.xmlupload.models.ingest import AssetClient
5
+ from dsp_tools.commands.xmlupload.prepare_xml_input.list_client import ListClient
6
+
7
+
8
+ @dataclass(frozen=True)
9
+ class UploadClients:
10
+ """Holds all the clients needed for the upload process."""
11
+
12
+ asset_client: AssetClient
13
+ list_client: ListClient
14
+ legal_info_client: LegalInfoClient
@@ -0,0 +1,20 @@
1
+ from dataclasses import dataclass
2
+ from dataclasses import field
3
+
4
+ from dsp_tools.commands.xmlupload.iri_resolver import IriResolver
5
+ from dsp_tools.commands.xmlupload.models.processed.res import ProcessedResource
6
+ from dsp_tools.commands.xmlupload.stash.stash_models import Stash
7
+ from dsp_tools.commands.xmlupload.upload_config import UploadConfig
8
+
9
+
10
+ @dataclass
11
+ class UploadState:
12
+ """
13
+ Save the state of an xmlupload, so that after an interruption, it can be resumed.
14
+ """
15
+
16
+ pending_resources: list[ProcessedResource]
17
+ pending_stash: Stash | None
18
+ config: UploadConfig
19
+ failed_uploads: list[str] = field(default_factory=list)
20
+ iri_resolver: IriResolver = field(default_factory=IriResolver)
@@ -0,0 +1,55 @@
1
+ from __future__ import annotations
2
+
3
+ import base64
4
+ import uuid
5
+
6
+ import regex
7
+
8
+ from dsp_tools.error.exceptions import BaseError
9
+
10
+
11
+ def convert_ark_v0_to_resource_iri(ark: str) -> str:
12
+ """
13
+ Converts an ARK URL from salsah.org (ARK version 0) of the form ark:/72163/080c-779b9990a0c3f-6e
14
+ to a DSP resource IRI of the form http://rdfh.ch/080C/Ef9heHjPWDS7dMR_gGax2Q
15
+
16
+ This method is needed for the migration of projects from salsah.org to DSP.
17
+ Resources need to be created with an existing ARK,
18
+ so the IRI needs to be extracted from that ARK
19
+ in order for the ARK URL to be still valid after the migration.
20
+
21
+ Args:
22
+ ark: an ARK version 0 of the form ark:/72163/080c-779b9990a0c3f-6e,
23
+ '72163' being the Name Assigning Authority number,
24
+ '080c' being the project shortcode,
25
+ '779b9990a0c3f' being an ID derived from the object's Salsah ID and
26
+ '6e' being check digits
27
+
28
+ Raises:
29
+ BaseError: if the ARK is invalid
30
+
31
+ Returns:
32
+ Resource IRI (str) of the form http://rdfh.ch/080C/Ef9heHjPWDS7dMR_gGax2Q
33
+ """
34
+ # create the DaSCH namespace to create version 5 UUIDs
35
+ generic_namespace_url = uuid.NAMESPACE_URL
36
+ dasch_uuid_ns = uuid.uuid5(generic_namespace_url, "https://dasch.swiss") # cace8b00-717e-50d5-bcb9-486f39d733a2
37
+
38
+ # get the salsah resource ID from the ARK and convert it to a UUID version 5 (base64 encoded)
39
+ if ark.count("-") != 2:
40
+ raise BaseError(f"Error while converting ARK '{ark}'. The ARK seems to be invalid")
41
+ project_id, resource_id, _ = ark.split("-")
42
+ _, project_id = project_id.rsplit("/", 1)
43
+ project_id = project_id.upper()
44
+ if not regex.match("^[0-9a-fA-F]{4}$", project_id):
45
+ raise BaseError(f"Error while converting ARK '{ark}'. Invalid project shortcode '{project_id}'")
46
+ if not regex.match("^[0-9A-Za-z]+$", resource_id):
47
+ raise BaseError(f"Error while converting ARK '{ark}'. Invalid Salsah ID '{resource_id}'")
48
+
49
+ # make a UUID v5 from the namespace created above (which is a UUID itself) and the resource ID
50
+ # and encode it to base64
51
+ dsp_uuid = base64.urlsafe_b64encode(uuid.uuid5(dasch_uuid_ns, resource_id).bytes).decode("utf-8")
52
+ dsp_uuid = dsp_uuid[:-2]
53
+
54
+ # use the new UUID to create the resource IRI
55
+ return f"http://rdfh.ch/{project_id}/{dsp_uuid}"
@@ -0,0 +1,252 @@
1
+ from typing import cast
2
+ from uuid import uuid4
3
+
4
+ from loguru import logger
5
+ from tqdm import tqdm
6
+
7
+ from dsp_tools.commands.xmlupload.models.lookup_models import XmlReferenceLookups
8
+ from dsp_tools.commands.xmlupload.models.permission import Permissions
9
+ from dsp_tools.commands.xmlupload.models.processed.file_values import ProcessedFileMetadata
10
+ from dsp_tools.commands.xmlupload.models.processed.file_values import ProcessedFileValue
11
+ from dsp_tools.commands.xmlupload.models.processed.file_values import ProcessedIIIFUri
12
+ from dsp_tools.commands.xmlupload.models.processed.res import MigrationMetadata
13
+ from dsp_tools.commands.xmlupload.models.processed.res import ProcessedResource
14
+ from dsp_tools.commands.xmlupload.models.processed.values import ProcessedBoolean
15
+ from dsp_tools.commands.xmlupload.models.processed.values import ProcessedColor
16
+ from dsp_tools.commands.xmlupload.models.processed.values import ProcessedDate
17
+ from dsp_tools.commands.xmlupload.models.processed.values import ProcessedDecimal
18
+ from dsp_tools.commands.xmlupload.models.processed.values import ProcessedGeometry
19
+ from dsp_tools.commands.xmlupload.models.processed.values import ProcessedGeoname
20
+ from dsp_tools.commands.xmlupload.models.processed.values import ProcessedInt
21
+ from dsp_tools.commands.xmlupload.models.processed.values import ProcessedInterval
22
+ from dsp_tools.commands.xmlupload.models.processed.values import ProcessedLink
23
+ from dsp_tools.commands.xmlupload.models.processed.values import ProcessedList
24
+ from dsp_tools.commands.xmlupload.models.processed.values import ProcessedRichtext
25
+ from dsp_tools.commands.xmlupload.models.processed.values import ProcessedSimpleText
26
+ from dsp_tools.commands.xmlupload.models.processed.values import ProcessedTime
27
+ from dsp_tools.commands.xmlupload.models.processed.values import ProcessedUri
28
+ from dsp_tools.commands.xmlupload.models.processed.values import ProcessedValue
29
+ from dsp_tools.commands.xmlupload.prepare_xml_input.ark2iri import convert_ark_v0_to_resource_iri
30
+ from dsp_tools.commands.xmlupload.prepare_xml_input.transform_input_values import TypeTransformerMapper
31
+ from dsp_tools.commands.xmlupload.prepare_xml_input.transform_input_values import assert_is_string
32
+ from dsp_tools.commands.xmlupload.prepare_xml_input.transform_input_values import assert_is_tuple
33
+ from dsp_tools.commands.xmlupload.prepare_xml_input.transform_input_values import transform_boolean
34
+ from dsp_tools.commands.xmlupload.prepare_xml_input.transform_input_values import transform_date
35
+ from dsp_tools.commands.xmlupload.prepare_xml_input.transform_input_values import transform_decimal
36
+ from dsp_tools.commands.xmlupload.prepare_xml_input.transform_input_values import transform_geometry
37
+ from dsp_tools.commands.xmlupload.prepare_xml_input.transform_input_values import transform_integer
38
+ from dsp_tools.commands.xmlupload.prepare_xml_input.transform_input_values import transform_interval
39
+ from dsp_tools.commands.xmlupload.prepare_xml_input.transform_input_values import transform_richtext
40
+ from dsp_tools.commands.xmlupload.prepare_xml_input.transform_input_values import transform_simpletext
41
+ from dsp_tools.commands.xmlupload.richtext_id2iri import find_internal_ids
42
+ from dsp_tools.error.exceptions import XmlUploadAuthorshipsNotFoundError
43
+ from dsp_tools.error.exceptions import XmlUploadListNodeNotFoundError
44
+ from dsp_tools.error.exceptions import XmlUploadPermissionsNotFoundError
45
+ from dsp_tools.legacy_models.datetimestamp import DateTimeStamp
46
+ from dsp_tools.utils.xml_parsing.models.parsed_resource import KnoraValueType
47
+ from dsp_tools.utils.xml_parsing.models.parsed_resource import ParsedFileValue
48
+ from dsp_tools.utils.xml_parsing.models.parsed_resource import ParsedFileValueMetadata
49
+ from dsp_tools.utils.xml_parsing.models.parsed_resource import ParsedMigrationMetadata
50
+ from dsp_tools.utils.xml_parsing.models.parsed_resource import ParsedResource
51
+ from dsp_tools.utils.xml_parsing.models.parsed_resource import ParsedValue
52
+
53
+ TYPE_TRANSFORMER_MAPPER: dict[KnoraValueType, TypeTransformerMapper] = {
54
+ KnoraValueType.BOOLEAN_VALUE: TypeTransformerMapper(ProcessedBoolean, transform_boolean),
55
+ KnoraValueType.COLOR_VALUE: TypeTransformerMapper(ProcessedColor, assert_is_string),
56
+ KnoraValueType.DECIMAL_VALUE: TypeTransformerMapper(ProcessedDecimal, transform_decimal),
57
+ KnoraValueType.DATE_VALUE: TypeTransformerMapper(ProcessedDate, transform_date),
58
+ KnoraValueType.GEOM_VALUE: TypeTransformerMapper(ProcessedGeometry, transform_geometry),
59
+ KnoraValueType.GEONAME_VALUE: TypeTransformerMapper(ProcessedGeoname, assert_is_string),
60
+ KnoraValueType.INT_VALUE: TypeTransformerMapper(ProcessedInt, transform_integer),
61
+ KnoraValueType.INTERVAL_VALUE: TypeTransformerMapper(ProcessedInterval, transform_interval),
62
+ KnoraValueType.TIME_VALUE: TypeTransformerMapper(ProcessedTime, assert_is_string),
63
+ KnoraValueType.SIMPLETEXT_VALUE: TypeTransformerMapper(ProcessedSimpleText, transform_simpletext),
64
+ KnoraValueType.URI_VALUE: TypeTransformerMapper(ProcessedUri, assert_is_string),
65
+ }
66
+
67
+
68
+ def get_processed_resources(
69
+ resources: list[ParsedResource], lookups: XmlReferenceLookups, is_on_prod_like_server: bool
70
+ ) -> list[ProcessedResource]:
71
+ logger.debug("Transform ParsedResource into ProcessedResource")
72
+ progress_bar = tqdm(resources, desc="Preparing data for upload", dynamic_ncols=True)
73
+ return [_get_one_resource(res, lookups, is_on_prod_like_server) for res in progress_bar]
74
+
75
+
76
+ def _get_one_resource(
77
+ resource: ParsedResource, lookups: XmlReferenceLookups, is_on_prod_like_server: bool
78
+ ) -> ProcessedResource:
79
+ permissions = _resolve_permission(resource.permissions_id, lookups.permissions)
80
+ values = [_get_one_processed_value(val, lookups) for val in resource.values]
81
+ migration_metadata = None
82
+ file_val, iiif_uri = _resolve_file_value(resource, lookups, is_on_prod_like_server)
83
+ if resource.migration_metadata:
84
+ migration_metadata = _get_resource_migration_metadata(resource.migration_metadata)
85
+ return ProcessedResource(
86
+ res_id=resource.res_id,
87
+ type_iri=resource.res_type,
88
+ label=resource.label,
89
+ permissions=permissions,
90
+ values=values,
91
+ file_value=file_val,
92
+ iiif_uri=iiif_uri,
93
+ migration_metadata=migration_metadata,
94
+ )
95
+
96
+
97
+ def _get_resource_migration_metadata(metadata: ParsedMigrationMetadata) -> MigrationMetadata:
98
+ res_iri = metadata.iri
99
+ # ARK takes precedence over the IRI,
100
+ # but must be transformed into an IRI as it is only for external reference and not consistent with a DB IRI
101
+ if metadata.ark:
102
+ res_iri = convert_ark_v0_to_resource_iri(metadata.ark)
103
+ date = DateTimeStamp(metadata.creation_date) if metadata.creation_date else None
104
+ return MigrationMetadata(res_iri, date)
105
+
106
+
107
+ def _resolve_file_value(
108
+ resource: ParsedResource, lookups: XmlReferenceLookups, is_on_prod_like_server: bool
109
+ ) -> tuple[None | ProcessedFileValue, None | ProcessedIIIFUri]:
110
+ file_val, iiif_uri = None, None
111
+ if not resource.file_value:
112
+ return file_val, iiif_uri
113
+
114
+ if is_on_prod_like_server:
115
+ metadata = _get_file_metadata(resource.file_value.metadata, lookups)
116
+ else:
117
+ metadata = _get_file_metadata_for_test_environments(resource.file_value.metadata, lookups)
118
+ if resource.file_value.value_type == KnoraValueType.STILL_IMAGE_IIIF:
119
+ iiif_uri = _get_iiif_uri_value(resource.file_value, metadata)
120
+ else:
121
+ file_val = _get_file_value(
122
+ val=resource.file_value, metadata=metadata, res_id=resource.res_id, res_label=resource.label
123
+ )
124
+ return file_val, iiif_uri
125
+
126
+
127
+ def _get_file_value(
128
+ val: ParsedFileValue, metadata: ProcessedFileMetadata, res_id: str, res_label: str
129
+ ) -> ProcessedFileValue:
130
+ file_type = cast(KnoraValueType, val.value_type)
131
+ file_val = assert_is_string(val.value)
132
+ return ProcessedFileValue(
133
+ value=file_val,
134
+ file_type=file_type,
135
+ metadata=metadata,
136
+ res_id=res_id,
137
+ res_label=res_label,
138
+ )
139
+
140
+
141
+ def _get_iiif_uri_value(iiif_uri: ParsedFileValue, metadata: ProcessedFileMetadata) -> ProcessedIIIFUri:
142
+ file_val = assert_is_string(iiif_uri.value)
143
+ return ProcessedIIIFUri(file_val, metadata)
144
+
145
+
146
+ def _get_file_metadata(file_metadata: ParsedFileValueMetadata, lookups: XmlReferenceLookups) -> ProcessedFileMetadata:
147
+ license_iri = assert_is_string(file_metadata.license_iri)
148
+ copyright_holder = assert_is_string(file_metadata.copyright_holder)
149
+ auth_id = assert_is_string(file_metadata.authorship_id)
150
+ authorships = _resolve_authorship(auth_id, lookups.authorships)
151
+ permissions = _resolve_permission(file_metadata.permissions_id, lookups.permissions)
152
+ return ProcessedFileMetadata(
153
+ license_iri=license_iri,
154
+ copyright_holder=copyright_holder,
155
+ authorships=authorships,
156
+ permissions=permissions,
157
+ )
158
+
159
+
160
+ def _get_file_metadata_for_test_environments(
161
+ metadata: ParsedFileValueMetadata, lookups: XmlReferenceLookups
162
+ ) -> ProcessedFileMetadata:
163
+ lic_iri = metadata.license_iri or "http://rdfh.ch/licenses/unknown"
164
+ copy_right = metadata.copyright_holder if metadata.copyright_holder else "DUMMY"
165
+ if not metadata.authorship_id:
166
+ authorship = ["DUMMY"]
167
+ else:
168
+ authorship = _resolve_authorship(metadata.authorship_id, lookups.authorships)
169
+ permissions = _resolve_permission(metadata.permissions_id, lookups.permissions)
170
+ return ProcessedFileMetadata(
171
+ license_iri=lic_iri,
172
+ copyright_holder=copy_right,
173
+ authorships=authorship,
174
+ permissions=permissions,
175
+ )
176
+
177
+
178
+ def _resolve_authorship(authorship_id: str, lookup: dict[str, list[str]]) -> list[str]:
179
+ if not (found := lookup.get(authorship_id)):
180
+ raise XmlUploadAuthorshipsNotFoundError(f"Could not find authorships for value: {authorship_id}")
181
+ return found
182
+
183
+
184
+ def _get_one_processed_value(val: ParsedValue, lookups: XmlReferenceLookups) -> ProcessedValue:
185
+ match val.value_type:
186
+ case KnoraValueType.LIST_VALUE:
187
+ return _get_list_value(val, lookups)
188
+ case KnoraValueType.LINK_VALUE:
189
+ return _get_link_value(val, lookups)
190
+ case KnoraValueType.RICHTEXT_VALUE:
191
+ return _get_richtext_value(val, lookups)
192
+ case _ as val_type:
193
+ transformation_mapper = TYPE_TRANSFORMER_MAPPER[val_type]
194
+ return _get_generic_value(val=val, lookups=lookups, transformation_mapper=transformation_mapper)
195
+
196
+
197
+ def _get_generic_value(
198
+ val: ParsedValue, lookups: XmlReferenceLookups, transformation_mapper: TypeTransformerMapper
199
+ ) -> ProcessedValue:
200
+ transformed_value = transformation_mapper.val_transformer(val.value)
201
+ permission_val = _resolve_permission(val.permissions_id, lookups.permissions)
202
+ return transformation_mapper.val_type(transformed_value, val.prop_name, val.comment, permission_val)
203
+
204
+
205
+ def _get_link_value(val: ParsedValue, lookups: XmlReferenceLookups) -> ProcessedValue:
206
+ transformed_value = assert_is_string(val.value)
207
+ permission_val = _resolve_permission(val.permissions_id, lookups.permissions)
208
+ link_val: ProcessedValue = ProcessedLink(
209
+ value=transformed_value,
210
+ prop_iri=val.prop_name,
211
+ comment=val.comment,
212
+ permissions=permission_val,
213
+ value_uuid=str(uuid4()),
214
+ )
215
+ return link_val
216
+
217
+
218
+ def _get_list_value(val: ParsedValue, lookups: XmlReferenceLookups) -> ProcessedValue:
219
+ tuple_val = assert_is_tuple(val.value)
220
+ if not (list_iri := lookups.listnodes.get(tuple_val)):
221
+ raise XmlUploadListNodeNotFoundError(f"Could not find list IRI for value: {tuple_val}")
222
+ permission_val = _resolve_permission(val.permissions_id, lookups.permissions)
223
+ list_val: ProcessedValue = ProcessedList(
224
+ value=list_iri,
225
+ prop_iri=val.prop_name,
226
+ comment=val.comment,
227
+ permissions=permission_val,
228
+ )
229
+ return list_val
230
+
231
+
232
+ def _get_richtext_value(val: ParsedValue, lookups: XmlReferenceLookups) -> ProcessedValue:
233
+ transformed_value = transform_richtext(val.value)
234
+ permission_val = _resolve_permission(val.permissions_id, lookups.permissions)
235
+ richtext: ProcessedValue = ProcessedRichtext(
236
+ value=transformed_value,
237
+ prop_iri=val.prop_name,
238
+ comment=val.comment,
239
+ permissions=permission_val,
240
+ resource_references=find_internal_ids(transformed_value.xmlstr),
241
+ value_uuid=str(uuid4()),
242
+ )
243
+ return richtext
244
+
245
+
246
+ def _resolve_permission(permissions: str | None, permissions_lookup: dict[str, Permissions]) -> Permissions | None:
247
+ """Resolve the permission into a string that can be sent to the API."""
248
+ if permissions:
249
+ if not (per := permissions_lookup.get(permissions)):
250
+ raise XmlUploadPermissionsNotFoundError(f"Could not find permissions for value: {permissions}")
251
+ return per
252
+ return None
@@ -0,0 +1,50 @@
1
+ from dataclasses import dataclass
2
+
3
+ import requests
4
+
5
+ from dsp_tools.commands.xmlupload.models.input_problems import IIIFUriProblem
6
+ from dsp_tools.utils.data_formats.uri_util import is_iiif_uri
7
+
8
+
9
+ @dataclass(frozen=True)
10
+ class IIIFUriValidator:
11
+ """Client handling communication with external IIIF-servers to do a health check."""
12
+
13
+ def validate_one_uri(self, uri: str) -> IIIFUriProblem | None:
14
+ """Check if the IIIF-server is reachable. If not, it returns information for error message."""
15
+ regex_has_passed = is_iiif_uri(uri)
16
+ response = self._make_network_call(uri)
17
+ if isinstance(response, Exception):
18
+ return IIIFUriProblem(
19
+ uri=uri,
20
+ regex_has_passed=regex_has_passed,
21
+ raised_exception_name=response.__class__.__name__,
22
+ )
23
+ match response.ok, regex_has_passed:
24
+ case True, True:
25
+ return None
26
+ case _:
27
+ return IIIFUriProblem(
28
+ uri=uri,
29
+ regex_has_passed=regex_has_passed,
30
+ status_code=response.status_code,
31
+ )
32
+
33
+ def _make_network_call(self, uri: str) -> requests.Response | Exception:
34
+ info_json_uri = self._make_info_json_uri(uri)
35
+ try:
36
+ return requests.get(
37
+ url=info_json_uri,
38
+ headers={"Content-Type": "application/ld+json"},
39
+ timeout=10,
40
+ )
41
+ except Exception as e: # noqa: BLE001 (blind-except)
42
+ return e
43
+
44
+ def _make_info_json_uri(self, uri: str) -> str:
45
+ splt = uri.split("/")
46
+ if len(splt) < 5:
47
+ info_uri = uri.rstrip("/")
48
+ else:
49
+ info_uri = "/".join(splt[:-4])
50
+ return f"{info_uri}/info.json"
@@ -0,0 +1,120 @@
1
+ from __future__ import annotations
2
+
3
+ from collections.abc import Iterable
4
+ from dataclasses import dataclass
5
+ from dataclasses import field
6
+ from typing import Any
7
+ from typing import Protocol
8
+ from urllib.parse import quote_plus
9
+
10
+ from loguru import logger
11
+
12
+ from dsp_tools.clients.connection import Connection
13
+
14
+
15
+ @dataclass(frozen=True)
16
+ class ListNode:
17
+ """Information about a list node."""
18
+
19
+ node_iri: str
20
+ node_name: str
21
+
22
+
23
+ @dataclass(frozen=True)
24
+ class List:
25
+ """Information about a list."""
26
+
27
+ root_iri: str
28
+ list_name: str
29
+ nodes: list[ListNode]
30
+
31
+
32
+ @dataclass(frozen=True)
33
+ class ProjectLists:
34
+ """Information about the lists of a project."""
35
+
36
+ lists: list[List]
37
+
38
+
39
+ class ListClient(Protocol):
40
+ """Interface (protocol) for list-related requests to the DSP-API."""
41
+
42
+ con: Connection
43
+ project_iri: str
44
+
45
+ def get_list_node_id_to_iri_lookup(self) -> dict[tuple[str, str], str]:
46
+ """Get a lookup of list node names to IRIs."""
47
+
48
+
49
+ @dataclass
50
+ class ListClientLive:
51
+ """Client handling list-related requests to the DSP-API."""
52
+
53
+ con: Connection
54
+ project_iri: str
55
+ list_info: ProjectLists | None = field(init=False, default=None)
56
+
57
+ def get_list_node_id_to_iri_lookup(self) -> dict[tuple[str, str], str]:
58
+ """
59
+ Get a mapping of list node IDs to their respective IRIs.
60
+ A list node ID is structured as follows:
61
+ `("<list name>", "<node name>") where the list name is the node name of the root node.
62
+
63
+ Returns:
64
+ The mapping of list node IDs to IRIs.
65
+ """
66
+ if not self.list_info:
67
+ self.list_info = _get_list_info_from_server(self.con, self.project_iri)
68
+ lookup = dict(_get_node_tuples(self.list_info.lists))
69
+ # Enable referencing list node IRIs in the XML:
70
+ # add a reference of the list node IRIs to themselves (with empty list names)
71
+ lookup.update({("", v): v for v in lookup.values()})
72
+ return lookup
73
+
74
+
75
+ def _get_node_tuples(lists: list[List]) -> Iterable[tuple[tuple[str, str], str]]:
76
+ for lst in lists:
77
+ list_name = lst.list_name
78
+ for node in lst.nodes:
79
+ yield (list_name, node.node_name), node.node_iri
80
+
81
+
82
+ def _get_list_info_from_server(con: Connection, project_iri: str) -> ProjectLists:
83
+ logger.info(f"Retrieving lists of project {project_iri}")
84
+ list_iris = _get_list_iris_from_server(con, project_iri)
85
+ lists = [_get_list_from_server(con, list_iri) for list_iri in list_iris]
86
+ return ProjectLists(lists)
87
+
88
+
89
+ def _get_list_iris_from_server(con: Connection, project_iri: str) -> list[str]:
90
+ iri = quote_plus(project_iri)
91
+ res = con.get(f"/admin/lists?projectIri={iri}")
92
+ lists: list[dict[str, Any]] = res["lists"]
93
+ logger.info(f"Found {len(lists)} lists for project")
94
+ return [lst["id"] for lst in lists]
95
+
96
+
97
+ def _get_list_from_server(con: Connection, list_iri: str) -> List:
98
+ logger.info(f"Retrieving nodes of list {list_iri}")
99
+ iri = quote_plus(list_iri)
100
+ res = con.get(f"/admin/lists/{iri}")
101
+ list_object: dict[str, Any] = res["list"]
102
+ list_info = list_object["listinfo"]
103
+ children: list[dict[str, Any]] = list_object["children"]
104
+ root_iri = list_info["id"]
105
+ # if the root node does not have a name, use the label instead
106
+ list_name: str = list_info.get("name") or list_info["labels"][0]["value"]
107
+ root_node = ListNode(root_iri, list_name)
108
+ nodes = [root_node, *_children_to_nodes(children)]
109
+ return List(root_iri, list_name, nodes)
110
+
111
+
112
+ def _children_to_nodes(children: list[dict[str, Any]]) -> list[ListNode]:
113
+ nodes = []
114
+ for child in children:
115
+ node_iri = child["id"]
116
+ node_name = child["name"]
117
+ nodes.append(ListNode(node_iri=node_iri, node_name=node_name))
118
+ if "children" in child:
119
+ nodes.extend(_children_to_nodes(child["children"]))
120
+ return nodes