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
@@ -2,21 +2,25 @@ import pickle
2
2
  import sys
3
3
 
4
4
  from loguru import logger
5
- from termcolor import colored
6
5
 
7
6
  from dsp_tools.cli.args import ServerCredentials
8
- from dsp_tools.commands.xmlupload.list_client import ListClient
9
- from dsp_tools.commands.xmlupload.list_client import ListClientLive
7
+ from dsp_tools.clients.authentication_client_live import AuthenticationClientLive
8
+ from dsp_tools.clients.connection_live import ConnectionLive
9
+ from dsp_tools.clients.legal_info_client import LegalInfoClient
10
+ from dsp_tools.clients.legal_info_client_live import LegalInfoClientLive
11
+ from dsp_tools.clients.project_client import ProjectClient
12
+ from dsp_tools.clients.project_client_live import ProjectClientLive
10
13
  from dsp_tools.commands.xmlupload.models.ingest import AssetClient
11
14
  from dsp_tools.commands.xmlupload.models.ingest import BulkIngestedAssetClient
12
15
  from dsp_tools.commands.xmlupload.models.ingest import DspIngestClientLive
13
16
  from dsp_tools.commands.xmlupload.models.upload_clients import UploadClients
14
17
  from dsp_tools.commands.xmlupload.models.upload_state import UploadState
15
- from dsp_tools.commands.xmlupload.project_client import ProjectClient
16
- from dsp_tools.commands.xmlupload.project_client import ProjectClientLive
18
+ from dsp_tools.commands.xmlupload.prepare_xml_input.list_client import ListClient
19
+ from dsp_tools.commands.xmlupload.prepare_xml_input.list_client import ListClientLive
17
20
  from dsp_tools.commands.xmlupload.upload_config import UploadConfig
18
21
  from dsp_tools.commands.xmlupload.xmlupload import execute_upload
19
- from dsp_tools.utils.connection_live import ConnectionLive
22
+ from dsp_tools.utils.ansi_colors import RED
23
+ from dsp_tools.utils.ansi_colors import RESET_TO_DEFAULT
20
24
 
21
25
 
22
26
  def resume_xmlupload(creds: ServerCredentials, skip_first_resource: bool = False) -> bool:
@@ -38,23 +42,19 @@ def resume_xmlupload(creds: ServerCredentials, skip_first_resource: bool = False
38
42
 
39
43
  _print_and_log(upload_state, server)
40
44
 
41
- con = ConnectionLive(server)
42
- con.login(creds.user, creds.password)
45
+ auth = AuthenticationClientLive(server, creds.user, creds.password)
46
+ con = ConnectionLive(server, auth)
43
47
 
44
48
  ingest_client: AssetClient
45
49
  if upload_state.config.media_previously_uploaded:
46
50
  ingest_client = BulkIngestedAssetClient()
47
51
  else:
48
- ingest_client = DspIngestClientLive(
49
- dsp_ingest_url=creds.dsp_ingest_url,
50
- token=con.get_token(),
51
- shortcode=upload_state.config.shortcode,
52
- imgdir=".",
53
- )
52
+ ingest_client = DspIngestClientLive(creds.dsp_ingest_url, auth, upload_state.config.shortcode, ".")
54
53
 
55
- project_client: ProjectClient = ProjectClientLive(con, upload_state.config.shortcode)
56
- list_client: ListClient = ListClientLive(con, project_client.get_project_iri())
57
- clients = UploadClients(ingest_client, project_client, list_client)
54
+ project_client: ProjectClient = ProjectClientLive(auth.server, auth)
55
+ list_client: ListClient = ListClientLive(con, project_client.get_project_iri(upload_state.config.shortcode))
56
+ legal_info_client: LegalInfoClient = LegalInfoClientLive(server, upload_state.config.shortcode, auth)
57
+ clients = UploadClients(ingest_client, list_client, legal_info_client)
58
58
 
59
59
  return execute_upload(clients, upload_state)
60
60
 
@@ -77,7 +77,7 @@ def _skip_first_resource(upload_state: UploadState) -> None:
77
77
  )
78
78
  resp = None
79
79
  while resp not in ["y", "n"]:
80
- resp = input(colored(msg, color="red"))
80
+ resp = input(RED + msg + RESET_TO_DEFAULT)
81
81
  if resp == "n":
