dsp-tools 9.1.0.post11__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 (316) hide show
  1. dsp_tools/__init__.py +4 -0
  2. dsp_tools/cli/args.py +36 -0
  3. dsp_tools/cli/call_action.py +51 -231
  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 +156 -58
  7. dsp_tools/cli/entry_point.py +56 -26
  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/{utils → clients}/connection.py +2 -18
  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 +57 -23
  58. dsp_tools/commands/excel2json/{new_lists → lists}/compliance_checks.py +26 -26
  59. dsp_tools/commands/excel2json/{new_lists/make_new_lists.py → lists/make_lists.py} +19 -18
  60. dsp_tools/commands/excel2json/{new_lists → lists}/models/input_error.py +1 -12
  61. dsp_tools/commands/excel2json/{new_lists → lists}/models/serialise.py +9 -5
  62. dsp_tools/commands/excel2json/{new_lists → lists}/utils.py +4 -4
  63. dsp_tools/commands/excel2json/models/input_error.py +31 -11
  64. dsp_tools/commands/excel2json/models/json_header.py +53 -15
  65. dsp_tools/commands/excel2json/models/ontology.py +4 -3
  66. dsp_tools/commands/excel2json/{lists.py → old_lists.py} +26 -112
  67. dsp_tools/commands/excel2json/project.py +78 -34
  68. dsp_tools/commands/excel2json/properties.py +57 -36
  69. dsp_tools/commands/excel2json/resources.py +32 -12
  70. dsp_tools/commands/excel2json/utils.py +20 -1
  71. dsp_tools/commands/excel2xml/__init__.py +2 -2
  72. dsp_tools/commands/excel2xml/excel2xml_cli.py +7 -15
  73. dsp_tools/commands/excel2xml/excel2xml_lib.py +138 -493
  74. dsp_tools/commands/excel2xml/propertyelement.py +5 -5
  75. dsp_tools/commands/{project → get}/get.py +29 -13
  76. dsp_tools/commands/get/get_permissions.py +257 -0
  77. dsp_tools/commands/get/get_permissions_legacy.py +89 -0
  78. dsp_tools/commands/{project/models → get/legacy_models}/context.py +6 -6
  79. dsp_tools/commands/{project/models → get/legacy_models}/group.py +5 -10
  80. dsp_tools/commands/{project/models → get/legacy_models}/listnode.py +5 -35
  81. dsp_tools/commands/{project/models → get/legacy_models}/model.py +1 -1
  82. dsp_tools/commands/{project/models → get/legacy_models}/ontology.py +9 -14
  83. dsp_tools/commands/{project/models → get/legacy_models}/project.py +13 -6
  84. dsp_tools/commands/{project/models → get/legacy_models}/propertyclass.py +9 -16
  85. dsp_tools/commands/{project/models → get/legacy_models}/resourceclass.py +8 -46
  86. dsp_tools/commands/{project/models → get/legacy_models}/user.py +19 -60
  87. dsp_tools/commands/get/models/permissions_models.py +10 -0
  88. dsp_tools/commands/id2iri.py +20 -10
  89. dsp_tools/commands/ingest_xmlupload/bulk_ingest_client.py +81 -56
  90. dsp_tools/commands/ingest_xmlupload/create_resources/apply_ingest_id.py +4 -10
  91. dsp_tools/commands/ingest_xmlupload/create_resources/upload_xml.py +97 -37
  92. dsp_tools/commands/ingest_xmlupload/create_resources/user_information.py +2 -2
  93. dsp_tools/commands/ingest_xmlupload/ingest_files/ingest_files.py +9 -10
  94. dsp_tools/commands/ingest_xmlupload/upload_files/filechecker.py +3 -3
  95. dsp_tools/commands/ingest_xmlupload/upload_files/input_error.py +2 -10
  96. dsp_tools/commands/ingest_xmlupload/upload_files/upload_failures.py +12 -2
  97. dsp_tools/commands/ingest_xmlupload/upload_files/upload_files.py +8 -9
  98. dsp_tools/commands/resume_xmlupload/resume_xmlupload.py +18 -18
  99. dsp_tools/commands/start_stack.py +126 -77
  100. dsp_tools/commands/update_legal/CLAUDE.md +344 -0
  101. dsp_tools/commands/update_legal/__init__.py +0 -0
  102. dsp_tools/commands/update_legal/core.py +182 -0
  103. dsp_tools/commands/update_legal/csv_operations.py +135 -0
  104. dsp_tools/commands/update_legal/models.py +87 -0
  105. dsp_tools/commands/update_legal/xml_operations.py +247 -0
  106. dsp_tools/commands/validate_data/CLAUDE.md +159 -0
  107. dsp_tools/commands/validate_data/__init__.py +0 -0
  108. dsp_tools/commands/validate_data/constants.py +59 -0
  109. dsp_tools/commands/validate_data/mappers.py +143 -0
  110. dsp_tools/commands/validate_data/models/__init__.py +0 -0
  111. dsp_tools/commands/validate_data/models/api_responses.py +45 -0
  112. dsp_tools/commands/validate_data/models/input_problems.py +119 -0
  113. dsp_tools/commands/validate_data/models/rdf_like_data.py +117 -0
  114. dsp_tools/commands/validate_data/models/validation.py +106 -0
  115. dsp_tools/commands/validate_data/prepare_data/__init__.py +0 -0
  116. dsp_tools/commands/validate_data/prepare_data/get_rdf_like_data.py +296 -0
  117. dsp_tools/commands/validate_data/prepare_data/make_data_graph.py +91 -0
  118. dsp_tools/commands/validate_data/prepare_data/prepare_data.py +184 -0
  119. dsp_tools/commands/validate_data/process_validation_report/__init__.py +0 -0
  120. dsp_tools/commands/validate_data/process_validation_report/get_user_validation_message.py +358 -0
  121. dsp_tools/commands/validate_data/process_validation_report/query_validation_result.py +507 -0
  122. dsp_tools/commands/validate_data/process_validation_report/reformat_validation_results.py +150 -0
  123. dsp_tools/commands/validate_data/shacl_cli_validator.py +70 -0
  124. dsp_tools/commands/validate_data/sparql/__init__.py +0 -0
  125. dsp_tools/commands/{xml_validate/sparql/resource_shacl.py → validate_data/sparql/cardinality_shacl.py} +45 -47
  126. dsp_tools/commands/validate_data/sparql/construct_shacl.py +92 -0
  127. dsp_tools/commands/validate_data/sparql/legal_info_shacl.py +36 -0
  128. dsp_tools/commands/validate_data/sparql/value_shacl.py +357 -0
  129. dsp_tools/commands/validate_data/utils.py +59 -0
  130. dsp_tools/commands/validate_data/validate_data.py +283 -0
  131. dsp_tools/commands/validate_data/validation/__init__.py +0 -0
  132. dsp_tools/commands/validate_data/validation/check_duplicate_files.py +55 -0
  133. dsp_tools/commands/validate_data/validation/check_for_unknown_classes.py +67 -0
  134. dsp_tools/commands/validate_data/validation/get_validation_report.py +94 -0
  135. dsp_tools/commands/validate_data/validation/validate_ontology.py +107 -0
  136. dsp_tools/commands/xmlupload/CLAUDE.md +292 -0
  137. dsp_tools/commands/xmlupload/make_rdf_graph/__init__.py +0 -0
  138. dsp_tools/commands/xmlupload/make_rdf_graph/constants.py +63 -0
  139. dsp_tools/commands/xmlupload/make_rdf_graph/jsonld_utils.py +44 -0
  140. dsp_tools/commands/xmlupload/make_rdf_graph/make_file_value.py +77 -0
  141. dsp_tools/commands/xmlupload/make_rdf_graph/make_resource_and_values.py +114 -0
  142. dsp_tools/commands/xmlupload/make_rdf_graph/make_values.py +262 -0
  143. dsp_tools/commands/xmlupload/models/bitstream_info.py +18 -0
  144. dsp_tools/commands/xmlupload/models/formatted_text_value.py +0 -25
  145. dsp_tools/commands/xmlupload/models/ingest.py +56 -70
  146. dsp_tools/commands/xmlupload/models/input_problems.py +6 -14
  147. dsp_tools/commands/xmlupload/models/lookup_models.py +21 -0
  148. dsp_tools/commands/xmlupload/models/permission.py +0 -39
  149. dsp_tools/commands/xmlupload/models/{deserialise/xmlpermission.py → permissions_parsed.py} +2 -2
  150. dsp_tools/commands/xmlupload/models/processed/__init__.py +0 -0
  151. dsp_tools/commands/xmlupload/models/processed/file_values.py +29 -0
  152. dsp_tools/commands/xmlupload/models/processed/res.py +27 -0
  153. dsp_tools/commands/xmlupload/models/processed/values.py +101 -0
  154. dsp_tools/commands/xmlupload/models/rdf_models.py +26 -0
  155. dsp_tools/commands/xmlupload/models/upload_clients.py +3 -3
  156. dsp_tools/commands/xmlupload/models/upload_state.py +2 -4
  157. dsp_tools/commands/xmlupload/prepare_xml_input/__init__.py +0 -0
  158. dsp_tools/commands/xmlupload/{ark2iri.py → prepare_xml_input/ark2iri.py} +1 -1
  159. dsp_tools/commands/xmlupload/prepare_xml_input/get_processed_resources.py +252 -0
  160. dsp_tools/commands/xmlupload/{iiif_uri_validator.py → prepare_xml_input/iiif_uri_validator.py} +2 -14
  161. dsp_tools/commands/xmlupload/{list_client.py → prepare_xml_input/list_client.py} +15 -10
  162. dsp_tools/commands/xmlupload/prepare_xml_input/prepare_xml_input.py +67 -0
  163. dsp_tools/commands/xmlupload/prepare_xml_input/read_validate_xml_file.py +58 -0
  164. dsp_tools/commands/xmlupload/prepare_xml_input/transform_input_values.py +118 -0
  165. dsp_tools/commands/xmlupload/resource_create_client.py +7 -468
  166. dsp_tools/commands/xmlupload/richtext_id2iri.py +37 -0
  167. dsp_tools/commands/xmlupload/stash/{construct_and_analyze_graph.py → analyse_circular_reference_graph.py} +64 -157
  168. dsp_tools/commands/xmlupload/stash/create_info_for_graph.py +53 -0
  169. dsp_tools/commands/xmlupload/stash/graph_models.py +13 -8
  170. dsp_tools/commands/xmlupload/stash/stash_circular_references.py +48 -115
  171. dsp_tools/commands/xmlupload/stash/stash_models.py +4 -9
  172. dsp_tools/commands/xmlupload/stash/upload_stashed_resptr_props.py +34 -40
  173. dsp_tools/commands/xmlupload/stash/upload_stashed_xml_texts.py +98 -108
  174. dsp_tools/commands/xmlupload/upload_config.py +8 -0
  175. dsp_tools/commands/xmlupload/write_diagnostic_info.py +14 -9
  176. dsp_tools/commands/xmlupload/xmlupload.py +214 -192
  177. dsp_tools/config/__init__.py +0 -0
  178. dsp_tools/config/logger_config.py +69 -0
  179. dsp_tools/{utils → config}/warnings_config.py +4 -1
  180. dsp_tools/error/__init__.py +0 -0
  181. dsp_tools/error/custom_warnings.py +39 -0
  182. dsp_tools/error/exceptions.py +204 -0
  183. dsp_tools/error/problems.py +10 -0
  184. dsp_tools/error/xmllib_errors.py +20 -0
  185. dsp_tools/error/xmllib_warnings.py +54 -0
  186. dsp_tools/error/xmllib_warnings_util.py +159 -0
  187. dsp_tools/error/xsd_validation_error_msg.py +19 -0
  188. dsp_tools/legacy_models/__init__.py +0 -0
  189. dsp_tools/{models → legacy_models}/datetimestamp.py +7 -7
  190. dsp_tools/{models → legacy_models}/langstring.py +1 -1
  191. dsp_tools/{models → legacy_models}/projectContext.py +4 -4
  192. dsp_tools/resources/schema/data.xsd +108 -83
  193. dsp_tools/resources/schema/lists-only.json +4 -23
  194. dsp_tools/resources/schema/project.json +80 -35
  195. dsp_tools/resources/schema/properties-only.json +1 -4
  196. dsp_tools/resources/start-stack/docker-compose.override-host.j2 +11 -0
  197. dsp_tools/resources/start-stack/docker-compose.yml +34 -30
  198. dsp_tools/resources/start-stack/dsp-app-config.json +45 -0
  199. dsp_tools/resources/start-stack/dsp-app-config.override-host.j2 +26 -0
  200. dsp_tools/resources/validate_data/api-shapes-resource-cardinalities.ttl +191 -0
  201. dsp_tools/resources/validate_data/api-shapes.ttl +804 -0
  202. dsp_tools/resources/validate_data/shacl-cli-image.yml +4 -0
  203. dsp_tools/resources/validate_data/validate-ontology.ttl +99 -0
  204. dsp_tools/utils/ansi_colors.py +32 -0
  205. dsp_tools/utils/data_formats/__init__.py +0 -0
  206. dsp_tools/utils/{date_util.py → data_formats/date_util.py} +13 -1
  207. dsp_tools/utils/data_formats/iri_util.py +30 -0
  208. dsp_tools/utils/{shared.py → data_formats/shared.py} +1 -35
  209. dsp_tools/utils/{uri_util.py → data_formats/uri_util.py} +12 -2
  210. dsp_tools/utils/fuseki_bloating.py +63 -0
  211. dsp_tools/utils/json_parsing.py +22 -0
  212. dsp_tools/utils/rdf_constants.py +42 -0
  213. dsp_tools/utils/rdflib_utils.py +10 -0
  214. dsp_tools/utils/replace_id_with_iri.py +66 -0
  215. dsp_tools/utils/request_utils.py +238 -0
  216. dsp_tools/utils/xml_parsing/__init__.py +0 -0
  217. dsp_tools/utils/xml_parsing/get_lookups.py +32 -0
  218. dsp_tools/utils/xml_parsing/get_parsed_resources.py +325 -0
  219. dsp_tools/utils/xml_parsing/models/__init__.py +0 -0
  220. dsp_tools/utils/xml_parsing/models/parsed_resource.py +76 -0
  221. dsp_tools/utils/xml_parsing/parse_clean_validate_xml.py +137 -0
  222. dsp_tools/xmllib/CLAUDE.md +302 -0
  223. dsp_tools/xmllib/__init__.py +49 -0
  224. dsp_tools/xmllib/general_functions.py +877 -0
  225. dsp_tools/xmllib/internal/__init__.py +0 -0
  226. dsp_tools/xmllib/internal/checkers.py +162 -0
  227. dsp_tools/xmllib/internal/circumvent_circular_imports.py +36 -0
  228. dsp_tools/xmllib/internal/constants.py +46 -0
  229. dsp_tools/xmllib/internal/input_converters.py +155 -0
  230. dsp_tools/xmllib/internal/serialise_file_value.py +57 -0
  231. dsp_tools/xmllib/internal/serialise_resource.py +177 -0
  232. dsp_tools/xmllib/internal/serialise_values.py +152 -0
  233. dsp_tools/xmllib/internal/type_aliases.py +11 -0
  234. dsp_tools/xmllib/models/config_options.py +28 -0
  235. dsp_tools/xmllib/models/date_formats.py +48 -0
  236. dsp_tools/xmllib/models/dsp_base_resources.py +1380 -400
  237. dsp_tools/xmllib/models/internal/__init__.py +0 -0
  238. dsp_tools/xmllib/models/internal/file_values.py +172 -0
  239. dsp_tools/xmllib/models/internal/geometry.py +162 -0
  240. dsp_tools/xmllib/models/{migration_metadata.py → internal/migration_metadata.py} +14 -10
  241. dsp_tools/xmllib/models/internal/serialise_permissions.py +66 -0
  242. dsp_tools/xmllib/models/internal/values.py +342 -0
  243. dsp_tools/xmllib/models/licenses/__init__.py +0 -0
  244. dsp_tools/xmllib/models/licenses/other.py +59 -0
  245. dsp_tools/xmllib/models/licenses/recommended.py +107 -0
  246. dsp_tools/xmllib/models/permissions.py +41 -0
  247. dsp_tools/xmllib/models/res.py +1782 -0
  248. dsp_tools/xmllib/models/root.py +313 -26
  249. dsp_tools/xmllib/value_checkers.py +310 -47
  250. dsp_tools/xmllib/value_converters.py +765 -8
  251. dsp_tools-18.3.0.post13.dist-info/METADATA +90 -0
  252. dsp_tools-18.3.0.post13.dist-info/RECORD +286 -0
  253. dsp_tools-18.3.0.post13.dist-info/WHEEL +4 -0
  254. {dsp_tools-9.1.0.post11.dist-info → dsp_tools-18.3.0.post13.dist-info}/entry_points.txt +1 -0
  255. dsp_tools/commands/project/create/project_create.py +0 -1107
  256. dsp_tools/commands/project/create/project_create_lists.py +0 -204
  257. dsp_tools/commands/project/create/project_validate.py +0 -453
  258. dsp_tools/commands/project/models/project_definition.py +0 -12
  259. dsp_tools/commands/rosetta.py +0 -124
  260. dsp_tools/commands/template.py +0 -30
  261. dsp_tools/commands/xml_validate/api_connection.py +0 -122
  262. dsp_tools/commands/xml_validate/deserialise_input.py +0 -135
  263. dsp_tools/commands/xml_validate/make_data_rdf.py +0 -193
  264. dsp_tools/commands/xml_validate/models/data_deserialised.py +0 -108
  265. dsp_tools/commands/xml_validate/models/data_rdf.py +0 -214
  266. dsp_tools/commands/xml_validate/models/input_problems.py +0 -191
  267. dsp_tools/commands/xml_validate/models/validation.py +0 -29
  268. dsp_tools/commands/xml_validate/reformat_validaton_result.py +0 -89
  269. dsp_tools/commands/xml_validate/sparql/construct_shapes.py +0 -16
  270. dsp_tools/commands/xml_validate/xml_validate.py +0 -151
  271. dsp_tools/commands/xmlupload/check_consistency_with_ontology.py +0 -253
  272. dsp_tools/commands/xmlupload/models/deserialise/deserialise_value.py +0 -236
  273. dsp_tools/commands/xmlupload/models/deserialise/xmlresource.py +0 -171
  274. dsp_tools/commands/xmlupload/models/namespace_context.py +0 -39
  275. dsp_tools/commands/xmlupload/models/ontology_lookup_models.py +0 -161
  276. dsp_tools/commands/xmlupload/models/ontology_problem_models.py +0 -178
  277. dsp_tools/commands/xmlupload/models/serialise/jsonld_serialiser.py +0 -40
  278. dsp_tools/commands/xmlupload/models/serialise/serialise_value.py +0 -51
  279. dsp_tools/commands/xmlupload/ontology_client.py +0 -92
  280. dsp_tools/commands/xmlupload/project_client.py +0 -91
  281. dsp_tools/commands/xmlupload/read_validate_xml_file.py +0 -99
  282. dsp_tools/models/custom_warnings.py +0 -31
  283. dsp_tools/models/exceptions.py +0 -90
  284. dsp_tools/resources/0100-template-repo/template.json +0 -45
  285. dsp_tools/resources/0100-template-repo/template.xml +0 -27
  286. dsp_tools/resources/start-stack/docker-compose-validation.yml +0 -5
  287. dsp_tools/resources/start-stack/start-stack-config.yml +0 -4
  288. dsp_tools/resources/xml_validate/api-shapes.ttl +0 -411
  289. dsp_tools/resources/xml_validate/replace_namespace.xslt +0 -61
  290. dsp_tools/utils/connection_live.py +0 -383
  291. dsp_tools/utils/iri_util.py +0 -14
  292. dsp_tools/utils/logger_config.py +0 -41
  293. dsp_tools/utils/set_encoder.py +0 -20
  294. dsp_tools/utils/xml_utils.py +0 -145
  295. dsp_tools/utils/xml_validation.py +0 -197
  296. dsp_tools/utils/xml_validation_models.py +0 -68
  297. dsp_tools/xmllib/models/file_values.py +0 -78
  298. dsp_tools/xmllib/models/resource.py +0 -415
  299. dsp_tools/xmllib/models/values.py +0 -428
  300. dsp_tools-9.1.0.post11.dist-info/METADATA +0 -130
  301. dsp_tools-9.1.0.post11.dist-info/RECORD +0 -167
  302. dsp_tools-9.1.0.post11.dist-info/WHEEL +0 -4
  303. dsp_tools-9.1.0.post11.dist-info/licenses/LICENSE +0 -674
  304. /dsp_tools/{commands/excel2json/new_lists → clients}/__init__.py +0 -0
  305. /dsp_tools/commands/{excel2json/new_lists/models → create}/__init__.py +0 -0
  306. /dsp_tools/commands/{project → create/create_on_server}/__init__.py +0 -0
  307. /dsp_tools/commands/{project/create → create/models}/__init__.py +0 -0
  308. /dsp_tools/commands/{project/models → create/parsing}/__init__.py +0 -0
  309. /dsp_tools/commands/{xml_validate → create/serialisation}/__init__.py +0 -0
  310. /dsp_tools/commands/{xml_validate/models → excel2json/lists}/__init__.py +0 -0
  311. /dsp_tools/commands/{xml_validate/sparql → excel2json/lists/models}/__init__.py +0 -0
  312. /dsp_tools/commands/excel2json/{new_lists → lists}/models/deserialise.py +0 -0
  313. /dsp_tools/commands/{xmlupload/models/deserialise → get}/__init__.py +0 -0
  314. /dsp_tools/commands/{xmlupload/models/serialise → get/legacy_models}/__init__.py +0 -0
  315. /dsp_tools/commands/{project/models → get/legacy_models}/helpers.py +0 -0
  316. /dsp_tools/{models → commands/get/models}/__init__.py +0 -0
