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,134 @@
1
+ from loguru import logger
2
+
3
+ from dsp_tools.clients.permissions_client import PermissionsClient
4
+ from dsp_tools.utils.ansi_colors import BOLD
5
+ from dsp_tools.utils.ansi_colors import RESET_TO_DEFAULT
6
+ from dsp_tools.utils.rdf_constants import KNORA_ADMIN_PREFIX
7
+
8
+
9
+ def create_default_permissions(
10
+ perm_client: PermissionsClient,
11
+ default_permissions: str,
12
+ default_permissions_overrule: dict[str, str | list[str]] | None,
13
+ shortcode: str,
14
+ ) -> bool:
15
+ print(BOLD + "Processing default permissions:" + RESET_TO_DEFAULT)
16
+ logger.info("Processing default permissions:")
17
+ if not _delete_existing_doaps(perm_client):
18
+ print(" WARNING: Cannot delete the existing default permissions")
19
+ logger.warning("Cannot delete the existing default permissions")
20
+ return False
21
+ if not _create_new_doap(perm_client, default_permissions):
22
+ print(" WARNING: Cannot create default permissions")
23
+ logger.warning("Cannot create default permissions")
24
+ return False
25
+ if default_permissions_overrule:
26
+ if not _create_overrules(perm_client, default_permissions_overrule, shortcode):
27
+ print(" WARNING: Cannot create default permissions overrules")
28
+ logger.warning("Cannot create default permissions overrules")
29
+ return False
30
+ print(" Default permissions have been set")
31
+ logger.info("Default permissions have been set")
32
+ return True
33
+
34
+
35
+ def _delete_existing_doaps(perm_client: PermissionsClient) -> bool:
36
+ if not (doaps := perm_client.get_project_doaps()):
37
+ return False
38
+ existing_doap_iris: list[str] = [x["iri"] for x in doaps]
39
+ for iri in existing_doap_iris:
40
+ if not perm_client.delete_doap(iri):
41
+ # don't continue with the others, it's better to stop DOAP handling immediately, to avoid a mess
42
+ return False
43
+ return True
44
+
45
+
46
+ def _create_new_doap(perm_client: PermissionsClient, default_permissions: str) -> bool:
47
+ perm = [
48
+ {"additionalInformation": f"{KNORA_ADMIN_PREFIX}ProjectAdmin", "name": "CR", "permissionCode": None},
49
+ {"additionalInformation": f"{KNORA_ADMIN_PREFIX}ProjectMember", "name": "D", "permissionCode": None},
50
+ ]
51
+ if default_permissions == "public":
52
+ perm.append({"additionalInformation": f"{KNORA_ADMIN_PREFIX}KnownUser", "name": "V", "permissionCode": None})
53
+ perm.append({"additionalInformation": f"{KNORA_ADMIN_PREFIX}UnknownUser", "name": "V", "permissionCode": None})
54
+ payload = {
55
+ "forGroup": f"{KNORA_ADMIN_PREFIX}ProjectMember",
56
+ "forProject": perm_client.proj_iri,
57
+ "hasPermissions": perm,
58
+ }
59
+ return perm_client.create_new_doap(payload)
60
+
61
+
62
+ def _create_overrules(
63
+ perm_client: PermissionsClient, default_permissions_overrule: dict[str, str | list[str]], shortcode: str
64
+ ) -> bool:
65
+ overall_success = True
66
+
67
+ # Handle private overrules
68
+ for entity in default_permissions_overrule.get("private", []):
69
+ first_letter = entity.split(":")[-1][0]
70
+ is_res = first_letter.upper() == first_letter
71
+ entity_iri = _get_iri_from_prefixed_name(entity, shortcode, perm_client.auth.server)
72
+ if is_res:
73
+ success = _create_one_private_overrule(perm_client=perm_client, res_iri=entity_iri, prop_iri=None)
74
+ else:
75
+ success = _create_one_private_overrule(perm_client=perm_client, res_iri=None, prop_iri=entity_iri)
76
+ if not success:
77
+ overall_success = False
78
+
79
+ # Handle limited_view overrules
80
+ if not (limited_view := default_permissions_overrule.get("limited_view")):
81
+ return overall_success
82
+ if limited_view == "all":
83
+ success = _create_one_limited_view_overrule(perm_client=perm_client, img_class_iri=None)
84
+ if not success:
85
+ overall_success = False
86
+ else:
87
+ # limited_view is a list of prefixed class names
88
+ for prefixed_img_class in limited_view:
89
+ img_class_iri = _get_iri_from_prefixed_name(prefixed_img_class, shortcode, perm_client.auth.server)
90
+ success = _create_one_limited_view_overrule(perm_client=perm_client, img_class_iri=img_class_iri)
91
+ if not success:
92
+ overall_success = False
93
+
94
+ return overall_success
95
+
96
+
97
+ def _create_one_private_overrule(perm_client: PermissionsClient, res_iri: str | None, prop_iri: str | None) -> bool:
98
+ perm = [
99
+ {"additionalInformation": f"{KNORA_ADMIN_PREFIX}ProjectAdmin", "name": "CR", "permissionCode": None},
100
+ {"additionalInformation": f"{KNORA_ADMIN_PREFIX}ProjectMember", "name": "D", "permissionCode": None},
101
+ ]
102
+ payload = {
103
+ "forProperty": prop_iri,
104
+ "forResourceClass": res_iri,
105
+ "forProject": perm_client.proj_iri,
106
+ "hasPermissions": perm,
107
+ }
108
+ return perm_client.create_new_doap(payload)
109
+
110
+
111
+ def _create_one_limited_view_overrule(perm_client: PermissionsClient, img_class_iri: str | None) -> bool:
112
+ # This makes only sense for the knora-api:hasStillImageFileValue property of image classes
113
+ # To set it for all image classes, set img_class_iri to None
114
+ perm = [
115
+ {"additionalInformation": f"{KNORA_ADMIN_PREFIX}ProjectAdmin", "name": "CR", "permissionCode": None},
116
+ {"additionalInformation": f"{KNORA_ADMIN_PREFIX}ProjectMember", "name": "D", "permissionCode": None},
117
+ {"additionalInformation": f"{KNORA_ADMIN_PREFIX}KnownUser", "name": "RV", "permissionCode": None},
118
+ {"additionalInformation": f"{KNORA_ADMIN_PREFIX}UnknownUser", "name": "RV", "permissionCode": None},
119
+ ]
120
+ payload = {
121
+ "forProperty": "http://api.knora.org/ontology/knora-api/v2#hasStillImageFileValue",
122
+ "forResourceClass": img_class_iri,
123
+ "forProject": perm_client.proj_iri,
124
+ "hasPermissions": perm,
125
+ }
126
+ return perm_client.create_new_doap(payload)
127
+
128
+
129
+ def _get_iri_from_prefixed_name(prefixed_name: str, proj_shortcode: str, server: str) -> str:
130
+ onto, name = prefixed_name.split(":")
131
+ host_iri = server.replace("https://", "http://")
132
+ if onto == "knora-api":
133
+ return f"http://api.knora.org/ontology/knora-api/v2#{name}"
134
+ return f"{host_iri}/ontology/{proj_shortcode}/{onto}/v2#{name}"
@@ -0,0 +1,165 @@
1
+ from typing import Any
2
+
3
+ from loguru import logger
4
+ from tqdm import tqdm
5
+
6
+ from dsp_tools.clients.authentication_client import AuthenticationClient
7
+ from dsp_tools.clients.group_user_clients import GroupClient
8
+ from dsp_tools.clients.group_user_clients import UserClient
9
+ from dsp_tools.clients.group_user_clients_live import UserClientLive
10
+ from dsp_tools.commands.create.models.create_problems import CollectedProblems
11
+ from dsp_tools.commands.create.models.create_problems import CreateProblem
12
+ from dsp_tools.commands.create.models.create_problems import UploadProblem
13
+ from dsp_tools.commands.create.models.create_problems import UploadProblemType
14
+ from dsp_tools.commands.create.models.parsed_project import ParsedGroup
15
+ from dsp_tools.commands.create.models.parsed_project import ParsedUser
16
+ from dsp_tools.commands.create.models.parsed_project import ParsedUserMemberShipInfo
17
+ from dsp_tools.commands.create.models.server_project_info import GroupNameToIriLookup
18
+ from dsp_tools.commands.create.models.server_project_info import UserNameToIriLookup
19
+ from dsp_tools.commands.create.serialisation.project import serialise_one_group
20
+ from dsp_tools.commands.create.serialisation.project import serialise_one_user_for_creation
21
+ from dsp_tools.utils.ansi_colors import BOLD
22
+ from dsp_tools.utils.ansi_colors import RESET_TO_DEFAULT
23
+
24
+
25
+ def create_users(
26
+ users: list[ParsedUser],
27
+ user_memberships: list[ParsedUserMemberShipInfo],
28
+ group_lookup: GroupNameToIriLookup,
29
+ auth: AuthenticationClient,
30
+ project_iri: str,
31
+ ) -> CollectedProblems | None:
32
+ print(BOLD + "Processing user section:" + RESET_TO_DEFAULT)
33
+ client = UserClientLive(auth.server, auth)
34
+ user_to_iri, problems = _create_all_users(users, client)
35
+ membership_problems = _add_all_memberships(user_memberships, user_to_iri, group_lookup, client, project_iri)
36
+ problems.extend(membership_problems)
37
+ if problems:
38
+ return CollectedProblems("During the creation of the users the following problems occurred:", problems)
39
+ return None
40
+
41
+
42
+ def _create_all_users(users: list[ParsedUser], client: UserClient) -> tuple[UserNameToIriLookup, list[CreateProblem]]:
43
+ problems: list[CreateProblem] = []
44
+ user_to_iri = UserNameToIriLookup()
45
+ progress_bar = tqdm(users, desc=" Creating users", dynamic_ncols=True)
46
+ logger.debug("Creating users")
47
+ for usr in progress_bar:
48
+ result = _create_one_user(usr, client)
49
+ if result is None:
50
+ problems.append(UploadProblem(usr.username, UploadProblemType.USER_COULD_NOT_BE_CREATED))
51
+ else:
52
+ user_to_iri.add_iri(usr.username, result)
53
+ return user_to_iri, problems
54
+
55
+
56
+ def _create_one_user(user: ParsedUser, client: UserClient) -> str | None:
57
+ if user_iri := client.get_user_iri_by_username(user.username):
58
+ return user_iri
59
+ serialised_user = serialise_one_user_for_creation(user)
60
+ return client.post_new_user(serialised_user)
61
+
62
+
63
+ def _add_all_memberships(
64
+ memberships: list[ParsedUserMemberShipInfo],
65
+ user_to_iri: UserNameToIriLookup,
66
+ group_lookup: GroupNameToIriLookup,
67
+ client: UserClient,
68
+ project_iri: str,
69
+ ) -> list[CreateProblem]:
70
+ problems: list[CreateProblem] = []
71
+ progress_bar = tqdm(memberships, desc=" Updating user information", dynamic_ncols=True)
72
+ logger.debug("Updating user information")
73
+ for memb in progress_bar:
74
+ if usr_iri := user_to_iri.get_iri(memb.username):
75
+ result = _add_user_to_project_memberships(memb, usr_iri, project_iri, client)
76
+ problems.extend(result)
77
+ if memb.groups:
78
+ result = _add_user_to_custom_groups(memb, usr_iri, client, group_lookup)
79
+ problems.extend(result)
80
+ else:
81
+ logger.debug(f"IRI of user '{memb.username}' could not be found, no project membership added.")
82
+ return problems
83
+
84
+
85
+ def _add_user_to_project_memberships(
86
+ membership: ParsedUserMemberShipInfo, user_iri: str, project_iri: str, client: UserClient
87
+ ) -> list[CreateProblem]:
88
+ problems: list[CreateProblem] = []
89
+ membership_good = client.add_user_as_project_member(user_iri, project_iri)
90
+ if not membership_good:
91
+ problems.append(UploadProblem(membership.username, UploadProblemType.PROJECT_MEMBERSHIP_COULD_NOT_BE_ADDED))
92
+ if membership.is_admin:
93
+ good = client.add_user_as_project_admin(user_iri, project_iri)
94
+ if not good:
95
+ problems.append(UploadProblem(membership.username, UploadProblemType.PROJECT_ADMIN_COULD_NOT_BE_ADDED))
96
+ return problems
97
+
98
+
99
+ def _add_user_to_custom_groups(
100
+ membership: ParsedUserMemberShipInfo, user_iri: str, client: UserClient, group_lookup: GroupNameToIriLookup
101
+ ) -> list[CreateProblem]:
102
+ problems: list[CreateProblem] = []
103
+ groups_not_found = []
104
+ groups_iris = []
105
+ for gr in membership.groups:
106
+ if found := group_lookup.get_iri(gr):
107
+ groups_iris.append(found)
108
+ else:
109
+ groups_not_found.append(gr)
110
+ if groups_iris:
111
+ group_good = client.add_user_to_custom_groups(user_iri, groups_iris)
112
+ if not group_good:
113
+ problems.append(UploadProblem(membership.username, UploadProblemType.USER_COULD_NOT_BE_ADDED_TO_GROUP))
114
+ if groups_not_found:
115
+ problems.append(UploadProblem(membership.username, UploadProblemType.USER_GROUPS_NOT_FOUND))
116
+ return problems
117
+
118
+
119
+ def create_groups(
120
+ groups: list[ParsedGroup],
121
+ group_client: GroupClient,
122
+ project_iri: str,
123
+ group_lookup: GroupNameToIriLookup,
124
+ ) -> tuple[GroupNameToIriLookup, CollectedProblems | None]:
125
+ problems: list[CreateProblem] = []
126
+ print(BOLD + "Processing group section:" + RESET_TO_DEFAULT)
127
+ progress_bar = tqdm(groups, desc=" Creating groups", dynamic_ncols=True)
128
+ logger.debug("Creating groups")
129
+ for gr in progress_bar:
130
+ if group_lookup.check_exists(gr.name):
131
+ logger.debug(f"Group with the name '{gr.name}' already exists, skipping.")
132
+ continue
133
+ result = _create_one_group(gr, group_client, project_iri)
134
+ if isinstance(result, UploadProblem):
135
+ problems.append(result)
136
+ else:
137
+ group_lookup.add_iri(gr.name, result)
138
+ all_problems = None
139
+ if problems:
140
+ all_problems = CollectedProblems("During the creation of the groups the following problems happened:", problems)
141
+ return group_lookup, all_problems
142
+
143
+
144
+ def _create_one_group(group: ParsedGroup, group_client: GroupClient, project_iri: str) -> str | UploadProblem:
145
+ serialised = serialise_one_group(group, project_iri)
146
+ new_iri = group_client.create_new_group(serialised)
147
+ if new_iri:
148
+ return new_iri
149
+ return UploadProblem(group.name, UploadProblemType.GROUP_COULD_NOT_BE_CREATED)
150
+
151
+
152
+ def get_existing_group_to_iri_lookup(
153
+ group_client: GroupClient, project_iri: str, shortname: str
154
+ ) -> GroupNameToIriLookup:
155
+ all_groups = group_client.get_all_groups()
156
+ return _construct_group_lookup(all_groups, project_iri, shortname)
157
+
158
+
159
+ def _construct_group_lookup(all_groups: list[dict[str, Any]], project_iri: str, shortname: str) -> GroupNameToIriLookup:
160
+ lookup = GroupNameToIriLookup({}, shortname)
161
+ for group in all_groups:
162
+ group_project_iri = group["project"]["id"]
163
+ if group_project_iri == project_iri:
164
+ lookup.add_iri(group["name"], group["id"])
165
+ return lookup
@@ -0,0 +1,163 @@
1
+ import warnings
2
+ from typing import Any
3
+
4
+ from loguru import logger
5
+ from tqdm import tqdm
6
+
7
+ from dsp_tools.clients.authentication_client import AuthenticationClient
8
+ from dsp_tools.clients.list_client import ListCreateClient
9
+ from dsp_tools.clients.list_client_live import ListCreateClientLive
10
+ from dsp_tools.clients.list_client_live import ListGetClientLive
11
+ from dsp_tools.commands.create.models.create_problems import CollectedProblems
12
+ from dsp_tools.commands.create.models.create_problems import CreateProblem
13
+ from dsp_tools.commands.create.models.create_problems import UploadProblem
14
+ from dsp_tools.commands.create.models.create_problems import UploadProblemType
15
+ from dsp_tools.commands.create.models.create_problems import UserInformation
16
+ from dsp_tools.commands.create.models.create_problems import UserInformationMessage
17
+ from dsp_tools.commands.create.models.parsed_project import ParsedList
18
+ from dsp_tools.commands.create.models.parsed_project import ParsedListNode
19
+ from dsp_tools.commands.create.models.parsed_project import ParsedNodeInfo
20
+ from dsp_tools.commands.create.models.server_project_info import ListNameToIriLookup
21
+ from dsp_tools.error.custom_warnings import DspToolsUnexpectedStatusCodeWarning
22
+ from dsp_tools.error.exceptions import FatalNonOkApiResponseCode
23
+ from dsp_tools.utils.ansi_colors import BOLD
24
+ from dsp_tools.utils.ansi_colors import BOLD_CYAN
25
+ from dsp_tools.utils.ansi_colors import RESET_TO_DEFAULT
26
+
27
+
28
+ def create_lists(
29
+ parsed_lists: list[ParsedList], shortcode: str, auth: AuthenticationClient, project_iri: str
30
+ ) -> tuple[ListNameToIriLookup, CollectedProblems | None]:
31
+ print(BOLD + "Processing list section:" + RESET_TO_DEFAULT)
32
+ name2iri = get_existing_lists_on_server(shortcode, auth)
33
+ if not parsed_lists:
34
+ return name2iri, None
35
+ lists_to_create, existing_info = _filter_out_existing_lists(parsed_lists, name2iri)
36
+ if existing_info:
37
+ _print_existing_list_info(existing_info)
38
+ if not lists_to_create:
39
+ msg = " All lists defined in the project are already on the server, no list was uploaded."
40
+ logger.warning(msg)
41
+ print(BOLD_CYAN + msg + RESET_TO_DEFAULT)
42
+ return name2iri, None
43
+
44
+ create_client = ListCreateClientLive(auth.server, auth, project_iri)
45
+
46
+ all_problems: list[CreateProblem] = []
47
+ progress_bar = tqdm(lists_to_create, desc=" Creating lists", dynamic_ncols=True)
48
+ for new_lst in progress_bar:
49
+ list_iri, problems = _create_new_list(new_lst, create_client, project_iri)
50
+ if list_iri is None:
51
+ problems.extend(problems)
52
+ else:
53
+ name2iri.add_iri(new_lst.list_info.name, list_iri)
54
+
55
+ create_problems = None
56
+ if all_problems:
57
+ create_problems = CollectedProblems("The following problems occurred during list creation:", all_problems)
58
+ return name2iri, create_problems
59
+
60
+
61
+ def _print_existing_list_info(existing_lists: list[UserInformation]) -> None:
62
+ lists = ", ".join([x.focus_object for x in existing_lists])
63
+ msg = f" The following lists already exist on the server and will be skipped: {lists}"
64
+ logger.info(msg)
65
+ print(BOLD_CYAN + msg + RESET_TO_DEFAULT)
66
+
67
+
68
+ def get_existing_lists_on_server(shortcode: str, auth: AuthenticationClient) -> ListNameToIriLookup:
69
+ client = ListGetClientLive(auth.server, shortcode)
70
+ try:
71
+ name2iri_dict = client.get_all_list_iris_and_names()
72
+ return ListNameToIriLookup(name2iri_dict)
73
+ except FatalNonOkApiResponseCode as e:
74
+ logger.exception(e)
75
+ warnings.warn(
76
+ DspToolsUnexpectedStatusCodeWarning(
77
+ "Could not retrieve existing lists on server. "
78
+ "We will not be able to create any properties that require a list that is not defined in the JSON."
79
+ )
80
+ )
81
+ return ListNameToIriLookup({})
82
+
83
+
84
+ def _filter_out_existing_lists(
85
+ parsed_lists: list[ParsedList], name2iri: ListNameToIriLookup
86
+ ) -> tuple[list[ParsedList], list[UserInformation]]:
87
+ lists_to_create: list[ParsedList] = []
88
+ existing_info: list[UserInformation] = []
89
+
90
+ for parsed_list in parsed_lists:
91
+ if name2iri.check_list_exists(parsed_list.list_info.name):
92
+ existing_info.append(
93
+ UserInformation(parsed_list.list_info.name, UserInformationMessage.LIST_EXISTS_ON_SERVER)
94
+ )
95
+ else:
96
+ lists_to_create.append(parsed_list)
97
+
98
+ return lists_to_create, existing_info
99
+
100
+
101
+ def _create_new_list(
102
+ parsed_list: ParsedList, create_client: ListCreateClient, project_iri: str
103
+ ) -> tuple[str | None, list[UploadProblem]]:
104
+ serialised = _serialise_list(parsed_list.list_info, project_iri)
105
+ new_iri = create_client.create_new_list(serialised)
106
+
107
+ if new_iri is None:
108
+ problems: list[UploadProblem] = [
109
+ UploadProblem(parsed_list.list_info.name, UploadProblemType.LIST_COULD_NOT_BE_CREATED)
110
+ ]
111
+ return None, problems
112
+
113
+ node_problems = []
114
+ if parsed_list.children:
115
+ node_problems = _create_node_tree(parsed_list.children, new_iri, create_client, project_iri)
116
+
117
+ return new_iri, node_problems
118
+
119
+
120
+ def _serialise_list(parsed_list_info: ParsedNodeInfo, project_iri: str) -> dict[str, Any]:
121
+ node_dict = {
122
+ "projectIri": project_iri,
123
+ "name": parsed_list_info.name,
124
+ "labels": _convert_to_api_format(parsed_list_info.labels),
125
+ }
126
+ if parsed_list_info.comments:
127
+ node_dict["comments"] = _convert_to_api_format(parsed_list_info.comments)
128
+ return node_dict
129
+
130
+
131
+ def _create_node_tree(
132
+ nodes: list[ParsedListNode], parent_iri: str, create_client: ListCreateClient, project_iri: str
133
+ ) -> list[UploadProblem]:
134
+ problems: list[UploadProblem] = []
135
+
136
+ for node in nodes:
137
+ serialised = _serialise_node(node.node_info, parent_iri, project_iri)
138
+ node_iri = create_client.add_list_node(serialised, parent_iri)
139
+ if node_iri is None:
140
+ problems.append(UploadProblem(node.node_info.name, UploadProblemType.LIST_NODE_COULD_NOT_BE_CREATED))
141
+ continue
142
+
143
+ if node.children:
144
+ child_problems = _create_node_tree(node.children, node_iri, create_client, project_iri)
145
+ problems.extend(child_problems)
146
+
147
+ return problems
148
+
149
+
150
+ def _serialise_node(node_info: ParsedNodeInfo, parent_iri: str, project_iri: str) -> dict[str, Any]:
151
+ node_dict = {
152
+ "parentNodeIri": parent_iri,
153
+ "projectIri": project_iri,
154
+ "name": node_info.name,
155
+ "labels": _convert_to_api_format(node_info.labels),
156
+ }
157
+ if node_info.comments:
158
+ node_dict["comments"] = _convert_to_api_format(node_info.comments)
159
+ return node_dict
160
+
161
+
162
+ def _convert_to_api_format(lang_dict: dict[str, str]) -> list[dict[str, str]]:
163
+ return [{"value": value, "language": lang} for lang, value in lang_dict.items()]
@@ -0,0 +1,12 @@
1
+ from rdflib import OWL
2
+ from rdflib import Literal
3
+
4
+ from dsp_tools.commands.create.models.parsed_ontology import Cardinality
5
+ from dsp_tools.commands.create.models.rdf_ontology import RdfCardinalityRestriction
6
+
7
+ PARSED_CARDINALITY_TO_RDF = {
8
+ Cardinality.C_1: RdfCardinalityRestriction(OWL.cardinality, Literal(1)),
9
+ Cardinality.C_0_1: RdfCardinalityRestriction(OWL.maxCardinality, Literal(1)),
10
+ Cardinality.C_1_N: RdfCardinalityRestriction(OWL.minCardinality, Literal(1)),
11
+ Cardinality.C_0_N: RdfCardinalityRestriction(OWL.minCardinality, Literal(0)),
12
+ }
@@ -0,0 +1,74 @@
1
+ import time
2
+ from http import HTTPStatus
3
+
4
+ import regex
5
+ import rustworkx as rx
6
+ from loguru import logger
7
+ from rdflib import OWL
8
+ from rdflib import RDF
9
+ from rdflib import Graph
10
+ from rdflib import URIRef
11
+
12
+ from dsp_tools.clients.ontology_clients import OntologyCreateClient
13
+ from dsp_tools.clients.ontology_get_client_live import OntologyGetClientLive
14
+ from dsp_tools.commands.create.exceptions import CircularOntologyDependency
15
+ from dsp_tools.commands.create.models.server_project_info import OntoLastModDateLookup
16
+ from dsp_tools.commands.create.models.server_project_info import ProjectIriLookup
17
+ from dsp_tools.error.exceptions import ProjectOntologyNotFound
18
+ from dsp_tools.utils.request_utils import ResponseCodeAndText
19
+
20
+
21
+ def should_retry_request(response: ResponseCodeAndText) -> bool:
22
+ if response.status_code == HTTPStatus.BAD_REQUEST:
23
+ if regex.search(r"Ontology .+ modified", response.text):
24
+ return True
25
+ elif HTTPStatus.INTERNAL_SERVER_ERROR <= response.status_code <= HTTPStatus.NETWORK_AUTHENTICATION_REQUIRED:
26
+ time.sleep(5)
27
+ return True
28
+ return False
29
+
30
+
31
+ def sort_for_upload(graph: rx.PyDiGraph, node_to_iri: dict[int, str]) -> list[str]:
32
+ try:
33
+ node_sorting_order = rx.topological_sort(graph)
34
+ return [node_to_iri[x] for x in reversed(node_sorting_order)]
35
+ except rx.DAGHasCycle as e:
36
+ logger.error(e)
37
+ raise CircularOntologyDependency("super-properties") from None
38
+
39
+
40
+ def get_modification_date_onto_lookup(
41
+ project_iri_lookup: ProjectIriLookup,
42
+ onto_client: OntologyCreateClient,
43
+ ) -> OntoLastModDateLookup:
44
+ lookup = OntoLastModDateLookup(
45
+ project_iri=project_iri_lookup.project_iri,
46
+ onto_iris={name: URIRef(iri) for name, iri in project_iri_lookup.onto_iris.items()},
47
+ )
48
+ for onto_iri in project_iri_lookup.onto_iris.values():
49
+ last_mod = onto_client.get_last_modification_date(project_iri_lookup.project_iri, onto_iri)
50
+ lookup.update_last_mod_date(onto_iri, last_mod)
51
+ return lookup
52
+
53
+
54
+ def get_project_iri_lookup(server: str, shortcode: str, project_iri: str) -> ProjectIriLookup:
55
+ client = OntologyGetClientLive(server, shortcode)
56
+ try:
57
+ ontologies, onto_iris = client.get_ontologies()
58
+ logger.debug(f"Project ontologies found: {', '.join(onto_iris)}")
59
+ lookup = _get_name_to_iri_lookup(ontologies)
60
+ return ProjectIriLookup(project_iri, lookup)
61
+ except ProjectOntologyNotFound:
62
+ logger.debug("No project ontologies on server.")
63
+ return ProjectIriLookup(project_iri)
64
+
65
+
66
+ def _get_name_to_iri_lookup(ontologies: list[str]) -> dict[str, str]:
67
+ lookup = {}
68
+ for onto in ontologies:
69
+ g = Graph()
70
+ g.parse(data=onto, format="ttl")
71
+ iri = str(next(g.subjects(RDF.type, OWL.Ontology)))
72
+ name = iri.split("/")[-2]
73
+ lookup[str(name)] = str(iri)
74
+ return lookup
@@ -0,0 +1,52 @@
1
+ import time
2
+
3
+ from loguru import logger
4
+ from tqdm import tqdm
5
+
6
+ from dsp_tools.clients.ontology_clients import OntologyCreateClient
7
+ from dsp_tools.commands.create.models.create_problems import CollectedProblems
8
+ from dsp_tools.commands.create.models.create_problems import CreateProblem
9
+ from dsp_tools.commands.create.models.create_problems import UploadProblem
10
+ from dsp_tools.commands.create.models.create_problems import UploadProblemType
11
+ from dsp_tools.commands.create.models.parsed_ontology import ParsedOntology
12
+ from dsp_tools.commands.create.models.server_project_info import ProjectIriLookup
13
+ from dsp_tools.commands.create.serialisation.ontology import serialise_ontology_graph_for_request
14
+ from dsp_tools.utils.request_utils import ResponseCodeAndText
15
+ from dsp_tools.utils.request_utils import is_server_error
16
+
17
+
18
+ def create_all_ontologies(
19
+ ontologies: list[ParsedOntology],
20
+ project_iri_lookup: ProjectIriLookup,
21
+ client: OntologyCreateClient,
22
+ ) -> tuple[ProjectIriLookup, CollectedProblems | None]:
23
+ logger.debug("Creating ontologies")
24
+ progress_bar = tqdm(ontologies, " Creating ontologies", dynamic_ncols=True)
25
+ problems: list[CreateProblem] = []
26
+ for o in progress_bar:
27
+ result = _create_one_ontology(o, project_iri_lookup.project_iri, client)
28
+ if isinstance(result, UploadProblem):
29
+ problems.append(result)
30
+ else:
31
+ project_iri_lookup.add_onto(o.name, result)
32
+ if problems:
33
+ return project_iri_lookup, CollectedProblems(
34
+ " While creating ontologies the following problems occurred:", problems
35
+ )
36
+ return project_iri_lookup, None
37
+
38
+
39
+ def _create_one_ontology(
40
+ onto: ParsedOntology,
41
+ project_iri: str,
42
+ client: OntologyCreateClient,
43
+ ) -> UploadProblem | str:
44
+ serialised = serialise_ontology_graph_for_request(onto, project_iri)
45
+ result = client.post_new_ontology(serialised)
46
+ if isinstance(result, ResponseCodeAndText):
47
+ if is_server_error(result):
48
+ time.sleep(5)
49
+ result = client.post_new_ontology(serialised)
50
+ if isinstance(result, ResponseCodeAndText):
51
+ return UploadProblem(result.text, UploadProblemType.ONTOLOGY_COULD_NOT_BE_CREATED)
52
+ return result
@@ -0,0 +1,68 @@
1
+ import sys
2
+
3
+ from loguru import logger
4
+
5
+ from dsp_tools.clients.authentication_client import AuthenticationClient
6
+ from dsp_tools.clients.project_client import ProjectClient
7
+ from dsp_tools.clients.project_client_live import ProjectClientLive
8
+ from dsp_tools.commands.create.exceptions import UnableToCreateProjectError
9
+ from dsp_tools.commands.create.models.parsed_project import ParsedProjectMetadata
10
+ from dsp_tools.commands.create.serialisation.project import serialise_project
11
+ from dsp_tools.error.exceptions import ProjectNotFoundError
12
+ from dsp_tools.utils.ansi_colors import BACKGROUND_BOLD_YELLOW
13
+ from dsp_tools.utils.ansi_colors import BOLD
14
+ from dsp_tools.utils.ansi_colors import BOLD_RED
15
+ from dsp_tools.utils.ansi_colors import RESET_TO_DEFAULT
16
+ from dsp_tools.utils.request_utils import is_server_error
17
+
18
+
19
+ def create_project(project: ParsedProjectMetadata, auth: AuthenticationClient, exit_if_exists: bool) -> str:
20
+ client = ProjectClientLive(auth.server, auth)
21
+ try:
22
+ project_iri = client.get_project_iri(project.shortcode)
23
+ _exit_if_create_should_not_continue(project.shortcode, exit_if_exists)
24
+ return project_iri
25
+
26
+ except ProjectNotFoundError:
27
+ logger.debug("No project with the shortcode exists. Continuing creating the project.")
28
+ return _create_project_on_server(project, client)
29
+
30
+
31
+ def _exit_if_create_should_not_continue(shortcode: str, exit_if_exists: bool) -> None:
32
+ if exit_if_exists:
33
+ msg = (
34
+ "The project already exists on the server and the flag '--exit-if-exists' was set. "
35
+ "The process is aborted without further uploads."
36
+ )
37
+ logger.info(msg)
38
+ print(BACKGROUND_BOLD_YELLOW + msg + RESET_TO_DEFAULT)
39
+ sys.exit(0)
40
+
41
+ msg = (
42
+ f"A project with the shortcode '{shortcode}' already exists on the server. "
43
+ f"Do you wish to continue uploading additional information? [y/n] "
44
+ )
45
+ logger.debug(msg)
46
+ resp = None
47
+ while resp not in ["y", "n"]:
48
+ resp = input(BOLD_RED + msg + RESET_TO_DEFAULT)
49
+ if resp == "n":
50
+ logger.debug("Response 'n' abort command.")
51
+ sys.exit(1)
52
+
53
+
54
+ def _create_project_on_server(project: ParsedProjectMetadata, client: ProjectClient) -> str:
55
+ info_str = f"Creating project '{project.shortname}' ({project.shortcode})"
56
+ print(BOLD + info_str + RESET_TO_DEFAULT)
57
+ logger.debug(info_str)
58
+ serialised = serialise_project(project)
59
+ result = client.post_new_project(serialised)
60
+ if isinstance(result, str):
61
+ return result
62
+ if is_server_error(result):
63
+ msg = "Due to a server error it was not possible to create the project. "
64
+ else:
65
+ msg = "Unable to create project."
66
+ raise UnableToCreateProjectError(
67
+ msg + f"\nOriginal response code: {result.status_code}\nOriginal response message: {result.text}"
68
+ )