82
82
  sys.exit(1)
83
83
 
@@ -1,3 +1,4 @@
1
+ import contextlib
1
2
  import importlib.resources
2
3
  import shutil
3
4
  import subprocess
@@ -9,11 +10,17 @@ from typing import Optional
9
10
  import regex
10
11
  import requests
11
12
  import yaml
13
+ from jinja2 import Template
12
14
  from loguru import logger
13
15
 
14
- from dsp_tools.models.exceptions import UserError
16
+ from dsp_tools.error.exceptions import InputError
17
+ from dsp_tools.error.exceptions import PermanentConnectionError
18
+ from dsp_tools.utils.request_utils import RequestParameters
19
+ from dsp_tools.utils.request_utils import log_request
20
+ from dsp_tools.utils.request_utils import log_response
15
21
 
16
22
  MAX_FILE_SIZE = 100_000
23
+ MINUTE = 60
17
24
 
18
25
 
19
26
  @dataclass(frozen=True)
@@ -26,7 +33,6 @@ class StackConfiguration:
26
33
  enforce_docker_system_prune: if True, prune Docker without asking the user
27
34
  suppress_docker_system_prune: if True, don't prune Docker (and don't ask)
28
35
  latest_dev_version: if True, start DSP-API from repo's main branch, instead of the latest deployed version
29
- api_version_for_validate: if True a fixed API version is taken that has the features necessary for xml-validate
30
36
  """
31
37
 
32
38
  max_file_size: Optional[int] = None
@@ -34,19 +40,23 @@ class StackConfiguration:
34
40
  suppress_docker_system_prune: bool = False
35
41
  latest_dev_version: bool = False
36
42
  upload_test_data: bool = False
37
- api_version_for_validate: bool = False
43
+ custom_host: Optional[str] = None
38
44
 
39
45
  def __post_init__(self) -> None:
40
46
  """
41
47
  Validate the input parameters passed by the user.
42
48
 
43
49
  Raises:
44
- UserError: if one of the parameters is invalid
50
+ InputError: if one of the parameters is invalid
45
51
  """
46
52
  if self.max_file_size is not None and not 1 <= self.max_file_size <= MAX_FILE_SIZE:
47
- raise UserError(f"max_file_size must be between 1 and {MAX_FILE_SIZE}")
53
+ raise InputError(f"max_file_size must be between 1 and {MAX_FILE_SIZE}")
48
54
  if self.enforce_docker_system_prune and self.suppress_docker_system_prune:
49
- raise UserError('The arguments "--prune" and "--no-prune" are mutually exclusive')
55
+ raise InputError('The arguments "--prune" and "--no-prune" are mutually exclusive')
56
+ if self.custom_host is not None and not regex.match(
57
+ r"^(((\d{1,3}\.){3}\d{1,3})|((([-\w_~]+\.)*([a-z]){2,})))$", self.custom_host
58
+ ):
59
+ raise InputError("Invalid format for custom host. Please, enter an IP or a domain name.")
50
60
 
51
61
 
52
62
  class StackHandler:
@@ -77,28 +87,25 @@ class StackHandler:
77
87
  For this reason, we need to know the commit hash of the DSP-API version that is currently deployed,
78
88
  so that the files can be retrieved from the correct commit.
79
89
 
80
- This function reads the commit hash of DSP-API
81
- that is configured in start-stack-config.yml,
90
+ This function reads the version tag in the docker-compose.yml file,
82
91
  and constructs the URL prefix necessary to retrieve the files from the DSP-API repository.
83
92
 
84
- If something goes wrong,
85
- the URL prefix falls back to pointing to the main branch of the DSP-API repository.
86
-
87
93
  If the latest development version of DSP-API is started,
88
94
  the URL prefix points to the main branch of the DSP-API repository.
89
95
 
90
96
  Returns:
91
97
  URL prefix used to retrieve files from the DSP-API repository
92
98
  """
93
- url_prefix_base = "https://github.com/dasch-swiss/dsp-api/raw"
99
+ url_prefix_base = "https://raw.githubusercontent.com/dasch-swiss/dsp-api"
94
100
 
95
101
  if self.__stack_configuration.latest_dev_version:
96
102
  return f"{url_prefix_base}/main/"
97
103
 
