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
@@ -8,46 +8,56 @@ from pathlib import Path
8
8
  from typing import Never
9
9
 
10
10
  from loguru import logger
11
- from lxml import etree
11
+ from rdflib import URIRef
12
12
  from tqdm import tqdm
13
13
 
14
14
  from dsp_tools.cli.args import ServerCredentials
15
- from dsp_tools.commands.xmlupload.check_consistency_with_ontology import do_xml_consistency_check_with_ontology
16
- from dsp_tools.commands.xmlupload.iiif_uri_validator import IIIFUriValidator
17
- from dsp_tools.commands.xmlupload.list_client import ListClient
18
- from dsp_tools.commands.xmlupload.list_client import ListClientLive
19
- from dsp_tools.commands.xmlupload.models.deserialise.xmlpermission import XmlPermission
20
- from dsp_tools.commands.xmlupload.models.deserialise.xmlresource import XMLResource
15
+ from dsp_tools.cli.args import ValidateDataConfig
16
+ from dsp_tools.cli.args import ValidationSeverity
17
+ from dsp_tools.clients.authentication_client import AuthenticationClient
18
+ from dsp_tools.clients.authentication_client_live import AuthenticationClientLive
19
+ from dsp_tools.clients.connection import Connection
20
+ from dsp_tools.clients.connection_live import ConnectionLive
21
+ from dsp_tools.clients.fuseki_metrics import FusekiMetrics
22
+ from dsp_tools.clients.legal_info_client import LegalInfoClient
23
+ from dsp_tools.clients.legal_info_client_live import LegalInfoClientLive
24
+ from dsp_tools.clients.project_client import ProjectClient
25
+ from dsp_tools.clients.project_client_live import ProjectClientLive
26
+ from dsp_tools.commands.validate_data.validate_data import validate_parsed_resources
27
+ from dsp_tools.commands.xmlupload.make_rdf_graph.make_resource_and_values import create_resource_with_values
21
28
  from dsp_tools.commands.xmlupload.models.ingest import AssetClient
22
29
  from dsp_tools.commands.xmlupload.models.ingest import DspIngestClientLive
23
- from dsp_tools.commands.xmlupload.models.namespace_context import get_json_ld_context_for_project
24
- from dsp_tools.commands.xmlupload.models.permission import Permissions
30
+ from dsp_tools.commands.xmlupload.models.lookup_models import IRILookups
31
+ from dsp_tools.commands.xmlupload.models.lookup_models import XmlReferenceLookups
32
+ from dsp_tools.commands.xmlupload.models.processed.res import ProcessedResource
25
33
  from dsp_tools.commands.xmlupload.models.upload_clients import UploadClients
26
34
  from dsp_tools.commands.xmlupload.models.upload_state import UploadState
27
- from dsp_tools.commands.xmlupload.ontology_client import OntologyClient
28
- from dsp_tools.commands.xmlupload.ontology_client import OntologyClientLive
29
- from dsp_tools.commands.xmlupload.project_client import ProjectClient
30
- from dsp_tools.commands.xmlupload.project_client import ProjectClientLive
31
- from dsp_tools.commands.xmlupload.read_validate_xml_file import check_if_bitstreams_exist
32
- from dsp_tools.commands.xmlupload.read_validate_xml_file import validate_and_parse
35
+ from dsp_tools.commands.xmlupload.prepare_xml_input.get_processed_resources import get_processed_resources
36
+ from dsp_tools.commands.xmlupload.prepare_xml_input.list_client import ListClient
37
+ from dsp_tools.commands.xmlupload.prepare_xml_input.list_client import ListClientLive
38
+ from dsp_tools.commands.xmlupload.prepare_xml_input.prepare_xml_input import get_parsed_resources_and_mappers
39
+ from dsp_tools.commands.xmlupload.prepare_xml_input.prepare_xml_input import get_stash_and_upload_order
40
+ from dsp_tools.commands.xmlupload.prepare_xml_input.read_validate_xml_file import check_if_bitstreams_exist
41
+ from dsp_tools.commands.xmlupload.prepare_xml_input.read_validate_xml_file import validate_iiif_uris
33
42
  from dsp_tools.commands.xmlupload.resource_create_client import ResourceCreateClient