@@ -0,0 +1,507 @@
1
+ from typing import cast
2
+
3
+ from loguru import logger
4
+ from rdflib import RDF
5
+ from rdflib import RDFS
6
+ from rdflib import SH
7
+ from rdflib import XSD
8
+ from rdflib import Graph
9
+ from rdflib import Literal
10
+
11
+ from dsp_tools.commands.validate_data.constants import FILE_VALUE_PROPERTIES
12
+ from dsp_tools.commands.validate_data.constants import LEGAL_INFO_PROPS
13
+ from dsp_tools.commands.validate_data.models.input_problems import AllProblems
14
+ from dsp_tools.commands.validate_data.models.validation import DetailBaseInfo
15
+ from dsp_tools.commands.validate_data.models.validation import QueryInfo
16
+ from dsp_tools.commands.validate_data.models.validation import UnexpectedComponent
17
+ from dsp_tools.commands.validate_data.models.validation import ValidationReportGraphs
18
+ from dsp_tools.commands.validate_data.models.validation import ValidationResult
19
+ from dsp_tools.commands.validate_data.models.validation import ValidationResultBaseInfo
20
+ from dsp_tools.commands.validate_data.models.validation import ViolationType
21
+ from dsp_tools.commands.validate_data.process_validation_report.reformat_validation_results import (
22
+ reformat_extracted_results,
23
+ )
24
+ from dsp_tools.utils.rdf_constants import API_SHAPES
25
+ from dsp_tools.utils.rdf_constants import DASH
26
+ from dsp_tools.utils.rdf_constants import KNORA_API
27
+ from dsp_tools.utils.rdf_constants import SubjectObjectTypeAlias
28
+
29
+
30
+ def reformat_validation_graph(report: ValidationReportGraphs) -> AllProblems:
31
+ """
32
+ Reformats the validation result from an RDF graph into class instances
33
+ that are used to communicate the problems with the user.
34
+
35
+ Args:
36
+ report: with all the information necessary to construct a user message
37
+
38
+ Returns:
39
+ All Problems
40
+ """
41
+ logger.debug("Reformatting validation results.")
42
+ results_and_onto = report.validation_graph + report.onto_graph
43
+ data_and_onto = report.onto_graph + report.data_graph
44
+ validation_results, unexpected_extracted = _query_all_results(results_and_onto, data_and_onto)
45
+ reformatted_results = reformat_extracted_results(validation_results)
46
+ return AllProblems(reformatted_results, unexpected_extracted)
47
+
48
+
49
+ def _query_all_results(
50
+ results_and_onto: Graph, data_onto_graph: Graph
51
+ ) -> tuple[list[ValidationResult], list[UnexpectedComponent]]:
52
+ no_details, with_details = _separate_result_types(results_and_onto, data_onto_graph)
53
+ extracted_results: list[ValidationResult] = []
54
+ unexpected_components: list[UnexpectedComponent] = []
55
+
56
+ no_detail_extracted, no_detail_unexpected = _query_all_without_detail(no_details, results_and_onto, data_onto_graph)
57
+ extracted_results.extend(no_detail_extracted)
58
+ unexpected_components.extend(no_detail_unexpected)
59
+
60
+ detail_reformatted, detail_unexpected = _query_all_with_detail(with_details, results_and_onto, data_onto_graph)
61
+ extracted_results.extend(detail_reformatted)
62
+ unexpected_components.extend(detail_unexpected)
63
+ return extracted_results, unexpected_components
64
+
65
+
66
+ def _separate_result_types(
67
+ results_and_onto: Graph, data_onto_graph: Graph
68
+ ) -> tuple[list[ValidationResultBaseInfo], list[ValidationResultBaseInfo]]:
69
+ base_info = _extract_base_info_of_resource_results(results_and_onto, data_onto_graph)
70
+ no_details = [x for x in base_info if not x.detail]
71
+ with_details = [x for x in base_info if x.detail]
72
+ return no_details, with_details
73
+
74
+
75
+ def _extract_base_info_of_resource_results(
76
+ results_and_onto: Graph, data_onto_graph: Graph
77
+ ) -> list[ValidationResultBaseInfo]:
78
+ all_res_focus_nodes = []
79
+ main_bns = _get_all_main_result_bns(results_and_onto)
80
+ value_types = _get_all_value_classes(data_onto_graph)
81
+ for nd in main_bns:
82
+ focus_iri = next(results_and_onto.objects(nd, SH.focusNode))
83
+ res_type = next(data_onto_graph.objects(focus_iri, RDF.type))
84
+ info = QueryInfo(
85
+ validation_bn=nd,
86
+ focus_iri=focus_iri,
87
+ focus_rdf_type=res_type,
88
+ )
89
+ all_res_focus_nodes.extend(_extract_one_base_info(info, results_and_onto, data_onto_graph, value_types))
90
+ return all_res_focus_nodes
91
+
92
+
93
+ def _get_all_value_classes(data_onto_graph: Graph) -> set[SubjectObjectTypeAlias]:
94
+ all_types = set(data_onto_graph.objects(predicate=RDF.type))
95
+ all_value_types = set()
96
+ for type_ in all_types:
97
+ super_classes = set(data_onto_graph.transitive_objects(type_, RDFS.subClassOf))
98
+ if KNORA_API.Value in super_classes:
99
+ all_value_types.add(type_)
100
+ return all_value_types
101
+
102
+
103
+ def _extract_one_base_info(
104
+ info: QueryInfo, results_and_onto: Graph, data_onto_graph: Graph, value_types: set[SubjectObjectTypeAlias]
105
+ ) -> list[ValidationResultBaseInfo]:
106
+ results = []
107
+ path = next(results_and_onto.objects(info.validation_bn, SH.resultPath))
108
+ main_component_type = next(results_and_onto.objects(info.validation_bn, SH.sourceConstraintComponent))
109
+ severity = next(results_and_onto.objects(info.validation_bn, SH.resultSeverity))
110
+ if detail_bn_list := list(results_and_onto.objects(info.validation_bn, SH.detail)):
111
+ for single_detail in detail_bn_list:
112
+ detail_component = next(results_and_onto.objects(single_detail, SH.sourceConstraintComponent))
113
+ detail = DetailBaseInfo(
114
+ detail_bn=single_detail,
115
+ source_constraint_component=detail_component,
116
+ )
117
+ results.append(
118
+ ValidationResultBaseInfo(
119
+ result_bn=info.validation_bn,
120
+ source_constraint_component=main_component_type,
121
+ focus_node_iri=info.focus_iri,
122
+ focus_node_type=info.focus_rdf_type,
123
+ result_path=path,
124
+ severity=severity,
125
+ detail=detail,
126
+ )
127
+ )
128
+ else:
129
+ resource_iri, resource_type, user_facing_prop = _get_resource_iri_and_type(
130
+ info, path, data_onto_graph, value_types
131
+ )
132
+ results.append(
133
+ ValidationResultBaseInfo(
134
+ result_bn=info.validation_bn,
135
+ source_constraint_component=main_component_type,
136
+ focus_node_iri=resource_iri,
137
+ focus_node_type=resource_type,
138
+ result_path=user_facing_prop,
139
+ severity=severity,
140
+ detail=None,
141
+ )
142
+ )
143
+ return results
144
+
145
+
146
+ def _get_all_main_result_bns(results_and_onto: Graph) -> set[SubjectObjectTypeAlias]:
147
+ all_bns = set(results_and_onto.subjects(RDF.type, SH.ValidationResult))
148
+ # All the blank nodes that are referenced in a sh:detail will be queried together with the main validation result
149
+ # if we queried them separately we would get duplicate errors
150
+ detail_bns = set(results_and_onto.objects(predicate=SH.detail))
151
+ return all_bns - detail_bns
152
+
153
+
154
+ def _get_resource_iri_and_type(
155
+ info: QueryInfo, path: SubjectObjectTypeAlias, data_onto_graph: Graph, value_types: set[SubjectObjectTypeAlias]
156
+ ) -> tuple[SubjectObjectTypeAlias, SubjectObjectTypeAlias, SubjectObjectTypeAlias]:
157
+ resource_iri, resource_type, user_facing_prop = info.focus_iri, info.focus_rdf_type, path
158
+ if info.focus_rdf_type in value_types:
159
+ resource_iri, predicate = next(data_onto_graph.subject_predicates(object=info.focus_iri))
160
+ resource_type = next(data_onto_graph.objects(resource_iri, RDF.type))
161
+ if user_facing_prop not in LEGAL_INFO_PROPS:
162
+ user_facing_prop = predicate
163
+ return resource_iri, resource_type, user_facing_prop
164
+
165
+
166
+ def _query_all_without_detail(
167
+ all_base_info: list[ValidationResultBaseInfo], results_and_onto: Graph, data: Graph
168
+ ) -> tuple[list[ValidationResult], list[UnexpectedComponent]]:
169
+ extracted_results: list[ValidationResult] = []
170
+ unexpected_components: list[UnexpectedComponent] = []
171
+
172
+ for base_info in all_base_info:
173
+ res = _query_one_without_detail(base_info, results_and_onto, data)
174
+ if res is None:
175
+ pass
176
+ elif isinstance(res, UnexpectedComponent):
177
+ unexpected_components.append(res)
178
+ else:
179
+ extracted_results.append(res)
180
+ return extracted_results, unexpected_components
181
+
182
+
183
+ def _query_one_without_detail( # noqa:PLR0911 (Too many return statements)
184
+ base_info: ValidationResultBaseInfo, results_and_onto: Graph, data: Graph
185
+ ) -> ValidationResult | UnexpectedComponent | None:
186
+ msg = next(results_and_onto.objects(base_info.result_bn, SH.resultMessage))
187
+ component = next(results_and_onto.objects(base_info.result_bn, SH.sourceConstraintComponent))
188
+ match component:
189
+ case SH.PatternConstraintComponent:
190
+ return _query_pattern_constraint_component_violation(base_info.result_bn, base_info, results_and_onto)
191
+ case SH.MinCountConstraintComponent:
192
+ return _query_for_min_cardinality_violation(base_info, msg)
193
+ case SH.MaxCountConstraintComponent:
194
+ return ValidationResult(
195
+ violation_type=ViolationType.MAX_CARD,
196
+ res_iri=base_info.focus_node_iri,
197
+ res_class=base_info.focus_node_type,
198
+ severity=base_info.severity,
199
+ property=base_info.result_path,
200
+ expected=msg,
201
+ )
202
+ case SH.LessThanOrEqualsConstraintComponent:
203
+ return _query_for_less_than_or_equal_violation(base_info, results_and_onto, data, msg)
204
+ case DASH.ClosedByTypesConstraintComponent:
205
+ return _query_for_non_existent_cardinality_violation(base_info, results_and_onto, data)
206
+ case SH.SPARQLConstraintComponent:
207
+ return _query_for_unique_value_violation(base_info, results_and_onto)
208
+ case DASH.CoExistsWithConstraintComponent:
209
+ return _query_for_coexists_with_violation(base_info, results_and_onto, data, msg)
210
+ case SH.ClassConstraintComponent:
211
+ return _query_class_constraint_without_detail(base_info, results_and_onto, data, msg)
212
+ case (
213
+ SH.InConstraintComponent
214
+ | SH.LessThanConstraintComponent
215
+ | SH.MinExclusiveConstraintComponent
216
+ | SH.MinInclusiveConstraintComponent
217
+ | DASH.SingleLineConstraintComponent
218
+ | SH.DatatypeConstraintComponent
219
+ ):
220
+ return _query_general_violation_info(
221
+ base_info.result_bn, base_info, results_and_onto, ViolationType.GENERIC
222
+ )
223
+ case SH.OrConstraintComponent:
224
+ return _query_general_violation_info_with_value_as_string(
225
+ base_info.result_bn, base_info, results_and_onto, data
226
+ )
227
+ case _:
228
+ return UnexpectedComponent(str(component))
229
+
230
+
231
+ def _query_class_constraint_without_detail(
232
+ base_info: ValidationResultBaseInfo, results_and_onto: Graph, data: Graph, message: SubjectObjectTypeAlias
233
+ ) -> ValidationResult | None:
234
+ val: None | SubjectObjectTypeAlias = next(results_and_onto.objects(base_info.result_bn, SH.value))
235
+ # In this case we have some kind of FileValue violation
236
+ violation_type = ViolationType.GENERIC
237
+ value_type: None | SubjectObjectTypeAlias = None
238
+ msg: None | SubjectObjectTypeAlias = message
239
+ expected = None
240
+ val_type_list = list(data.objects(val, RDF.type))
241
+ # Here we have a normal value type violation
242
+ if val_type_list:
243
+ val_type = val_type_list.pop(0)
244
+ value_super_class = list(results_and_onto.transitive_objects(val_type, RDFS.subClassOf))
245
+ if KNORA_API.FileValue not in value_super_class:
246
+ value_type = val_type
247
+ val = None
248
+ violation_type = ViolationType.VALUE_TYPE
249
+ msg = None
250
+ expected = message
251
+ elif base_info.result_path == KNORA_API.hasStandoffLinkTo:
252
+ violation_type = ViolationType.LINK_TARGET
253
+ return ValidationResult(
254
+ violation_type=violation_type,
255
+ res_iri=base_info.focus_node_iri,
256
+ res_class=base_info.focus_node_type,
257
+ severity=base_info.severity,
258
+ property=base_info.result_path,
259
+ message=msg,
260
+ expected=expected,
261
+ input_value=val,
262
+ input_type=value_type,
263
+ )
264
+
265
+
266
+ def _query_for_less_than_or_equal_violation(
267
+ base_info: ValidationResultBaseInfo, results_and_onto: Graph, data: Graph, message: SubjectObjectTypeAlias
268
+ ) -> ValidationResult | None:
269
+ value_iri = next(results_and_onto.objects(base_info.result_bn, SH.focusNode))
270
+ start = next(data.objects(value_iri, API_SHAPES.dateHasStart))
271
+ end = next(data.objects(value_iri, API_SHAPES.dateHasEnd))
272
+ start_lit = cast(Literal, start)
273
+ end_lit = cast(Literal, end)
274
+ start_is_string = start_lit.datatype == XSD.string
275
+ end_is_string = end_lit.datatype == XSD.string
276
+ # If any one of the date ranges cannot be parsed as an xsd date, we get this violation also.
277
+ # But the main problem is, that the date format is wrong, in which case the datatype is xsd:string.
278
+ # This produces its own message
279
+ if any([start_is_string, end_is_string]):
280
+ return None
281
+ return ValidationResult(
282
+ violation_type=ViolationType.GENERIC,
283
+ res_iri=base_info.focus_node_iri,
284
+ res_class=base_info.focus_node_type,
285
+ severity=base_info.severity,
286
+ property=base_info.result_path,
287
+ input_value=_get_value_as_string(base_info.result_bn, results_and_onto, data),
288
+ message=message,
289
+ )
290
+
291
+
292
+ def _query_for_non_existent_cardinality_violation(
293
+ base_info: ValidationResultBaseInfo, results_and_onto: Graph, data: Graph
294
+ ) -> ValidationResult | None:
295
+ input_val = None
296
+ if base_info.result_path in FILE_VALUE_PROPERTIES:
297
+ violation_type = ViolationType.FILE_VALUE_PROHIBITED
298
+ if value_bn_found := list(results_and_onto.objects(base_info.result_bn, SH.value)):
299
+ value_bn = value_bn_found.pop(0)
300
+ if file_path := list(data.objects(value_bn, KNORA_API.fileValueHasFilename)):
301
+ input_val = file_path.pop(0)
302
+ elif iiif_uri := list(data.objects(value_bn, KNORA_API.stillImageFileValueHasExternalUrl)):
303
+ input_val = iiif_uri.pop(0)
304
+ else:
305
+ violation_type = ViolationType.NON_EXISTING_CARD
306
+ return ValidationResult(
307
+ violation_type=violation_type,
308
+ res_iri=base_info.focus_node_iri,
309
+ res_class=base_info.focus_node_type,
310
+ severity=base_info.severity,
311
+ property=base_info.result_path,
312
+ input_value=input_val,
313
+ )
314
+
315
+
316
+ def _query_all_with_detail(
317
+ all_base_info: list[ValidationResultBaseInfo], results_and_onto: Graph, data_onto_graph: Graph
318
+ ) -> tuple[list[ValidationResult], list[UnexpectedComponent]]:
319
+ extracted_results: list[ValidationResult] = []
320
+ unexpected_components: list[UnexpectedComponent] = []
321
+
322
+ for base_info in all_base_info:
323
+ res = _query_one_with_detail(base_info, results_and_onto, data_onto_graph)
324
+ if isinstance(res, UnexpectedComponent):
325
+ unexpected_components.append(res)
326
+ else:
327
+ extracted_results.append(res)
328
+ return extracted_results, unexpected_components
329
+
330
+
331
+ def _query_one_with_detail(
332
+ base_info: ValidationResultBaseInfo, results_and_onto: Graph, data_graph: Graph
333
+ ) -> ValidationResult | UnexpectedComponent:
334
+ detail_info = cast(DetailBaseInfo, base_info.detail)
335
+ match detail_info.source_constraint_component:
336
+ case SH.MinCountConstraintComponent:
337
+ if base_info.result_path in FILE_VALUE_PROPERTIES:
338
+ return _query_general_violation_info(
339
+ base_info.result_bn, base_info, results_and_onto, ViolationType.GENERIC
340
+ )
341
+ return _query_for_value_type_violation(base_info, results_and_onto, data_graph)
342
+ case SH.PatternConstraintComponent:
343
+ return _query_pattern_constraint_component_violation(detail_info.detail_bn, base_info, results_and_onto)
344
+ case SH.ClassConstraintComponent:
345
+ return _query_class_constraint_component_violation(base_info, results_and_onto, data_graph)
346
+ case SH.InConstraintComponent | DASH.SingleLineConstraintComponent:
347
+ detail = cast(DetailBaseInfo, base_info.detail)
348
+ return _query_general_violation_info(detail.detail_bn, base_info, results_and_onto, ViolationType.GENERIC)
349
+ case _:
350
+ return UnexpectedComponent(str(detail_info.source_constraint_component))
351
+
352
+
353
+ def _query_class_constraint_component_violation(
354
+ base_info: ValidationResultBaseInfo, results_and_onto: Graph, data_graph: Graph
355
+ ) -> ValidationResult | UnexpectedComponent:
356
+ detail_info = cast(DetailBaseInfo, base_info.detail)
357
+ detail_path = next(results_and_onto.objects(detail_info.detail_bn, SH.resultPath))
358
+ if detail_path == RDF.type:
359
+ return _query_for_value_type_violation(base_info, results_and_onto, data_graph)
360
+ return _query_for_link_value_target_violation(base_info, results_and_onto, data_graph)
361
+
362
+
363
+ def _query_for_value_type_violation(
364
+ base_info: ValidationResultBaseInfo, results_and_onto: Graph, data_graph: Graph
365
+ ) -> ValidationResult:
366
+ detail_info = cast(DetailBaseInfo, base_info.detail)
367
+ msg = next(results_and_onto.objects(detail_info.detail_bn, SH.resultMessage))
368
+ val = next(results_and_onto.objects(base_info.result_bn, SH.value))
369
+ val_type = next(data_graph.objects(val, RDF.type))
370
+ return ValidationResult(
371
+ violation_type=ViolationType.VALUE_TYPE,
372
+ res_iri=base_info.focus_node_iri,
373
+ res_class=base_info.focus_node_type,
374
+ severity=base_info.severity,
375
+ property=base_info.result_path,
376
+ expected=msg,
377
+ input_type=val_type,
378
+ )
379
+
380
+
381
+ def _query_pattern_constraint_component_violation(
382
+ bn_with_info: SubjectObjectTypeAlias, base_info: ValidationResultBaseInfo, results_and_onto: Graph
383
+ ) -> ValidationResult:
384
+ val = next(results_and_onto.objects(bn_with_info, SH.value))
385
+ msg = next(results_and_onto.objects(bn_with_info, SH.resultMessage))
386
+ return ValidationResult(
387
+ violation_type=ViolationType.PATTERN,
388
+ res_iri=base_info.focus_node_iri,
389
+ res_class=base_info.focus_node_type,
390
+ severity=base_info.severity,
391
+ property=base_info.result_path,
392
+ expected=msg,
393
+ input_value=val,
394
+ )
395
+
396
+
397
+ def _query_general_violation_info(
398
+ result_bn: SubjectObjectTypeAlias,
399
+ base_info: ValidationResultBaseInfo,
400
+ results_and_onto: Graph,
401
+ violation_type: ViolationType,
402
+ value: SubjectObjectTypeAlias | None = None,
403
+ ) -> ValidationResult:
404
+ if not value:
405
+ val = next(results_and_onto.objects(result_bn, SH.value), None)
406
+ else:
407
+ val = value
408
+ msg = next(results_and_onto.objects(result_bn, SH.resultMessage))
409
+ return ValidationResult(
410
+ violation_type=violation_type,
411
+ res_iri=base_info.focus_node_iri,
412
+ res_class=base_info.focus_node_type,
413
+ severity=base_info.severity,
414
+ property=base_info.result_path,
415
+ message=msg,
416
+ input_value=val,
417
+ )
418
+
419
+
420
+ def _query_general_violation_info_with_value_as_string(
421
+ result_bn: SubjectObjectTypeAlias,
422
+ base_info: ValidationResultBaseInfo,
423
+ results_and_onto: Graph,
424
+ data: Graph,
425
+ ) -> ValidationResult:
426
+ value_iri = next(results_and_onto.objects(result_bn, SH.focusNode))
427
+ value = next(data.objects(value_iri, KNORA_API.valueAsString))
428
+ return _query_general_violation_info(result_bn, base_info, results_and_onto, ViolationType.GENERIC, value)
429
+
430
+
431
+ def _query_for_link_value_target_violation(
432
+ base_info: ValidationResultBaseInfo, results_and_onto: Graph, data_graph: Graph
433
+ ) -> ValidationResult:
434
+ detail_info = cast(DetailBaseInfo, base_info.detail)
435
+ target_iri = next(results_and_onto.objects(detail_info.detail_bn, SH.value))
436
+ target_rdf_type = next(data_graph.objects(target_iri, RDF.type), None)
437
+ expected_type = next(results_and_onto.objects(detail_info.detail_bn, SH.resultMessage))
438
+ return ValidationResult(
439
+ violation_type=ViolationType.LINK_TARGET,
440
+ res_iri=base_info.focus_node_iri,
441
+ res_class=base_info.focus_node_type,
442
+ severity=base_info.severity,
443
+ property=base_info.result_path,
444
+ expected=expected_type,
445
+ input_value=target_iri,
446
+ input_type=target_rdf_type,
447
+ )
448
+
449
+
450
+ def _query_for_min_cardinality_violation(
451
+ base_info: ValidationResultBaseInfo, msg: SubjectObjectTypeAlias
452
+ ) -> ValidationResult:
453
+ if base_info.result_path in LEGAL_INFO_PROPS:
454
+ violation_type = ViolationType.GENERIC
455
+ else:
456
+ violation_type = ViolationType.MIN_CARD
457
+ return ValidationResult(
458
+ violation_type=violation_type,
459
+ res_iri=base_info.focus_node_iri,
460
+ res_class=base_info.focus_node_type,
461
+ severity=base_info.severity,
462
+ property=base_info.result_path,
463
+ expected=msg,
464
+ )
465
+
466
+
467
+ def _query_for_unique_value_violation(
468
+ base_info: ValidationResultBaseInfo,
469
+ results_and_onto: Graph,
470
+ ) -> ValidationResult:
471
+ val = next(results_and_onto.objects(base_info.result_bn, SH.value))
472
+ return ValidationResult(
473
+ violation_type=ViolationType.UNIQUE_VALUE,
474
+ res_iri=base_info.focus_node_iri,
475
+ res_class=base_info.focus_node_type,
476
+ severity=base_info.severity,
477
+ property=base_info.result_path,
478
+ input_value=val,
479
+ )
480
+
481
+
482
+ def _query_for_coexists_with_violation(
483
+ base_info: ValidationResultBaseInfo, results_and_onto: Graph, data: Graph, message: SubjectObjectTypeAlias
484
+ ) -> ValidationResult:
485
+ source_shapes = next(results_and_onto.objects(base_info.result_bn, SH.sourceShape))
486
+ if source_shapes == API_SHAPES.seqnum_PropShape:
487
+ violation_type = ViolationType.SEQNUM_IS_PART_OF
488
+ value = None
489
+ prop = None
490
+ else:
491
+ violation_type = ViolationType.GENERIC
492
+ value = _get_value_as_string(base_info.result_bn, results_and_onto, data)
493
+ prop = base_info.result_path
494
+ return ValidationResult(
495
+ violation_type=violation_type,
496
+ res_iri=base_info.focus_node_iri,
497
+ res_class=base_info.focus_node_type,
498
+ property=prop,
499
+ severity=base_info.severity,
500
+ message=message,
501
+ input_value=value,
502
+ )
503
+
504
+
505
+ def _get_value_as_string(result_bn: SubjectObjectTypeAlias, results: Graph, data: Graph) -> SubjectObjectTypeAlias:
506
+ value_iri = next(results.objects(result_bn, SH.focusNode))
507
+ return next(data.objects(value_iri, KNORA_API.valueAsString))
@@ -0,0 +1,150 @@
1
+ from typing import cast
2
+
3
+ from rdflib import SH
4
+ from rdflib import URIRef
5
+
6
+ from dsp_tools.commands.validate_data.constants import FILE_VALUE_PROPERTIES
7
+ from dsp_tools.commands.validate_data.constants import FILEVALUE_DETAIL_INFO
8
+ from dsp_tools.commands.validate_data.constants import LEGAL_INFO_PROPS
9
+ from dsp_tools.commands.validate_data.mappers import RESULT_TO_PROBLEM_MAPPER
10
+ from dsp_tools.commands.validate_data.models.input_problems import InputProblem
11
+ from dsp_tools.commands.validate_data.models.input_problems import ProblemType
12
+ from dsp_tools.commands.validate_data.models.input_problems import Severity
13
+ from dsp_tools.commands.validate_data.models.validation import ReformattedIRI
14
+ from dsp_tools.commands.validate_data.models.validation import ValidationResult
15
+ from dsp_tools.commands.validate_data.models.validation import ViolationType
16
+ from dsp_tools.commands.validate_data.utils import reformat_any_iri
17
+ from dsp_tools.commands.validate_data.utils import reformat_data_iri
18
+ from dsp_tools.commands.validate_data.utils import reformat_onto_iri
19
+ from dsp_tools.error.exceptions import BaseError
20
+ from dsp_tools.utils.rdf_constants import KNORA_API
21
+ from dsp_tools.utils.rdf_constants import SubjectObjectTypeAlias
22
+
23
+ SEVERITY_MAPPER: dict[SubjectObjectTypeAlias, Severity] = {
24
+ SH.Violation: Severity.VIOLATION,
25
+ SH.Warning: Severity.WARNING,
26
+ SH.Info: Severity.INFO,
27
+ }
28
+
29
+
30
+ def reformat_extracted_results(results: list[ValidationResult]) -> list[InputProblem]:
31
+ return [_reformat_one_validation_result(x) for x in results]
32
+
33
+
34
+ def _reformat_one_validation_result(validation_result: ValidationResult) -> InputProblem:
35
+ match validation_result.violation_type:
36
+ case ViolationType.MIN_CARD:
37
+ return _reformat_min_card(validation_result)
38
+ case (
39
+ ViolationType.MAX_CARD
40
+ | ViolationType.NON_EXISTING_CARD
41
+ | ViolationType.PATTERN
42
+ | ViolationType.UNIQUE_VALUE
43
+ | ViolationType.VALUE_TYPE as violation
44
+ ):
45
+ problem = RESULT_TO_PROBLEM_MAPPER[violation]
46
+ return _reformat_generic(result=validation_result, problem_type=problem)
47
+ case ViolationType.GENERIC:
48
+ prop_str = None
49
+ if validation_result.property in LEGAL_INFO_PROPS or validation_result.property in FILE_VALUE_PROPERTIES:
50
+ prop_str = "bitstream / iiif-uri"
51
+ return _reformat_generic(validation_result, ProblemType.GENERIC, prop_string=prop_str)
52
+ case ViolationType.FILE_VALUE_PROHIBITED | ViolationType.FILE_VALUE_MISSING as violation:
53
+ problem = RESULT_TO_PROBLEM_MAPPER[violation]
54
+ return _reformat_generic(result=validation_result, problem_type=problem, prop_string="bitstream / iiif-uri")
55
+ case ViolationType.SEQNUM_IS_PART_OF:
56
+ return _reformat_generic(
57
+ result=validation_result, problem_type=ProblemType.GENERIC, prop_string="seqnum or isPartOf"
58
+ )
59
+ case ViolationType.LINK_TARGET:
60
+ return _reformat_link_target_violation_result(validation_result)
61
+ case _:
62
+ raise BaseError(f"An unknown violation result was found: {validation_result.__class__.__name__}")
63
+
64
+
65
+ def _reformat_min_card(result: ValidationResult) -> InputProblem:
66
+ iris = _reformat_main_iris(result)
67
+ if file_prop_info := FILEVALUE_DETAIL_INFO.get(cast(URIRef, result.property)):
68
+ prop_str, file_extensions = file_prop_info
69
+ detail_msg = None
70
+ problem_type = ProblemType.FILE_VALUE_MISSING
71
+ expected: str | None = f"This resource requires a file with one of the following extensions: {file_extensions}"
72
+ else:
73
+ prop_str = iris.prop_name
74
+ detail_msg = _convert_rdflib_input_to_string(result.message)
75
+ problem_type = ProblemType.MIN_CARD
76
+ expected = _convert_rdflib_input_to_string(result.expected)
77
+
78
+ return InputProblem(
79
+ problem_type=problem_type,
80
+ res_id=iris.res_id,
81
+ res_type=iris.res_type,
82
+ prop_name=prop_str,
83
+ severity=SEVERITY_MAPPER[result.severity],
84
+ message=detail_msg,
85
+ input_value=_convert_rdflib_input_to_string(result.input_value),
86
+ input_type=_convert_rdflib_input_to_string(result.input_type),
87
+ expected=expected,
88
+ )
89
+
90
+
91
+ def _reformat_generic(
92
+ result: ValidationResult, problem_type: ProblemType, prop_string: str | None = None
93
+ ) -> InputProblem:
94
+ iris = _reformat_main_iris(result)
95
+ user_prop = iris.prop_name if not prop_string else prop_string
96
+ return InputProblem(
97
+ problem_type=problem_type,
98
+ res_id=iris.res_id,
99
+ res_type=iris.res_type,
100
+ prop_name=user_prop,
101
+ severity=SEVERITY_MAPPER[result.severity],
102
+ message=_convert_rdflib_input_to_string(result.message),
103
+ input_value=_convert_rdflib_input_to_string(result.input_value),
104
+ input_type=_convert_rdflib_input_to_string(result.input_type),
105
+ expected=_convert_rdflib_input_to_string(result.expected),
106
+ )
107
+
108
+
109
+ def _reformat_link_target_violation_result(result: ValidationResult) -> InputProblem:
110
+ iris = _reformat_main_iris(result)
111
+ input_type = None
112
+ expected = None
113
+ problem_type = ProblemType.INEXISTENT_LINKED_RESOURCE
114
+ # If it is a stand-off link, we want to preserve the message
115
+ if result.property == KNORA_API.hasStandoffLinkTo:
116
+ msg = str(result.message)
117
+ else:
118
+ msg = None
119
+
120
+ if result.input_type:
121
+ problem_type = ProblemType.LINK_TARGET_TYPE_MISMATCH
122
+ input_type = reformat_onto_iri(str(result.input_type))
123
+ expected = reformat_onto_iri(str(result.expected))
124
+
125
+ return InputProblem(
126
+ problem_type=problem_type,
127
+ res_id=iris.res_id,
128
+ res_type=iris.res_type,
129
+ prop_name=iris.prop_name,
130
+ severity=SEVERITY_MAPPER[result.severity],
131
+ input_value=reformat_data_iri(str(result.input_value)),
132
+ input_type=input_type,
133
+ expected=expected,
134
+ message=msg,
135
+ )
136
+
137
+
138
+ def _reformat_main_iris(result: ValidationResult) -> ReformattedIRI:
139
+ subject_id = reformat_data_iri(result.res_iri)
140
+ prop_name = reformat_onto_iri(result.property) if result.property else ""
141
+ res_type = reformat_onto_iri(result.res_class)
142
+ return ReformattedIRI(res_id=subject_id, res_type=res_type, prop_name=prop_name)
143
+
144
+
145
+ def _convert_rdflib_input_to_string(input_val: SubjectObjectTypeAlias | None) -> str | None:
146
+ if not input_val:
147
+ return None
148
+ if isinstance(input_val, URIRef):
149
+ return reformat_any_iri(input_val)
150
+ return str(input_val)