98
- config_file = importlib.resources.files("dsp_tools").joinpath("resources/start-stack/start-stack-config.yml")
99
- start_stack_config = yaml.safe_load(config_file.read_bytes())
100
- commit_of_used_api_version = start_stack_config["DSP-API commit"]
101
- return f"{url_prefix_base}/{commit_of_used_api_version}/"
104
+ docker_compose_pth = importlib.resources.files("dsp_tools").joinpath("resources/start-stack/docker-compose.yml")
105
+ docker_compose = yaml.safe_load(docker_compose_pth.read_bytes())
106
+ tag = docker_compose["services"]["api"]["image"].split(":")[-1]
107
+
108
+ return f"{url_prefix_base}/{tag}/"
102
109
 
103
110
  def _copy_resources_to_home_dir(self) -> None:
104
111
  """
@@ -110,6 +117,7 @@ class StackHandler:
110
117
  by an earlier run of this method.
111
118
  So, this method must always be called, at every run of start-stack.
112
119
  """
120
+ logger.debug("Copying resources to home directory ...")
113
121
  docker_path_of_distribution = importlib.resources.files("dsp_tools").joinpath("resources/start-stack")
114
122
  for file in docker_path_of_distribution.iterdir():
115
123
  with importlib.resources.as_file(file) as f:
@@ -117,8 +125,40 @@ class StackHandler:
117
125
  shutil.copy(file_path, self.__docker_path_of_user / file.name)
118
126
  if not self.__stack_configuration.latest_dev_version:
119
127
  Path(self.__docker_path_of_user / "docker-compose.override.yml").unlink()
120
- if not self.__stack_configuration.api_version_for_validate:
121
- Path(self.__docker_path_of_user / "docker-compose-validation.yml").unlink()
128
+
129
+ def _set_custom_host(self) -> None:
130
+ """
131
+ To ensure the frontend can communicate with a backend on a different server, the host in the environments
132
+ needs to be changed.
133
+ By design the IRIs match the host of the database.
134
+
135
+ This is done by overriding the environment variables in the docker-compose.yml and by replacing the
136
+ configuration for the frontend.
137
+ """
138
+ if self.__stack_configuration.custom_host is not None:
139
+ logger.debug("Setting custom host...")
140
+ self.__localhost_url = f"http://{self.__stack_configuration.custom_host}"
141
+
142
+ docker_template_path = importlib.resources.files("dsp_tools").joinpath(
143
+ "resources/start-stack/docker-compose.override-host.j2"
144
+ )
145
+ docker_template = Template(docker_template_path.read_text(encoding="utf-8"))
146
+ docker_template_rendered = docker_template.render(CUSTOM_HOST=self.__stack_configuration.custom_host)
147
+ Path(self.__docker_path_of_user / "docker-compose.override-host.yml").write_text(
148
+ docker_template_rendered, encoding="utf-8"
149
+ )
150
+
151
+ dsp_app_config_template_path = importlib.resources.files("dsp_tools").joinpath(
152
+ "resources/start-stack/dsp-app-config.override-host.j2"
153
+ )
154
+ dsp_app_config_template = Template(dsp_app_config_template_path.read_text(encoding="utf-8"))
155
+ dsp_app_config_rendered = dsp_app_config_template.render(CUSTOM_HOST=self.__stack_configuration.custom_host)
156
+ Path(self.__docker_path_of_user / "dsp-app-config.json").unlink()
157
+ Path(self.__docker_path_of_user / "dsp-app-config.json").write_text(
158
+ dsp_app_config_rendered, encoding="utf-8"
159
+ )
160
+ Path(self.__docker_path_of_user / "docker-compose.override-host.j2").unlink()
161
+ Path(self.__docker_path_of_user / "dsp-app-config.override-host.j2").unlink()
122
162
 
123
163
  def _get_sipi_docker_config_lua(self) -> None:
124
164
  """
@@ -126,14 +166,15 @@ class StackHandler:
126
166
  and set the max_file_size parameter if necessary.
127
167
 
128
168
  Raises:
129
- UserError: if max_file_size is set but cannot be injected into sipi.docker-config.lua
169
+ InputError: if max_file_size is set but cannot be injected into sipi.docker-config.lua
130
170
  """