34
- from dsp_tools.commands.xmlupload.stash.stash_circular_references import identify_circular_references
35
- from dsp_tools.commands.xmlupload.stash.stash_circular_references import stash_circular_references
36
- from dsp_tools.commands.xmlupload.stash.stash_models import Stash
37
43
  from dsp_tools.commands.xmlupload.stash.upload_stashed_resptr_props import upload_stashed_resptr_props
38
44
  from dsp_tools.commands.xmlupload.stash.upload_stashed_xml_texts import upload_stashed_xml_texts
39
45
  from dsp_tools.commands.xmlupload.upload_config import UploadConfig
40
46
  from dsp_tools.commands.xmlupload.write_diagnostic_info import write_id2iri_mapping
41
- from dsp_tools.models.custom_warnings import DspToolsUserWarning
42
- from dsp_tools.models.exceptions import BaseError
43
- from dsp_tools.models.exceptions import PermanentConnectionError
44
- from dsp_tools.models.exceptions import PermanentTimeOutError
45
- from dsp_tools.models.exceptions import UserError
46
- from dsp_tools.models.exceptions import XmlUploadInterruptedError
47
- from dsp_tools.models.projectContext import ProjectContext
48
- from dsp_tools.utils.connection import Connection
49
- from dsp_tools.utils.connection_live import ConnectionLive
50
- from dsp_tools.utils.logger_config import WARNINGS_SAVEPATH
47
+ from dsp_tools.config.logger_config import WARNINGS_SAVEPATH
48
+ from dsp_tools.error.custom_warnings import DspToolsUserWarning
49
+ from dsp_tools.error.exceptions import BaseError
50
+ from dsp_tools.error.exceptions import PermanentConnectionError
51
+ from dsp_tools.error.exceptions import PermanentTimeOutError
52
+ from dsp_tools.error.exceptions import XmlUploadInterruptedError
53
+ from dsp_tools.utils.ansi_colors import BOLD_RED
54
+ from dsp_tools.utils.ansi_colors import BOLD_YELLOW
55
+ from dsp_tools.utils.ansi_colors import RESET_TO_DEFAULT
56
+ from dsp_tools.utils.data_formats.uri_util import is_prod_like_server
57
+ from dsp_tools.utils.fuseki_bloating import communicate_fuseki_bloating
58
+ from dsp_tools.utils.replace_id_with_iri import use_id2iri_mapping_to_replace_ids
59
+ from dsp_tools.utils.xml_parsing.models.parsed_resource import ParsedResource
60
+ from dsp_tools.utils.xml_parsing.parse_clean_validate_xml import parse_and_clean_xml_file
51
61
 
52
62
 