171
+ logger.debug("Retrieving sipi.docker-config.lua...")
131
172
  docker_config_lua_response = requests.get(f"{self.__url_prefix}sipi/config/sipi.docker-config.lua", timeout=30)
132
173
  docker_config_lua_text = docker_config_lua_response.text
133
174
  if self.__stack_configuration.max_file_size:
134
175
  max_post_size_regex = r"max_post_size ?= ?[\'\"]?\d+[MG][\'\"]?"
135
176
  if not regex.search(max_post_size_regex, docker_config_lua_text):
136
- raise UserError("Unable to set max_file_size. Please try again without this flag.")
177
+ raise InputError("Unable to set max_file_size. Please try again without this flag.")
137
178
  docker_config_lua_text = regex.sub(
138
179
  max_post_size_regex,
139
180
  f"max_post_size = '{self.__stack_configuration.max_file_size}M'",
@@ -147,67 +188,43 @@ class StackHandler:
147
188
  Start up the Docker container of the fuseki database.
148
189
 
149
190
  Raises:
150
- UserError: if the database cannot be started
191
+ InputError: if the database cannot be started
151
192
  """
152
- cmd = "docker compose up db -d".split()
193
+ logger.debug("Starting up the fuseki container...")
194
+ cmd = "docker compose up -d db".split()
153
195
  completed_process = subprocess.run(cmd, cwd=self.__docker_path_of_user, check=False)
154
196
  if not completed_process or completed_process.returncode != 0:
155
- msg = "Cannot start the API: Error while executing 'docker compose up db -d'"
197
+ msg = "Cannot start the API: Error while executing 'docker compose up -d db'"
156
198
  logger.error(f"{msg}. completed_process = '{vars(completed_process)}'")
157
- raise UserError(msg)
199
+ raise InputError(msg)
158
200
 
159
201
  def _wait_for_fuseki(self) -> None:
160
202
  """
161
203
  Wait up to 6 minutes, until the fuseki database is up and running.
162
204
  This function imitates the behaviour of the script dsp-api/webapi/scripts/wait-for-db.sh.
163
205
  """
206
+ logger.debug("Waiting for the fuseki container to be up and running...")
164
207
  for _ in range(6 * 60):
165
208
  try:
166
209
  response = requests.get(f"{self.__localhost_url}:3030/$/server", auth=("admin", "test"), timeout=10)
167
210
  if response.ok:
211
+ logger.debug("Fuseki is now up and running.")
168
212
  break
169
213
  except Exception: # noqa: BLE001 (blind-except)
170
214
  time.sleep(1)
171
215
  time.sleep(1)
172
216
 
173
- def _create_knora_test_repo(self) -> None:
174
- """
175
- Inside fuseki, create the "knora-test" repository.
176
- This function imitates the behaviour of the script dsp-api/webapi/scripts/fuseki-init-knora-test.sh.
177
-
178
- Raises:
179
- UserError: in case of failure
180
- """
181
- repo_template_response = requests.get(
182
- f"{self.__url_prefix}webapi/scripts/fuseki-repository-config.ttl.template",
183
- timeout=30,
184
- )
185
- repo_template = repo_template_response.text
186
- repo_template = repo_template.replace("@REPOSITORY@", "knora-test")
187
- response = requests.post(
188
- f"{self.__localhost_url}:3030/$/datasets",
189
- files={"file": ("file.ttl", repo_template, "text/turtle; charset=utf8")},
190
- auth=("admin", "test"),
191
- timeout=30,
192
- )
193
- if not response.ok:
194
- msg = (
195
- "Cannot start DSP-API: Error when creating the 'knora-test' repository. "
196
- "Is DSP-API perhaps running already?"
197
- )
198
- logger.error(f"{msg}. response = {vars(response)}")
199
- raise UserError(msg)
200
-
201
217
  def _load_data_into_repo(self) -> None:
202
218
  """
203
219
  Load some basic ontologies and data into the repository.
204
220
  This function imitates the behaviour of the script
205
- dsp-api/webapi/target/docker/stage/opt/docker/scripts/fuseki-init-knora-test.sh.
221
+ dsp-api/webapi/scripts/fuseki-init-knora-test.sh.
206
222
 
207
223
  Raises:
208
- UserError: if one of the graphs cannot be created
224
+ InputError: if one of the graphs cannot be created
209
225
  """
210
- graph_prefix = f"{self.__localhost_url}:3030/knora-test/data?graph="
226
+ logger.debug("Loading data into the 'dsp-repo' repository...")
227
+ graph_prefix = f"{self.__localhost_url}:3030/dsp-repo/data?graph="
211
228
  ttl_files = [
212
229
  ("webapi/src/main/resources/knora-ontologies/knora-admin.ttl", "http://www.knora.org/ontology/knora-admin"),
213
230
  ("webapi/src/main/resources/knora-ontologies/knora-base.ttl", "http://www.knora.org/ontology/knora-base"),
@@ -224,7 +241,7 @@ class StackHandler:
224
241
  if not ttl_response.ok:
225
242
  msg = f"Cannot start DSP-API: Error when retrieving '{self.__url_prefix + ttl_file}'"
226
243
  logger.error(f"{msg}'. response = {vars(ttl_response)}")
227
- raise UserError(msg)
244
+ raise InputError(msg)
228
245
  ttl_text = ttl_response.text
229
246
  response = requests.post(
230
247
  graph_prefix + graph,
@@ -234,7 +251,7 @@ class StackHandler:
234
251
  )
235
252
  if not response.ok:
236
253
  logger.error(f"Cannot start DSP-API: Error when creating graph '{graph}'. response = {vars(response)}")
237
- raise UserError(f"Cannot start DSP-API: Error when creating graph '{graph}'")
254
+ raise InputError(f"Cannot start DSP-API: Error when creating graph '{graph}'")
238
255
 
239
256
  def _create_admin_user(self) -> None:
240
257
  """
@@ -242,9 +259,10 @@ class StackHandler:
242
259
  The password is the hash for "test".
243
260
 
244
261
  Raises:
245
- UserError: If the user cannot be created.
262
+ InputError: If the user cannot be created.
246
263
  """
247
- graph_prefix = f"{self.__localhost_url}:3030/knora-test/data?graph="
264
+ logger.debug("Creating the default admin user...")
265
+ graph_prefix = f"{self.__localhost_url}:3030/dsp-repo/data?graph="
248
266
  admin_graph = "http://www.knora.org/data/admin"
249
267
  admin_user = """
250
268
  @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@@ -271,13 +289,12 @@ class StackHandler:
271
289
  )
272
290
  if not response.ok:
273
291
  logger.error(f"Cannot start DSP-API: Error when creating the admin user. response = {vars(response)}")
274
- raise UserError("Cannot start DSP-API: Error when creating the admin user.")
292
+ raise InputError("Cannot start DSP-API: Error when creating the admin user.")
275
293
 
276
294
  def _initialize_fuseki(self) -> None:
277
295
  """
278
- Create the "knora-test" repository and load some basic ontologies and data into it.
296
+ Load some basic ontologies and data into the 'dsp-repo' repository.
279
297
  """
280
- self._create_knora_test_repo()
281
298
  if self.__stack_configuration.upload_test_data:
282
299
  self._load_data_into_repo()
283
300
  else:
@@ -290,11 +307,13 @@ class StackHandler:
290
307
  """
291
308
  compose_str = "docker compose -f docker-compose.yml"
292
309
  if self.__stack_configuration.latest_dev_version:
310
+ logger.debug("In order to get the latest dev version, run 'docker compose pull' ...")
293
311
  subprocess.run("docker compose pull".split(), cwd=self.__docker_path_of_user, check=True)
294
312
  compose_str += " -f docker-compose.override.yml"
295
- elif self.__stack_configuration.api_version_for_validate:
296
- compose_str += " -f docker-compose-validation.yml"
313
+ if self.__stack_configuration.custom_host is not None:
314
+ compose_str += " -f docker-compose.override-host.yml"
297
315
  compose_str += " up -d"
316
+ logger.debug(f"Running '{compose_str}' ...")
298
317
  subprocess.run(compose_str.split(), cwd=self.__docker_path_of_user, check=True)
299
318
 
300
319
  def _wait_for_api(self) -> None:
@@ -302,16 +321,37 @@ class StackHandler:
302
321
  Wait until the API is up and running.
303
322
  This mimicks the behaviour of the script webapi/scripts/wait-for-api.sh in the DSP-API repository.
304
323
  """