53
63
  def xmlupload(
@@ -67,7 +77,7 @@ def xmlupload(
67
77
 
68
78
  Raises:
69
79
  BaseError: in case of permanent network or software failure
70
- UserError: in case of permanent network or software failure, or if the XML file is invalid
80
+ InputError: in case of permanent network or software failure, or if the XML file is invalid
71
81
  InputError: in case of permanent network or software failure, or if the XML file is invalid
72
82
 
73
83
  Returns:
@@ -75,66 +85,143 @@ def xmlupload(
75
85
  uploaded because there is an error in it
76
86
  """
77
87
 
78
- default_ontology, root, shortcode = _parse_xml(input_file=input_file, imgdir=imgdir)
79
-
80
- if not config.skip_iiif_validation:
81
- _validate_iiif_uris(root)
88
+ root = parse_and_clean_xml_file(input_file)
89
+ shortcode = root.attrib["shortcode"]
82
90
 
83
- con = ConnectionLive(creds.server)
84
- con.login(creds.user, creds.password)
91
+ auth = AuthenticationClientLive(server=creds.server, email=creds.user, password=creds.password)
92
+ con = ConnectionLive(creds.server, auth)
85
93
  config = config.with_server_info(server=creds.server, shortcode=shortcode)
94
+ clients = _get_live_clients(con, auth, creds, shortcode, imgdir)
86
95
 
87
- ontology_client = OntologyClientLive(con=con, shortcode=shortcode, default_ontology=default_ontology)
88
- resources, permissions_lookup, stash = prepare_upload(root, ontology_client)
96
+ parsed_resources, lookups = get_parsed_resources_and_mappers(root, clients)
97
+ if config.id2iri_file:
98
+ parsed_resources = use_id2iri_mapping_to_replace_ids(parsed_resources, Path(config.id2iri_file))
89
99
 
90
- clients = _get_live_clients(con, creds, shortcode, imgdir)
91
- state = UploadState(resources, stash, config, permissions_lookup)
100
+ is_on_prod_like_server = is_prod_like_server(creds.server)
92
101
 
93
- return execute_upload(clients, state)
102
+ validation_ok = _handle_validation(
103
+ parsed_resources=parsed_resources,
104
+ lookups=lookups,
105
+ config=config,
106
+ is_on_prod_like_server=is_on_prod_like_server,
107
+ auth=auth,
108
+ input_file=input_file,
109
+ )
110
+ if not validation_ok:
111
+ return False
94
112
 
113
+ check_if_bitstreams_exist(root, imgdir)
114
+ if not config.skip_iiif_validation:
115
+ validate_iiif_uris(root)
95
116
 
96
- def _parse_xml(imgdir: str, input_file: Path) -> tuple[str, etree._Element, str]:
97
- """
98
- This function takes a path to an XML file.
99
- It validates the file against the XML schema.
100
- It checks if all the mentioned bitstream files are in the specified location.
101
- It retrieves the shortcode and default ontology from the XML file.
117
+ if not is_on_prod_like_server:
118
+ enable_unknown_license_if_any_are_missing(clients.legal_info_client, parsed_resources)
102
119
 
103
- Args:
104
- imgdir: directory to the bitstream files
105
- input_file: file that will be pased
120
+ processed_resources = get_processed_resources(parsed_resources, lookups, is_on_prod_like_server)
106
121
 
107
- Returns:
108
- The ontology name, the parsed XML file and the shortcode of the project
109
- """
110
- root, shortcode, default_ontology = validate_and_parse(input_file)
111
- check_if_bitstreams_exist(root=root, imgdir=imgdir)
112
- logger.info(f"Validated and parsed the XML file. {shortcode=:} and {default_ontology=:}")
113
- return default_ontology, root, shortcode
122
+ sorted_resources, stash = get_stash_and_upload_order(processed_resources)
123
+ state = UploadState(
124
+ pending_resources=sorted_resources,
125
+ pending_stash=stash,
126
+ config=config,
127
+ )
128
+
129
+ return execute_upload(clients, state)
130
+
131
+
132
+ def _handle_validation(
133
+ parsed_resources: list[ParsedResource],
134
+ lookups: XmlReferenceLookups,
135
+ config: UploadConfig,
136
+ is_on_prod_like_server: bool,
137
+ auth: AuthenticationClient,
138
+ input_file: Path,
139
+ ) -> bool:
140
+ validation_should_be_skipped = config.skip_validation
141
+ if is_on_prod_like_server and config.skip_validation:
142
+ msg = (
143
+ "You set the flag '--skip-validation' to circumvent the SHACL schema validation. "
144
+ "This means that the upload may fail due to undetected errors. "
145
+ "Do you wish to skip the validation (yes/no)? "
146
+ )
147
+ resp = ""
148
+ while resp not in ["yes", "no"]:
149
+ resp = input(BOLD_RED + msg + RESET_TO_DEFAULT)
150
+ if str(resp) == "no":
151
+ validation_should_be_skipped = False
152
+ if not validation_should_be_skipped:
153
+ ignore_duplicates = config.ignore_duplicate_files_warning
154
+ if is_on_prod_like_server and ignore_duplicates:
155
+ msg = (
156
+ "You set the flag '--ignore-duplicate-files-warning'. "
157
+ "This means that duplicate multimedia files will not be detected. "
158
+ "Are you sure you want to exclude this from the validation? (yes/no)"
159
+ )
160
+ resp = ""
161
+ while resp not in ["yes", "no"]:
162
+ resp = input(BOLD_RED + msg + RESET_TO_DEFAULT)
163
+ if str(resp) == "no":
164
+ ignore_duplicates = False
165
+ v_severity = config.validation_severity
166
+ if is_on_prod_like_server:
167
+ v_severity = ValidationSeverity.INFO
168
+ validation_passed = validate_parsed_resources(
169
+ parsed_resources=parsed_resources,
170
+ authorship_lookup=lookups.authorships,
171
+ permission_ids=list(lookups.permissions.keys()),
172
+ shortcode=config.shortcode,
173
+ config=ValidateDataConfig(
174
+ input_file,
175
+ save_graph_dir=None,
176
+ severity=v_severity,
177
+ ignore_duplicate_files_warning=ignore_duplicates,
178
+ is_on_prod_server=is_on_prod_like_server,
179
+ skip_ontology_validation=config.skip_ontology_validation,
180
+ do_not_request_resource_metadata_from_db=config.do_not_request_resource_metadata_from_db,
181
+ ),
182
+ auth=auth,
183
+ )
184
+ if not validation_passed:
185
+ return False
186
+ else:
187
+ logger.debug("SHACL validation was skipped.")
188
+ return True
114
189
 
115
190
 
116
191
  def _get_live_clients(
117
192
  con: Connection,
193
+ auth: AuthenticationClient,
118
194
  creds: ServerCredentials,
119
195
  shortcode: str,
120
196
  imgdir: str,
121
197
  ) -> UploadClients:
122
198
  ingest_client: AssetClient
123
- ingest_client = DspIngestClientLive(
124
- dsp_ingest_url=creds.dsp_ingest_url,
125
- token=con.get_token(),
126
- shortcode=shortcode,
127
- imgdir=imgdir,
128
- )
129
- project_client: ProjectClient = ProjectClientLive(con, shortcode)
130
- list_client: ListClient = ListClientLive(con, project_client.get_project_iri())
199
+ ingest_client = DspIngestClientLive(creds.dsp_ingest_url, auth, shortcode, imgdir)
200
+ project_client: ProjectClient = ProjectClientLive(auth.server, auth)
201
+ list_client: ListClient = ListClientLive(con, project_client.get_project_iri(shortcode))
202
+ legal_info_client: LegalInfoClient = LegalInfoClientLive(creds.server, shortcode, auth)
131
203
  return UploadClients(
132
204
  asset_client=ingest_client,
133
- project_client=project_client,
134
205
  list_client=list_client,
206
+ legal_info_client=legal_info_client,
135
207
  )
136
208
 
137
209
 
210
+ def enable_unknown_license_if_any_are_missing(
211
+ legal_info_client: LegalInfoClient, parsed_resources: list[ParsedResource]
212
+ ) -> None:
213
+ all_license_infos = [x.file_value.metadata.license_iri for x in parsed_resources if x.file_value]
214
+ if not all(all_license_infos):
215
+ legal_info_client.enable_unknown_license()
216
+ msg = (
217
+ "The files or iiif-uris in your data are missing some legal information. "
218
+ "To facilitate an upload on a test environment we are adding dummy information.\n"
219
+ "In order to be able to use the license 'unknown' in place of missing licenses, "
220
+ "we are enabling it for your project."
221
+ )
222
+ print(BOLD_YELLOW, msg, RESET_TO_DEFAULT)
223
+
224
+
138
225
  def execute_upload(clients: UploadClients, upload_state: UploadState) -> bool:
139
226
  """Execute an upload from an upload state, and clean up afterwards.
140
227
 
@@ -145,29 +232,33 @@ def execute_upload(clients: UploadClients, upload_state: UploadState) -> bool:
145
232
  Returns:
146
233
  True if all resources could be uploaded without errors; False if any resource could not be uploaded
147
234
  """
235
+ logger.debug("Start uploading data")
236
+ db_metrics = None
237
+ if clients.legal_info_client.server == "http://0.0.0.0:3333":
238
+ db_metrics = FusekiMetrics()
239
+ db_metrics.try_get_start_size()
240
+ _upload_copyright_holders(upload_state.pending_resources, clients.legal_info_client)
148
241
  _upload_resources(clients, upload_state)
242
+ if db_metrics is not None:
243
+ db_metrics.try_get_end_size()
244
+ communicate_fuseki_bloating(db_metrics)
149
245
  return _cleanup_upload(upload_state)
150
246
 
151
247
 
152
- def prepare_upload(
153
- root: etree._Element,
154
- ontology_client: OntologyClient,
155
- ) -> tuple[list[XMLResource], dict[str, Permissions], Stash | None]:
156
- """Do the consistency check, resolve circular references, and return the resources and permissions."""
157
- do_xml_consistency_check_with_ontology(onto_client=ontology_client, root=root)
158
- return _resolve_circular_references(
159
- root=root,
160
- con=ontology_client.con,
161
- default_ontology=ontology_client.default_ontology,
162
- )
248
+ def _upload_copyright_holders(resources: list[ProcessedResource], legal_info_client: LegalInfoClient) -> None:
249
+ logger.debug("Get and upload copyright holders")
250
+ copyright_holders = _get_copyright_holders(resources)
251
+ legal_info_client.post_copyright_holders(copyright_holders)
163
252
 
164
253
 
165
- def _validate_iiif_uris(root: etree._Element) -> None:
166
- uris = [uri for node in root.iter(tag="iiif-uri") if (uri := node.text)]
167
- if problems := IIIFUriValidator(uris).validate():
168
- msg = problems.get_msg()
169
- warnings.warn(DspToolsUserWarning(msg))
170
- logger.warning(msg)
254
+ def _get_copyright_holders(resources: list[ProcessedResource]) -> list[str]:
255
+ copyright_holders = set()
256
+ for res in resources:
257
+ if res.file_value:
258
+ copyright_holders.add(res.file_value.metadata.copyright_holder)
259
+ elif res.iiif_uri:
260
+ copyright_holders.add(res.iiif_uri.metadata.copyright_holder)
261
+ return [x for x in copyright_holders if x]
171
262
 
172
263
 
173
264
  def _cleanup_upload(upload_state: UploadState) -> bool:
@@ -180,7 +271,11 @@ def _cleanup_upload(upload_state: UploadState) -> bool:
180
271
  Returns:
181
272
  success status (deduced from failed_uploads and non-applied stash)
182
273
  """
183
- write_id2iri_mapping(upload_state.iri_resolver.lookup, upload_state.config.diagnostics)
274
+ write_id2iri_mapping(
275
+ id2iri_mapping=upload_state.iri_resolver.lookup,
276
+ shortcode=upload_state.config.shortcode,
277
+ diagnostics=upload_state.config.diagnostics,
278
+ )
184
279
  has_stash_failed = upload_state.pending_stash and not upload_state.pending_stash.is_empty()
185
280
  if not upload_state.failed_uploads and not has_stash_failed:
186
281
  success = True
@@ -205,28 +300,6 @@ def _cleanup_upload(upload_state: UploadState) -> bool:
205
300
  return success
206
301
 
207
302
 
208
- def _resolve_circular_references(
209
- root: etree._Element,
210
- con: Connection,
211
- default_ontology: str,
212
- ) -> tuple[list[XMLResource], dict[str, Permissions], Stash | None]:
213
- logger.info("Checking resources for circular references...")
214
- print(f"{datetime.now()}: Checking resources for circular references...")
215
- stash_lookup, upload_order = identify_circular_references(root)
216
- logger.info("Get data from XML...")
217
- resources, permissions_lookup = _get_data_from_xml(
218
- con=con,
219
- root=root,
220
- default_ontology=default_ontology,
221
- )
222
- sorting_lookup = {res.res_id: res for res in resources}
223
- resources = [sorting_lookup[res_id] for res_id in upload_order]
224
- logger.info("Stashing circular references...")
225
- print(f"{datetime.now()}: Stashing circular references...")
226
- stash = stash_circular_references(resources, stash_lookup, permissions_lookup)
227
- return resources, permissions_lookup, stash
228
-
229
-
230
303
  def _upload_resources(clients: UploadClients, upload_state: UploadState) -> None:
231
304
  """
232
305
  Iterates through all resources and tries to upload them to DSP.
@@ -241,20 +314,16 @@ def _upload_resources(clients: UploadClients, upload_state: UploadState) -> None
241
314
  BaseException: in case of an unhandled exception during resource creation
242
315
  XmlUploadInterruptedError: if the number of resources created is equal to the interrupt_after value
243
316
  """
244
- project_iri = clients.project_client.get_project_iri()
245
- project_onto_dict = clients.project_client.get_ontology_name_dict()
246
- listnode_lookup = clients.list_client.get_list_node_id_to_iri_lookup()
317
+ project_iri = clients.list_client.project_iri
247
318
 
248
- resource_create_client = ResourceCreateClient(
249
- con=clients.project_client.con,
250
- project_iri=project_iri,
251
- iri_resolver=upload_state.iri_resolver,
252
- project_onto_dict=project_onto_dict,
253
- permissions_lookup=upload_state.permissions_lookup,
254
- listnode_lookup=listnode_lookup,
255
- media_previously_ingested=upload_state.config.media_previously_uploaded,
319
+ iri_lookup = IRILookups(
320
+ project_iri=URIRef(project_iri),
321
+ id_to_iri=upload_state.iri_resolver,
256
322
  )
257
323
 
324
+ resource_create_client = ResourceCreateClient(
325
+ con=clients.list_client.con,
326
+ )
258
327
  progress_bar = tqdm(upload_state.pending_resources.copy(), desc="Creating Resources", dynamic_ncols=True)
259
328
  try:
260
329
  for creation_attempts_of_this_round, resource in enumerate(progress_bar):
@@ -263,104 +332,61 @@ def _upload_resources(clients: UploadClients, upload_state: UploadState) -> None
263
332
  resource=resource,
264
333
  ingest_client=clients.asset_client,
265
334
  resource_create_client=resource_create_client,
335
+ iri_lookups=iri_lookup,
266
336
  creation_attempts_of_this_round=creation_attempts_of_this_round,
267
337
  )
268
338
  progress_bar.set_description(f"Creating Resources (failed: {len(upload_state.failed_uploads)})")
269
339
  if upload_state.pending_stash:
270
- _upload_stash(upload_state, clients.project_client)
340
+ _upload_stash(upload_state, clients.list_client.con)
271
341
  except XmlUploadInterruptedError as err:
272
342
  _handle_upload_error(err, upload_state)
273
343
 
274
344
 
275
- def _get_data_from_xml(
276
- con: Connection,
277
- root: etree._Element,
278
- default_ontology: str,
279
- ) -> tuple[list[XMLResource], dict[str, Permissions]]:
280
- proj_context = _get_project_context_from_server(connection=con, shortcode=root.attrib["shortcode"])
281
- permissions = _extract_permissions_from_xml(root, proj_context)
282
- resources = _extract_resources_from_xml(root, default_ontology)
283
- permissions_lookup = {name: perm.get_permission_instance() for name, perm in permissions.items()}
284
- return resources, permissions_lookup
285
-
286
-
287
345
  def _upload_stash(
288
346
  upload_state: UploadState,
289
- project_client: ProjectClient,
347
+ con: Connection,
290
348
  ) -> None:
291
349
  if upload_state.pending_stash and upload_state.pending_stash.standoff_stash:
292
- upload_stashed_xml_texts(upload_state, project_client.con)
293
- context = get_json_ld_context_for_project(project_client.get_ontology_name_dict())
350
+ upload_stashed_xml_texts(upload_state, con)
294
351
  if upload_state.pending_stash and upload_state.pending_stash.link_value_stash:
295
- upload_stashed_resptr_props(upload_state, project_client.con, context)
296
-
297
-
298
- def _get_project_context_from_server(connection: Connection, shortcode: str) -> ProjectContext:
299
- """
300
- This function retrieves the project context previously uploaded on the server (json file)
301
-
302
- Args:
303
- connection: connection to the server
304
- shortcode: shortcode of the project
305
-
306
- Returns:
307
- Project context
308
-
309
- Raises:
310
- UserError: If the project was not previously uploaded on the server
311
- """
312
- try:
313
- proj_context = ProjectContext(con=connection, shortcode=shortcode)
314
- except BaseError:
315
- logger.opt(exception=True).error("Unable to retrieve project context from DSP server")
316
- raise UserError("Unable to retrieve project context from DSP server") from None
317
- return proj_context
318
-
319
-
320
- def _extract_permissions_from_xml(root: etree._Element, proj_context: ProjectContext) -> dict[str, XmlPermission]:
321
- permission_ele = list(root.iter(tag="permissions"))
322
- permissions = [XmlPermission(permission, proj_context) for permission in permission_ele]
323
- return {permission.permission_id: permission for permission in permissions}
324
-
325
-
326
- def _extract_resources_from_xml(root: etree._Element, default_ontology: str) -> list[XMLResource]:
327
- resources = list(root.iter(tag="resource"))
328
- return [XMLResource.from_node(res, default_ontology) for res in resources]
352
+ upload_stashed_resptr_props(upload_state, con)
329
353
 
330
354
 
331
355
  def _upload_one_resource(
332
356
  upload_state: UploadState,
333
- resource: XMLResource,
357
+ resource: ProcessedResource,
334
358
  ingest_client: AssetClient,
335
359
  resource_create_client: ResourceCreateClient,
360
+ iri_lookups: IRILookups,
336
361
  creation_attempts_of_this_round: int,
337
362
  ) -> None:
338
- try:
339
- if resource.bitstream:
340
- success, media_info = ingest_client.get_bitstream_info(
341
- resource.bitstream, upload_state.permissions_lookup, resource.label, resource.res_id
342
- )
343
- else:
344
- success, media_info = True, None
345
-
346
- if not success:
363
+ media_info = None
364
+ if resource.file_value:
365
+ try:
366
+ ingest_result = ingest_client.get_bitstream_info(resource.file_value)
367
+ except PermanentConnectionError as err:
368
+ _handle_permanent_connection_error(err)
369
+ except KeyboardInterrupt:
370
+ _handle_keyboard_interrupt()
371
+ if not ingest_result:
347
372
  upload_state.failed_uploads.append(resource.res_id)
348
373
  return
349
- except PermanentConnectionError as err:
350
- _handle_permanent_connection_error(err)
351
- except KeyboardInterrupt:
352
- _handle_keyboard_interrupt()
374
+ media_info = ingest_result
353
375
 
354
376
  iri = None
355
377
  try:
356
- iri = resource_create_client.create_resource(resource, media_info)
378
+ serialised_resource = create_resource_with_values(
379
+ resource=resource, bitstream_information=media_info, lookups=iri_lookups
380
+ )
381
+ logger.info(f"Attempting to create resource {resource.res_id} (label: {resource.label})...")
382
+ iri = resource_create_client.create_resource(serialised_resource, resource_has_bitstream=bool(media_info))
357
383
  except (PermanentTimeOutError, KeyboardInterrupt) as err:
358
384
  _handle_permanent_timeout_or_keyboard_interrupt(err, resource.res_id)
359
385
  except PermanentConnectionError as err:
360
386
  _handle_permanent_connection_error(err)
361
387
  except Exception as err: # noqa: BLE001 (blind-except)
362
388
  err_msg = err.message if isinstance(err, BaseError) else None
363
- _handle_resource_creation_failure(resource, err_msg)
389
+ _inform_about_resource_creation_failure(resource, err_msg)
364
390
 
365
391
  try:
366
392
  _tidy_up_resource_creation_idempotent(upload_state, iri, resource)
@@ -374,6 +400,7 @@ def _handle_permanent_connection_error(err: PermanentConnectionError) -> Never:
374
400
  msg = "Lost connection to DSP server, probably because the server is down. "
375
401
  msg += f"Please continue later with 'resume-xmlupload'. Reason for this failure: {err.message}"
376
402
  logger.error(msg)
403
+ msg += f"\nSee {WARNINGS_SAVEPATH} for more information."
377
404
  raise XmlUploadInterruptedError(msg) from None
378
405
 
379
406
 
@@ -410,7 +437,7 @@ def _interrupt_if_indicated(upload_state: UploadState, creation_attempts_of_this
410
437
  def _tidy_up_resource_creation_idempotent(
411
438
  upload_state: UploadState,
412
439
  iri: str | None,
413
- resource: XMLResource,
440
+ resource: ProcessedResource,
414
441
  ) -> None:
415
442
  previous_successful = len(upload_state.iri_resolver.lookup)
416
443
  previous_failed = len(upload_state.failed_uploads)
@@ -431,15 +458,10 @@ def _tidy_up_resource_creation_idempotent(
431
458
  upload_state.pending_resources.remove(resource)
432
459
 
433
460
 
434
- def _handle_resource_creation_failure(resource: XMLResource, err_msg: str | None) -> None:
435
- msg = f"{datetime.now()}: WARNING: Unable to create resource '{resource.label}' (ID: '{resource.res_id}')"
461
+ def _inform_about_resource_creation_failure(resource: ProcessedResource, err_msg: str | None) -> None:
462
+ log_msg = f"Unable to create resource '{resource.label}' ({resource.res_id})\n"
436
463
  if err_msg:
437
- msg = f"{msg}: {err_msg}"
438
- log_msg = (
439
- f"Unable to create resource '{resource.label}' ({resource.res_id})\n"
440
- f"Resource details:\n{vars(resource)}\n"
441
- f"Property details:\n" + "\n".join([str(vars(prop)) for prop in resource.properties])
442
- )
464
+ log_msg += err_msg
443
465
  logger.exception(log_msg)
444
466
 
445
467
 
@@ -476,7 +498,7 @@ def _handle_upload_error(err: BaseException, upload_state: UploadState) -> None:
476
498
  msg += f"Independently from this, there were some resources that could not be uploaded: {failed}\n"
477
499
 
478
500
  if exit_code == 1:
479
- logger.exception(msg)
501
+ logger.error(msg)
480
502
  else:
481
503
  logger.info(msg)
482
504
  print(msg)
File without changes
@@ -0,0 +1,69 @@
1
+ import os
2
+ from datetime import datetime
3
+ from pathlib import Path
4
+
5
+ from dotenv import find_dotenv
6
+ from dotenv import load_dotenv
7
+ from loguru import logger
8
+
9
+ load_dotenv(dotenv_path=find_dotenv(usecwd=True))
10
+
11
+
12
+ def _make_and_get_logs_directory() -> Path:
13
+ """Get the base .dsp-tools directory, creating it if it doesn't exist."""
14
+ base_dir = Path.home() / ".dsp-tools" / "logs"
15
+ base_dir.mkdir(exist_ok=True, parents=True)
16
+ return base_dir
17
+
18
+
19
+ timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S-%f")[:-3]
20
+
21
+ LOGGER_SAVEPATH = (_make_and_get_logs_directory() / f"{timestamp}_logging.log").absolute()
22
+ WARNINGS_SAVEPATH = Path("warnings.log")
23
+
24
+
25
+ def logger_config() -> None:
26
+ """
27
+ This function configures the log files.
28
+ Currently, there are three sinks:
29
+ - timestamp_logging.log in ~/.dsp-tools/logs/ contains the entire stack-trace
30
+ - warnings.log in the cwd only with level warning and higher for the user (no stack-trace)
31
+ OR a complete logging.log file with the stack-trace if configured in the .env
32
+ - print output on the terminal, formatted the same as the warnings.log
33
+ """
34
+ # If this is not removed, the default formatting is also printed out on the terminal
35
+ logger.remove()
36
+
37
+ text_format = "<level>{time:YYYY-MM-DD HH:mm:ss.SSS} | {level: <8} | {message}</level>"
38
+ rotation_size = "100 MB"
39
+
40
+ logger.add(
41
+ sink=LOGGER_SAVEPATH,
42
+ format=text_format,
43
+ backtrace=True,
44
+ diagnose=True,
45
+ delay=True,
46
+ )
47
+
48
+ additional_log = str(os.getenv("DSP_TOOLS_SAVE_ADDITIONAL_LOG_FILE_IN_CWD"))
49
+ if additional_log.lower() == "true":
50
+ logger.add(
51
+ sink=Path("logging.log"),
52
+ format=text_format,
53
+ backtrace=True,
54
+ diagnose=True,
55
+ rotation=rotation_size,
56
+ retention=2,
57
+ delay=True,
58
+ )
59
+ else:
60
+ logger.add(
61
+ sink=WARNINGS_SAVEPATH,
62
+ level="WARNING",
63
+ format=text_format,
64
+ backtrace=False,
65
+ diagnose=False,
66
+ rotation=rotation_size,
67
+ retention=2,
68
+ delay=True,
69
+ )
@@ -1,7 +1,8 @@
1
1
  import warnings
2
2
  from typing import TextIO
3
3
 
4
- from dsp_tools.models.custom_warnings import DspToolsWarning
4
+ from dsp_tools.error.custom_warnings import DspToolsWarning
5
+ from dsp_tools.error.xmllib_warnings import XmllibUserInfoBase
5
6
 
6
7
 
7
8
  def initialize_warnings() -> None:
@@ -23,6 +24,8 @@ def initialize_warnings() -> None:
23
24
  ) -> None:
24
25
  if issubclass(category, DspToolsWarning):
25
26
  category.showwarning(str(message))
27
+ elif issubclass(category, XmllibUserInfoBase):
28
+ category.showwarning(str(message))
26
29
  else:
27
30
  built_in_showwarning(message, category, filename, lineno, file, line)
28
31
 
File without changes