305
- for _ in range(6 * 60):
324
+ logger.debug("Waiting for the API to start...")
325
+ for num_secs in range(6 * 60):
306
326
  try:
307
- response = requests.get(f"{self.__localhost_url}:3333/health", timeout=1)
308
- if not response.ok:
309
- time.sleep(1)
310
- continue
311
- except requests.exceptions.RequestException:
312
- time.sleep(1)
313
- continue
314
- print(f"DSP-API is now running on {self.__localhost_url}:3333/ and DSP-APP on {self.__localhost_url}:4200/")
327
+ params = RequestParameters("GET", f"{self.__localhost_url}:3333/health", timeout=1)
328
+ log_request(params)
329
+ response = requests.get(params.url, timeout=params.timeout)
330
+ log_response(response)
331
+ if response.ok:
332
+ break
333
+ except requests.exceptions.RequestException as e:
334
+ logger.debug(f"RequestException while checking API status: {e}")
335
+ if num_secs > MINUTE / 2 and num_secs % 10 == 0:
336
+ # There is probably an issue, so we need more logs
337
+ with contextlib.suppress():
338
+ docker_ps_output = subprocess.run(
339
+ "docker ps -a".split(), cwd=self.__docker_path_of_user, check=True, capture_output=True
340
+ ).stdout.decode("utf-8")
341
+ docker_ps_output = "\n\t".join(docker_ps_output.split("\n"))
342
+ logger.debug(f"docker ps -a output:\n\t{docker_ps_output}")
343
+ docker_logs_output = subprocess.run(
344
+ "docker logs start-stack-api-1".split(),
345
+ cwd=self.__docker_path_of_user,
346
+ check=True,
347
+ capture_output=True,
348
+ ).stdout.decode("utf-8")
349
+ docker_logs_output = "\n\t".join(docker_logs_output.split("\n"))
350
+ logger.debug(f"Logs of DSP-API container:\n\t{docker_logs_output}")
351
+ time.sleep(1)
352
+ msg = f"DSP-API is now running on {self.__localhost_url}:3333/ and DSP-APP on {self.__localhost_url}:4200/"
353
+ logger.debug(msg)
354
+ print(msg)
315
355
 
316
356
  def _execute_docker_system_prune(self) -> None:
317
357
  """
@@ -333,6 +373,7 @@ class StackHandler:
333
373
  "to keep your docker clean and running smoothly. [y/n]"
334
374
  )
335
375
  if prune_docker == "y":
376
+ logger.debug("Running 'docker system prune --volumes -f' ...")
336
377
  subprocess.run("docker system prune --volumes -f".split(), cwd=self.__docker_path_of_user, check=False)
337
378
 
338
379
  def _start_docker_containers(self) -> None:
@@ -356,15 +397,20 @@ class StackHandler:
356
397
  After startup, ask user if Docker should be pruned or not.
357
398
 
358
399
  Raises:
359
- UserError: if the stack cannot be started with the parameters passed by the user
400
+ InputError: if the stack cannot be started with the parameters passed by the user
360
401
 
361
402
  Returns:
362
403
  True if everything went well, False otherwise
363
404
  """
364
- if subprocess.run("docker stats --no-stream".split(), check=False, capture_output=True).returncode != 0:
365
- raise UserError("Docker is not running properly. Please start Docker and try again.")
366
405
  self._copy_resources_to_home_dir()
367
- self._get_sipi_docker_config_lua()
406
+ self._set_custom_host()
407
+ try:
408
+ self._get_sipi_docker_config_lua()
409
+ except (requests.ConnectionError, requests.ReadTimeout):
410
+ raise PermanentConnectionError(
411
+ "This command requires an internet connection. "
412
+ "Please ensure that your computer is connected and try again."
413
+ )
368
414
  self._start_docker_containers()
369
415
  return True
370
416
 
@@ -376,4 +422,7 @@ class StackHandler:
376
422
  True if everything went well, False otherwise
377
423
  """
378
424
  subprocess.run("docker compose down --volumes".split(), cwd=self.__docker_path_of_user, check=True)
425
+ shutil.rmtree(self.__docker_path_of_user / "sipi", ignore_errors=True)
426
+ # ignore all errors, because the dir cannot be found if this function is called multiple times,
427
+ # and because in GitHub CI, python lacks permissions to delete this dir
379
428
  return True