dsp-tools 0.9.13__py3-none-any.whl → 18.3.0.post13__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (348) hide show
  1. dsp_tools/__init__.py +5 -0
  2. dsp_tools/cli/args.py +47 -0
  3. dsp_tools/cli/call_action.py +85 -0
  4. dsp_tools/cli/call_action_files_only.py +101 -0
  5. dsp_tools/cli/call_action_with_network.py +207 -0
  6. dsp_tools/cli/create_parsers.py +479 -0
  7. dsp_tools/cli/entry_point.py +322 -0
  8. dsp_tools/cli/utils.py +87 -0
  9. dsp_tools/clients/CLAUDE.md +420 -0
  10. dsp_tools/clients/authentication_client.py +14 -0
  11. dsp_tools/clients/authentication_client_live.py +66 -0
  12. dsp_tools/clients/connection.py +35 -0
  13. dsp_tools/clients/connection_live.py +233 -0
  14. dsp_tools/clients/fuseki_metrics.py +60 -0
  15. dsp_tools/clients/group_user_clients.py +35 -0
  16. dsp_tools/clients/group_user_clients_live.py +181 -0
  17. dsp_tools/clients/legal_info_client.py +23 -0
  18. dsp_tools/clients/legal_info_client_live.py +132 -0
  19. dsp_tools/clients/list_client.py +49 -0
  20. dsp_tools/clients/list_client_live.py +166 -0
  21. dsp_tools/clients/metadata_client.py +24 -0
  22. dsp_tools/clients/metadata_client_live.py +47 -0
  23. dsp_tools/clients/ontology_clients.py +49 -0
  24. dsp_tools/clients/ontology_create_client_live.py +166 -0
  25. dsp_tools/clients/ontology_get_client_live.py +80 -0
  26. dsp_tools/clients/permissions_client.py +68 -0
  27. dsp_tools/clients/project_client.py +16 -0
  28. dsp_tools/clients/project_client_live.py +66 -0
  29. dsp_tools/commands/create/communicate_problems.py +24 -0
  30. dsp_tools/commands/create/create.py +134 -0
  31. dsp_tools/commands/create/create_on_server/cardinalities.py +111 -0
  32. dsp_tools/commands/create/create_on_server/classes.py +99 -0
  33. dsp_tools/commands/create/create_on_server/complete_ontologies.py +116 -0
  34. dsp_tools/commands/create/create_on_server/default_permissions.py +134 -0
  35. dsp_tools/commands/create/create_on_server/group_users.py +165 -0
  36. dsp_tools/commands/create/create_on_server/lists.py +163 -0
  37. dsp_tools/commands/create/create_on_server/mappers.py +12 -0
  38. dsp_tools/commands/create/create_on_server/onto_utils.py +74 -0
  39. dsp_tools/commands/create/create_on_server/ontology.py +52 -0
  40. dsp_tools/commands/create/create_on_server/project.py +68 -0
  41. dsp_tools/commands/create/create_on_server/properties.py +119 -0
  42. dsp_tools/commands/create/exceptions.py +29 -0
  43. dsp_tools/commands/create/lists_only.py +66 -0
  44. dsp_tools/commands/create/models/create_problems.py +87 -0
  45. dsp_tools/commands/create/models/parsed_ontology.py +88 -0
  46. dsp_tools/commands/create/models/parsed_project.py +81 -0
  47. dsp_tools/commands/create/models/rdf_ontology.py +12 -0
  48. dsp_tools/commands/create/models/server_project_info.py +100 -0
  49. dsp_tools/commands/create/parsing/parse_lists.py +45 -0
  50. dsp_tools/commands/create/parsing/parse_ontology.py +243 -0
  51. dsp_tools/commands/create/parsing/parse_project.py +149 -0
  52. dsp_tools/commands/create/parsing/parsing_utils.py +40 -0
  53. dsp_tools/commands/create/project_validate.py +595 -0
  54. dsp_tools/commands/create/serialisation/ontology.py +119 -0
  55. dsp_tools/commands/create/serialisation/project.py +44 -0
  56. dsp_tools/commands/excel2json/CLAUDE.md +101 -0
  57. dsp_tools/commands/excel2json/json_header.py +321 -0
  58. dsp_tools/commands/excel2json/lists/__init__.py +0 -0
  59. dsp_tools/commands/excel2json/lists/compliance_checks.py +292 -0
  60. dsp_tools/commands/excel2json/lists/make_lists.py +247 -0
  61. dsp_tools/commands/excel2json/lists/models/__init__.py +0 -0
  62. dsp_tools/commands/excel2json/lists/models/deserialise.py +30 -0
  63. dsp_tools/commands/excel2json/lists/models/input_error.py +216 -0
  64. dsp_tools/commands/excel2json/lists/models/serialise.py +57 -0
  65. dsp_tools/commands/excel2json/lists/utils.py +81 -0
  66. dsp_tools/commands/excel2json/models/__init__.py +0 -0
  67. dsp_tools/commands/excel2json/models/input_error.py +416 -0
  68. dsp_tools/commands/excel2json/models/json_header.py +175 -0
  69. dsp_tools/commands/excel2json/models/list_node_name.py +16 -0
  70. dsp_tools/commands/excel2json/models/ontology.py +76 -0
  71. dsp_tools/commands/excel2json/old_lists.py +328 -0
  72. dsp_tools/commands/excel2json/project.py +280 -0
  73. dsp_tools/commands/excel2json/properties.py +370 -0
  74. dsp_tools/commands/excel2json/resources.py +336 -0
  75. dsp_tools/commands/excel2json/utils.py +352 -0
  76. dsp_tools/commands/excel2xml/__init__.py +7 -0
  77. dsp_tools/commands/excel2xml/excel2xml_cli.py +523 -0
  78. dsp_tools/commands/excel2xml/excel2xml_lib.py +1953 -0
  79. dsp_tools/commands/excel2xml/propertyelement.py +47 -0
  80. dsp_tools/commands/get/__init__.py +0 -0
  81. dsp_tools/commands/get/get.py +166 -0
  82. dsp_tools/commands/get/get_permissions.py +257 -0
  83. dsp_tools/commands/get/get_permissions_legacy.py +89 -0
  84. dsp_tools/commands/get/legacy_models/__init__.py +0 -0
  85. dsp_tools/commands/get/legacy_models/context.py +318 -0
  86. dsp_tools/commands/get/legacy_models/group.py +241 -0
  87. dsp_tools/commands/get/legacy_models/helpers.py +47 -0
  88. dsp_tools/commands/get/legacy_models/listnode.py +390 -0
  89. dsp_tools/commands/get/legacy_models/model.py +12 -0
  90. dsp_tools/commands/get/legacy_models/ontology.py +324 -0
  91. dsp_tools/commands/get/legacy_models/project.py +366 -0
  92. dsp_tools/commands/get/legacy_models/propertyclass.py +417 -0
  93. dsp_tools/commands/get/legacy_models/resourceclass.py +676 -0
  94. dsp_tools/commands/get/legacy_models/user.py +438 -0
  95. dsp_tools/commands/get/models/__init__.py +0 -0
  96. dsp_tools/commands/get/models/permissions_models.py +10 -0
  97. dsp_tools/commands/id2iri.py +258 -0
  98. dsp_tools/commands/ingest_xmlupload/__init__.py +0 -0
  99. dsp_tools/commands/ingest_xmlupload/bulk_ingest_client.py +178 -0
  100. dsp_tools/commands/ingest_xmlupload/create_resources/__init__.py +0 -0
  101. dsp_tools/commands/ingest_xmlupload/create_resources/apply_ingest_id.py +69 -0
  102. dsp_tools/commands/ingest_xmlupload/create_resources/upload_xml.py +166 -0
  103. dsp_tools/commands/ingest_xmlupload/create_resources/user_information.py +121 -0
  104. dsp_tools/commands/ingest_xmlupload/ingest_files/__init__.py +0 -0
  105. dsp_tools/commands/ingest_xmlupload/ingest_files/ingest_files.py +64 -0
  106. dsp_tools/commands/ingest_xmlupload/upload_files/__init__.py +0 -0
  107. dsp_tools/commands/ingest_xmlupload/upload_files/filechecker.py +20 -0
  108. dsp_tools/commands/ingest_xmlupload/upload_files/input_error.py +57 -0
  109. dsp_tools/commands/ingest_xmlupload/upload_files/upload_failures.py +66 -0
  110. dsp_tools/commands/ingest_xmlupload/upload_files/upload_files.py +67 -0
  111. dsp_tools/commands/resume_xmlupload/__init__.py +0 -0
  112. dsp_tools/commands/resume_xmlupload/resume_xmlupload.py +96 -0
  113. dsp_tools/commands/start_stack.py +428 -0
  114. dsp_tools/commands/update_legal/CLAUDE.md +344 -0
  115. dsp_tools/commands/update_legal/__init__.py +0 -0
  116. dsp_tools/commands/update_legal/core.py +182 -0
  117. dsp_tools/commands/update_legal/csv_operations.py +135 -0
  118. dsp_tools/commands/update_legal/models.py +87 -0
  119. dsp_tools/commands/update_legal/xml_operations.py +247 -0
  120. dsp_tools/commands/validate_data/CLAUDE.md +159 -0
  121. dsp_tools/commands/validate_data/__init__.py +0 -0
  122. dsp_tools/commands/validate_data/constants.py +59 -0
  123. dsp_tools/commands/validate_data/mappers.py +143 -0
  124. dsp_tools/commands/validate_data/models/__init__.py +0 -0
  125. dsp_tools/commands/validate_data/models/api_responses.py +45 -0
  126. dsp_tools/commands/validate_data/models/input_problems.py +119 -0
  127. dsp_tools/commands/validate_data/models/rdf_like_data.py +117 -0
  128. dsp_tools/commands/validate_data/models/validation.py +106 -0
  129. dsp_tools/commands/validate_data/prepare_data/__init__.py +0 -0
  130. dsp_tools/commands/validate_data/prepare_data/get_rdf_like_data.py +296 -0
  131. dsp_tools/commands/validate_data/prepare_data/make_data_graph.py +91 -0
  132. dsp_tools/commands/validate_data/prepare_data/prepare_data.py +184 -0
  133. dsp_tools/commands/validate_data/process_validation_report/__init__.py +0 -0
  134. dsp_tools/commands/validate_data/process_validation_report/get_user_validation_message.py +358 -0
  135. dsp_tools/commands/validate_data/process_validation_report/query_validation_result.py +507 -0
  136. dsp_tools/commands/validate_data/process_validation_report/reformat_validation_results.py +150 -0
  137. dsp_tools/commands/validate_data/shacl_cli_validator.py +70 -0
  138. dsp_tools/commands/validate_data/sparql/__init__.py +0 -0
  139. dsp_tools/commands/validate_data/sparql/cardinality_shacl.py +209 -0
  140. dsp_tools/commands/validate_data/sparql/construct_shacl.py +92 -0
  141. dsp_tools/commands/validate_data/sparql/legal_info_shacl.py +36 -0
  142. dsp_tools/commands/validate_data/sparql/value_shacl.py +357 -0
  143. dsp_tools/commands/validate_data/utils.py +59 -0
  144. dsp_tools/commands/validate_data/validate_data.py +283 -0
  145. dsp_tools/commands/validate_data/validation/__init__.py +0 -0
  146. dsp_tools/commands/validate_data/validation/check_duplicate_files.py +55 -0
  147. dsp_tools/commands/validate_data/validation/check_for_unknown_classes.py +67 -0
  148. dsp_tools/commands/validate_data/validation/get_validation_report.py +94 -0
  149. dsp_tools/commands/validate_data/validation/validate_ontology.py +107 -0
  150. dsp_tools/commands/xmlupload/CLAUDE.md +292 -0
  151. dsp_tools/commands/xmlupload/__init__.py +0 -0
  152. dsp_tools/commands/xmlupload/iri_resolver.py +21 -0
  153. dsp_tools/commands/xmlupload/make_rdf_graph/__init__.py +0 -0
  154. dsp_tools/commands/xmlupload/make_rdf_graph/constants.py +63 -0
  155. dsp_tools/commands/xmlupload/make_rdf_graph/jsonld_utils.py +44 -0
  156. dsp_tools/commands/xmlupload/make_rdf_graph/make_file_value.py +77 -0
  157. dsp_tools/commands/xmlupload/make_rdf_graph/make_resource_and_values.py +114 -0
  158. dsp_tools/commands/xmlupload/make_rdf_graph/make_values.py +262 -0
  159. dsp_tools/commands/xmlupload/models/__init__.py +0 -0
  160. dsp_tools/commands/xmlupload/models/bitstream_info.py +18 -0
  161. dsp_tools/commands/xmlupload/models/formatted_text_value.py +10 -0
  162. dsp_tools/commands/xmlupload/models/ingest.py +143 -0
  163. dsp_tools/commands/xmlupload/models/input_problems.py +58 -0
  164. dsp_tools/commands/xmlupload/models/lookup_models.py +21 -0
  165. dsp_tools/commands/xmlupload/models/permission.py +45 -0
  166. dsp_tools/commands/xmlupload/models/permissions_parsed.py +93 -0
  167. dsp_tools/commands/xmlupload/models/processed/__init__.py +0 -0
  168. dsp_tools/commands/xmlupload/models/processed/file_values.py +29 -0
  169. dsp_tools/commands/xmlupload/models/processed/res.py +27 -0
  170. dsp_tools/commands/xmlupload/models/processed/values.py +101 -0
  171. dsp_tools/commands/xmlupload/models/rdf_models.py +26 -0
  172. dsp_tools/commands/xmlupload/models/upload_clients.py +14 -0
  173. dsp_tools/commands/xmlupload/models/upload_state.py +20 -0
  174. dsp_tools/commands/xmlupload/prepare_xml_input/__init__.py +0 -0
  175. dsp_tools/commands/xmlupload/prepare_xml_input/ark2iri.py +55 -0
  176. dsp_tools/commands/xmlupload/prepare_xml_input/get_processed_resources.py +252 -0
  177. dsp_tools/commands/xmlupload/prepare_xml_input/iiif_uri_validator.py +50 -0
  178. dsp_tools/commands/xmlupload/prepare_xml_input/list_client.py +120 -0
  179. dsp_tools/commands/xmlupload/prepare_xml_input/prepare_xml_input.py +67 -0
  180. dsp_tools/commands/xmlupload/prepare_xml_input/read_validate_xml_file.py +58 -0
  181. dsp_tools/commands/xmlupload/prepare_xml_input/transform_input_values.py +118 -0
  182. dsp_tools/commands/xmlupload/resource_create_client.py +25 -0
  183. dsp_tools/commands/xmlupload/richtext_id2iri.py +37 -0
  184. dsp_tools/commands/xmlupload/stash/__init__.py +0 -0
  185. dsp_tools/commands/xmlupload/stash/analyse_circular_reference_graph.py +236 -0
  186. dsp_tools/commands/xmlupload/stash/create_info_for_graph.py +53 -0
  187. dsp_tools/commands/xmlupload/stash/graph_models.py +87 -0
  188. dsp_tools/commands/xmlupload/stash/stash_circular_references.py +68 -0
  189. dsp_tools/commands/xmlupload/stash/stash_models.py +109 -0
  190. dsp_tools/commands/xmlupload/stash/upload_stashed_resptr_props.py +106 -0
  191. dsp_tools/commands/xmlupload/stash/upload_stashed_xml_texts.py +196 -0
  192. dsp_tools/commands/xmlupload/upload_config.py +76 -0
  193. dsp_tools/commands/xmlupload/write_diagnostic_info.py +27 -0
  194. dsp_tools/commands/xmlupload/xmlupload.py +516 -0
  195. dsp_tools/config/__init__.py +0 -0
  196. dsp_tools/config/logger_config.py +69 -0
  197. dsp_tools/config/warnings_config.py +32 -0
  198. dsp_tools/error/__init__.py +0 -0
  199. dsp_tools/error/custom_warnings.py +39 -0
  200. dsp_tools/error/exceptions.py +204 -0
  201. dsp_tools/error/problems.py +10 -0
  202. dsp_tools/error/xmllib_errors.py +20 -0
  203. dsp_tools/error/xmllib_warnings.py +54 -0
  204. dsp_tools/error/xmllib_warnings_util.py +159 -0
  205. dsp_tools/error/xsd_validation_error_msg.py +19 -0
  206. dsp_tools/legacy_models/__init__.py +0 -0
  207. dsp_tools/legacy_models/datetimestamp.py +81 -0
  208. dsp_tools/legacy_models/langstring.py +253 -0
  209. dsp_tools/legacy_models/projectContext.py +49 -0
  210. dsp_tools/py.typed +0 -0
  211. dsp_tools/resources/schema/data.xsd +648 -0
  212. dsp_tools/resources/schema/lists-only.json +72 -0
  213. dsp_tools/resources/schema/project.json +1258 -0
  214. dsp_tools/resources/schema/properties-only.json +874 -0
  215. dsp_tools/resources/schema/resources-only.json +140 -0
  216. dsp_tools/resources/start-stack/docker-compose.override-host.j2 +11 -0
  217. dsp_tools/resources/start-stack/docker-compose.override.yml +11 -0
  218. dsp_tools/resources/start-stack/docker-compose.yml +88 -0
  219. dsp_tools/resources/start-stack/dsp-app-config.json +45 -0
  220. dsp_tools/resources/start-stack/dsp-app-config.override-host.j2 +26 -0
  221. dsp_tools/resources/validate_data/api-shapes-resource-cardinalities.ttl +191 -0
  222. dsp_tools/resources/validate_data/api-shapes.ttl +804 -0
  223. dsp_tools/resources/validate_data/shacl-cli-image.yml +4 -0
  224. dsp_tools/resources/validate_data/validate-ontology.ttl +99 -0
  225. dsp_tools/utils/__init__.py +0 -0
  226. dsp_tools/utils/ansi_colors.py +32 -0
  227. dsp_tools/utils/data_formats/__init__.py +0 -0
  228. dsp_tools/utils/data_formats/date_util.py +166 -0
  229. dsp_tools/utils/data_formats/iri_util.py +30 -0
  230. dsp_tools/utils/data_formats/shared.py +81 -0
  231. dsp_tools/utils/data_formats/uri_util.py +76 -0
  232. dsp_tools/utils/fuseki_bloating.py +63 -0
  233. dsp_tools/utils/json_parsing.py +22 -0
  234. dsp_tools/utils/rdf_constants.py +42 -0
  235. dsp_tools/utils/rdflib_utils.py +10 -0
  236. dsp_tools/utils/replace_id_with_iri.py +66 -0
  237. dsp_tools/utils/request_utils.py +238 -0
  238. dsp_tools/utils/xml_parsing/__init__.py +0 -0
  239. dsp_tools/utils/xml_parsing/get_lookups.py +32 -0
  240. dsp_tools/utils/xml_parsing/get_parsed_resources.py +325 -0
  241. dsp_tools/utils/xml_parsing/models/__init__.py +0 -0
  242. dsp_tools/utils/xml_parsing/models/parsed_resource.py +76 -0
  243. dsp_tools/utils/xml_parsing/parse_clean_validate_xml.py +137 -0
  244. dsp_tools/xmllib/CLAUDE.md +302 -0
  245. dsp_tools/xmllib/__init__.py +49 -0
  246. dsp_tools/xmllib/general_functions.py +877 -0
  247. dsp_tools/xmllib/internal/__init__.py +0 -0
  248. dsp_tools/xmllib/internal/checkers.py +162 -0
  249. dsp_tools/xmllib/internal/circumvent_circular_imports.py +36 -0
  250. dsp_tools/xmllib/internal/constants.py +46 -0
  251. dsp_tools/xmllib/internal/input_converters.py +155 -0
  252. dsp_tools/xmllib/internal/serialise_file_value.py +57 -0
  253. dsp_tools/xmllib/internal/serialise_resource.py +177 -0
  254. dsp_tools/xmllib/internal/serialise_values.py +152 -0
  255. dsp_tools/xmllib/internal/type_aliases.py +11 -0
  256. dsp_tools/xmllib/models/__init__.py +0 -0
  257. dsp_tools/xmllib/models/config_options.py +28 -0
  258. dsp_tools/xmllib/models/date_formats.py +48 -0
  259. dsp_tools/xmllib/models/dsp_base_resources.py +1542 -0
  260. dsp_tools/xmllib/models/internal/__init__.py +0 -0
  261. dsp_tools/xmllib/models/internal/file_values.py +172 -0
  262. dsp_tools/xmllib/models/internal/geometry.py +162 -0
  263. dsp_tools/xmllib/models/internal/migration_metadata.py +55 -0
  264. dsp_tools/xmllib/models/internal/serialise_permissions.py +66 -0
  265. dsp_tools/xmllib/models/internal/values.py +342 -0
  266. dsp_tools/xmllib/models/licenses/__init__.py +0 -0
  267. dsp_tools/xmllib/models/licenses/other.py +59 -0
  268. dsp_tools/xmllib/models/licenses/recommended.py +107 -0
  269. dsp_tools/xmllib/models/permissions.py +41 -0
  270. dsp_tools/xmllib/models/res.py +1782 -0
  271. dsp_tools/xmllib/models/root.py +348 -0
  272. dsp_tools/xmllib/value_checkers.py +434 -0
  273. dsp_tools/xmllib/value_converters.py +777 -0
  274. dsp_tools-18.3.0.post13.dist-info/METADATA +90 -0
  275. dsp_tools-18.3.0.post13.dist-info/RECORD +286 -0
  276. dsp_tools-18.3.0.post13.dist-info/WHEEL +4 -0
  277. dsp_tools-18.3.0.post13.dist-info/entry_points.txt +3 -0
  278. dsp_tools-0.9.13.dist-info/LICENSE +0 -674
  279. dsp_tools-0.9.13.dist-info/METADATA +0 -144
  280. dsp_tools-0.9.13.dist-info/RECORD +0 -71
  281. dsp_tools-0.9.13.dist-info/WHEEL +0 -5
  282. dsp_tools-0.9.13.dist-info/entry_points.txt +0 -3
  283. dsp_tools-0.9.13.dist-info/top_level.txt +0 -1
  284. dsplib/models/connection.py +0 -272
  285. dsplib/models/group.py +0 -296
  286. dsplib/models/helpers.py +0 -505
  287. dsplib/models/langstring.py +0 -277
  288. dsplib/models/listnode.py +0 -578
  289. dsplib/models/model.py +0 -20
  290. dsplib/models/ontology.py +0 -448
  291. dsplib/models/permission.py +0 -112
  292. dsplib/models/project.py +0 -547
  293. dsplib/models/propertyclass.py +0 -505
  294. dsplib/models/resource.py +0 -366
  295. dsplib/models/resourceclass.py +0 -810
  296. dsplib/models/sipi.py +0 -30
  297. dsplib/models/user.py +0 -731
  298. dsplib/models/value.py +0 -1000
  299. dsplib/utils/knora-data-schema.xsd +0 -454
  300. dsplib/utils/knora-schema-lists.json +0 -83
  301. dsplib/utils/knora-schema.json +0 -434
  302. dsplib/utils/onto_commons.py +0 -24
  303. dsplib/utils/onto_create_lists.py +0 -73
  304. dsplib/utils/onto_create_ontology.py +0 -442
  305. dsplib/utils/onto_get.py +0 -58
  306. dsplib/utils/onto_validate.py +0 -33
  307. dsplib/utils/xml_upload.py +0 -539
  308. dsplib/widgets/doublepassword.py +0 -80
  309. knora/MLS-import-libraries.py +0 -84
  310. knora/dsp_tools.py +0 -96
  311. knora/dsplib/models/connection.py +0 -272
  312. knora/dsplib/models/group.py +0 -296
  313. knora/dsplib/models/helpers.py +0 -506
  314. knora/dsplib/models/langstring.py +0 -277
  315. knora/dsplib/models/listnode.py +0 -578
  316. knora/dsplib/models/model.py +0 -20
  317. knora/dsplib/models/ontology.py +0 -448
  318. knora/dsplib/models/permission.py +0 -112
  319. knora/dsplib/models/project.py +0 -583
  320. knora/dsplib/models/propertyclass.py +0 -505
  321. knora/dsplib/models/resource.py +0 -416
  322. knora/dsplib/models/resourceclass.py +0 -811
  323. knora/dsplib/models/sipi.py +0 -35
  324. knora/dsplib/models/user.py +0 -731
  325. knora/dsplib/models/value.py +0 -1000
  326. knora/dsplib/utils/knora-data-schema.xsd +0 -464
  327. knora/dsplib/utils/knora-schema-lists.json +0 -83
  328. knora/dsplib/utils/knora-schema.json +0 -444
  329. knora/dsplib/utils/onto_commons.py +0 -24
  330. knora/dsplib/utils/onto_create_lists.py +0 -73
  331. knora/dsplib/utils/onto_create_ontology.py +0 -451
  332. knora/dsplib/utils/onto_get.py +0 -58
  333. knora/dsplib/utils/onto_validate.py +0 -33
  334. knora/dsplib/utils/xml_upload.py +0 -540
  335. knora/dsplib/widgets/doublepassword.py +0 -80
  336. knora/knora.py +0 -2108
  337. knora/test.py +0 -99
  338. knora/testit.py +0 -76
  339. knora/xml2knora.py +0 -633
  340. {dsplib → dsp_tools/cli}/__init__.py +0 -0
  341. {dsplib/models → dsp_tools/clients}/__init__.py +0 -0
  342. {dsplib/utils → dsp_tools/commands}/__init__.py +0 -0
  343. {dsplib/widgets → dsp_tools/commands/create}/__init__.py +0 -0
  344. {knora → dsp_tools/commands/create/create_on_server}/__init__.py +0 -0
  345. {knora/dsplib → dsp_tools/commands/create/models}/__init__.py +0 -0
  346. {knora/dsplib/models → dsp_tools/commands/create/parsing}/__init__.py +0 -0
  347. {knora/dsplib/utils → dsp_tools/commands/create/serialisation}/__init__.py +0 -0
  348. {knora/dsplib/widgets → dsp_tools/commands/excel2json}/__init__.py +0 -0
@@ -0,0 +1,428 @@
1
+ import contextlib
2
+ import importlib.resources
3
+ import shutil
4
+ import subprocess
5
+ import time
6
+ from dataclasses import dataclass
7
+ from pathlib import Path
8
+ from typing import Optional
9
+
10
+ import regex
11
+ import requests
12
+ import yaml
13
+ from jinja2 import Template
14
+ from loguru import logger
15
+
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
21
+
22
+ MAX_FILE_SIZE = 100_000
23
+ MINUTE = 60
24
+
25
+
26
+ @dataclass(frozen=True)
27
+ class StackConfiguration:
28
+ """
29
+ Groups together configuration information for the StackHandler.
30
+
31
+ Args:
32
+ max_file_size: max. multimedia file size allowed for ingest, in MB (max: 100'000)
33
+ enforce_docker_system_prune: if True, prune Docker without asking the user
34
+ suppress_docker_system_prune: if True, don't prune Docker (and don't ask)
35
+ latest_dev_version: if True, start DSP-API from repo's main branch, instead of the latest deployed version
36
+ """
37
+
38
+ max_file_size: Optional[int] = None
39
+ enforce_docker_system_prune: bool = False
40
+ suppress_docker_system_prune: bool = False
41
+ latest_dev_version: bool = False
42
+ upload_test_data: bool = False
43
+ custom_host: Optional[str] = None
44
+
45
+ def __post_init__(self) -> None:
46
+ """
47
+ Validate the input parameters passed by the user.
48
+
49
+ Raises:
50
+ InputError: if one of the parameters is invalid
51
+ """
52
+ if self.max_file_size is not None and not 1 <= self.max_file_size <= MAX_FILE_SIZE:
53
+ raise InputError(f"max_file_size must be between 1 and {MAX_FILE_SIZE}")
54
+ if self.enforce_docker_system_prune and self.suppress_docker_system_prune:
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.")
60
+
61
+
62
+ class StackHandler:
63
+ """This class contains functions to start and stop the Docker containers of DSP-API and DSP-APP."""
64
+
65
+ __stack_configuration: StackConfiguration
66
+ __url_prefix: str
67
+ __docker_path_of_user: Path
68
+ __localhost_url = "http://0.0.0.0"
69
+
70
+ def __init__(self, stack_configuration: StackConfiguration) -> None:
71
+ """
72
+ Initialize a StackHandler with a StackConfiguration
73
+
74
+ Args:
75
+ stack_configuration: configuration information for the StackHandler
76
+ """
77
+ self.__stack_configuration = stack_configuration
78
+ self.__url_prefix = self._get_url_prefix()
79
+ self.__docker_path_of_user = Path.home() / Path(".dsp-tools/start-stack")
80
+ self.__docker_path_of_user.mkdir(parents=True, exist_ok=True)
81
+
82
+ def _get_url_prefix(self) -> str:
83
+ """
84
+ The start-stack command needs some files from the DSP-API repository.
85
+ By default, start-stack starts the latest deployed version of DSP-API.
86
+ Since the last deployment, the DSP-API repository may have been updated.
87
+ For this reason, we need to know the commit hash of the DSP-API version that is currently deployed,
88
+ so that the files can be retrieved from the correct commit.
89
+
90
+ This function reads the version tag in the docker-compose.yml file,
91
+ and constructs the URL prefix necessary to retrieve the files from the DSP-API repository.
92
+
93
+ If the latest development version of DSP-API is started,
94
+ the URL prefix points to the main branch of the DSP-API repository.
95
+
96
+ Returns:
97
+ URL prefix used to retrieve files from the DSP-API repository
98
+ """
99
+ url_prefix_base = "https://raw.githubusercontent.com/dasch-swiss/dsp-api"
100
+
101
+ if self.__stack_configuration.latest_dev_version:
102
+ return f"{url_prefix_base}/main/"
103
+
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}/"
109
+
110
+ def _copy_resources_to_home_dir(self) -> None:
111
+ """
112
+ On most systems, Docker is not allowed to access files outside of the user's home directory.
113
+ For this reason, copy the contents of the distribution (src/dsp_tools/resources/start-stack)
114
+ to the user's home directory (~/.dsp-tools/start-stack).
115
+
116
+ Important: The files of the home directory might have been modified
117
+ by an earlier run of this method.
118
+ So, this method must always be called, at every run of start-stack.
119
+ """
120
+ logger.debug("Copying resources to home directory ...")
121
+ docker_path_of_distribution = importlib.resources.files("dsp_tools").joinpath("resources/start-stack")
122
+ for file in docker_path_of_distribution.iterdir():
123
+ with importlib.resources.as_file(file) as f:
124
+ file_path = Path(f)
125
+ shutil.copy(file_path, self.__docker_path_of_user / file.name)
126
+ if not self.__stack_configuration.latest_dev_version:
127
+ Path(self.__docker_path_of_user / "docker-compose.override.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()
162
+
163
+ def _get_sipi_docker_config_lua(self) -> None:
164
+ """
165
+ Retrieve the config file sipi.docker-config.lua from the DSP-API repository,
166
+ and set the max_file_size parameter if necessary.
167
+
168
+ Raises:
169
+ InputError: if max_file_size is set but cannot be injected into sipi.docker-config.lua
170
+ """
171
+ logger.debug("Retrieving sipi.docker-config.lua...")
172
+ docker_config_lua_response = requests.get(f"{self.__url_prefix}sipi/config/sipi.docker-config.lua", timeout=30)
173
+ docker_config_lua_text = docker_config_lua_response.text
174
+ if self.__stack_configuration.max_file_size:
175
+ max_post_size_regex = r"max_post_size ?= ?[\'\"]?\d+[MG][\'\"]?"
176
+ if not regex.search(max_post_size_regex, docker_config_lua_text):
177
+ raise InputError("Unable to set max_file_size. Please try again without this flag.")
178
+ docker_config_lua_text = regex.sub(
179
+ max_post_size_regex,
180
+ f"max_post_size = '{self.__stack_configuration.max_file_size}M'",
181
+ docker_config_lua_text,
182
+ )
183
+ with open(self.__docker_path_of_user / "sipi.docker-config.lua", "w", encoding="utf-8") as f:
184
+ f.write(docker_config_lua_text)
185
+
186
+ def _start_up_fuseki(self) -> None:
187
+ """
188
+ Start up the Docker container of the fuseki database.
189
+
190
+ Raises:
191
+ InputError: if the database cannot be started
192
+ """
193
+ logger.debug("Starting up the fuseki container...")
194
+ cmd = "docker compose up -d db".split()
195
+ completed_process = subprocess.run(cmd, cwd=self.__docker_path_of_user, check=False)
196
+ if not completed_process or completed_process.returncode != 0:
197
+ msg = "Cannot start the API: Error while executing 'docker compose up -d db'"
198
+ logger.error(f"{msg}. completed_process = '{vars(completed_process)}'")
199
+ raise InputError(msg)
200
+
201
+ def _wait_for_fuseki(self) -> None:
202
+ """
203
+ Wait up to 6 minutes, until the fuseki database is up and running.
204
+ This function imitates the behaviour of the script dsp-api/webapi/scripts/wait-for-db.sh.
205
+ """
206
+ logger.debug("Waiting for the fuseki container to be up and running...")
207
+ for _ in range(6 * 60):
208
+ try:
209
+ response = requests.get(f"{self.__localhost_url}:3030/$/server", auth=("admin", "test"), timeout=10)
210
+ if response.ok:
211
+ logger.debug("Fuseki is now up and running.")
212
+ break
213
+ except Exception: # noqa: BLE001 (blind-except)
214
+ time.sleep(1)
215
+ time.sleep(1)
216
+
217
+ def _load_data_into_repo(self) -> None:
218
+ """
219
+ Load some basic ontologies and data into the repository.
220
+ This function imitates the behaviour of the script
221
+ dsp-api/webapi/scripts/fuseki-init-knora-test.sh.
222
+
223
+ Raises:
224
+ InputError: if one of the graphs cannot be created
225
+ """
226
+ logger.debug("Loading data into the 'dsp-repo' repository...")
227
+ graph_prefix = f"{self.__localhost_url}:3030/dsp-repo/data?graph="
228
+ ttl_files = [
229
+ ("webapi/src/main/resources/knora-ontologies/knora-admin.ttl", "http://www.knora.org/ontology/knora-admin"),
230
+ ("webapi/src/main/resources/knora-ontologies/knora-base.ttl", "http://www.knora.org/ontology/knora-base"),
231
+ ("webapi/src/main/resources/knora-ontologies/standoff-onto.ttl", "http://www.knora.org/ontology/standoff"),
232
+ ("webapi/src/main/resources/knora-ontologies/standoff-data.ttl", "http://www.knora.org/data/standoff"),
233
+ ("webapi/src/main/resources/knora-ontologies/salsah-gui.ttl", "http://www.knora.org/ontology/salsah-gui"),
234
+ ("test_data/project_data/admin-data.ttl", "http://www.knora.org/data/admin"),
235
+ ("test_data/project_data/permissions-data.ttl", "http://www.knora.org/data/permissions"),
236
+ ("test_data/project_ontologies/anything-onto.ttl", "http://www.knora.org/ontology/0001/anything"),
237
+ ("test_data/project_data/anything-data.ttl", "http://www.knora.org/data/0001/anything"),
238
+ ]
239
+ for ttl_file, graph in ttl_files:
240
+ ttl_response = requests.get(self.__url_prefix + ttl_file, timeout=30)
241
+ if not ttl_response.ok:
242
+ msg = f"Cannot start DSP-API: Error when retrieving '{self.__url_prefix + ttl_file}'"
243
+ logger.error(f"{msg}'. response = {vars(ttl_response)}")
244
+ raise InputError(msg)
245
+ ttl_text = ttl_response.text
246
+ response = requests.post(
247
+ graph_prefix + graph,
248
+ files={"file": ("file.ttl", ttl_text, "text/turtle; charset: utf-8")},
249
+ auth=("admin", "test"),
250
+ timeout=30,
251
+ )
252
+ if not response.ok:
253
+ logger.error(f"Cannot start DSP-API: Error when creating graph '{graph}'. response = {vars(response)}")
254
+ raise InputError(f"Cannot start DSP-API: Error when creating graph '{graph}'")
255
+
256
+ def _create_admin_user(self) -> None:
257
+ """
258
+ This function adds the default system admin user to the database.
259
+ The password is the hash for "test".
260
+
261
+ Raises:
262
+ InputError: If the user cannot be created.
263
+ """
264
+ logger.debug("Creating the default admin user...")
265
+ graph_prefix = f"{self.__localhost_url}:3030/dsp-repo/data?graph="
266
+ admin_graph = "http://www.knora.org/data/admin"
267
+ admin_user = """
268
+ @prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
269
+ @prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
270
+ @prefix knora-admin: <http://www.knora.org/ontology/knora-admin#> .
271
+
272
+ <http://rdfh.ch/users/root>
273
+ rdf:type knora-admin:User ;
274
+ knora-admin:username "root"^^xsd:string ;
275
+ knora-admin:email "root@example.com"^^xsd:string ;
276
+ knora-admin:givenName "System"^^xsd:string ;
277
+ knora-admin:familyName "Administrator"^^xsd:string ;
278
+ knora-admin:password "$2a$12$7XEBehimXN1rbhmVgQsyve08.vtDmKK7VMin4AdgCEtE4DWgfQbTK"^^xsd:string ;
279
+ knora-admin:phone "123456"^^xsd:string ;
280
+ knora-admin:preferredLanguage "en"^^xsd:string ;
281
+ knora-admin:status "true"^^xsd:boolean ;
282
+ knora-admin:isInSystemAdminGroup "true"^^xsd:boolean .
283
+ """
284
+ response = requests.post(
285
+ graph_prefix + admin_graph,
286
+ files={"file": ("file.ttl", admin_user, "text/turtle; charset: utf-8")},
287
+ auth=("admin", "test"),
288
+ timeout=30,
289
+ )
290
+ if not response.ok:
291
+ logger.error(f"Cannot start DSP-API: Error when creating the admin user. response = {vars(response)}")
292
+ raise InputError("Cannot start DSP-API: Error when creating the admin user.")
293
+
294
+ def _initialize_fuseki(self) -> None:
295
+ """
296
+ Load some basic ontologies and data into the 'dsp-repo' repository.
297
+ """
298
+ if self.__stack_configuration.upload_test_data:
299
+ self._load_data_into_repo()
300
+ else:
301
+ self._create_admin_user()
302
+
303
+ def _start_remaining_docker_containers(self) -> None:
304
+ """
305
+ Start the other Docker containers that are not running yet.
306
+ (Fuseki is already running at this point.)
307
+ """
308
+ compose_str = "docker compose -f docker-compose.yml"
309
+ if self.__stack_configuration.latest_dev_version:
310
+ logger.debug("In order to get the latest dev version, run 'docker compose pull' ...")
311
+ subprocess.run("docker compose pull".split(), cwd=self.__docker_path_of_user, check=True)
312
+ compose_str += " -f docker-compose.override.yml"
313
+ if self.__stack_configuration.custom_host is not None:
314
+ compose_str += " -f docker-compose.override-host.yml"
315
+ compose_str += " up -d"
316
+ logger.debug(f"Running '{compose_str}' ...")
317
+ subprocess.run(compose_str.split(), cwd=self.__docker_path_of_user, check=True)
318
+
319
+ def _wait_for_api(self) -> None:
320
+ """
321
+ Wait until the API is up and running.
322
+ This mimicks the behaviour of the script webapi/scripts/wait-for-api.sh in the DSP-API repository.
323
+ """
324
+ logger.debug("Waiting for the API to start...")
325
+ for num_secs in range(6 * 60):
326
+ try:
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)
355
+
356
+ def _execute_docker_system_prune(self) -> None:
357
+ """
358
+ Depending on the CLI parameters or the user's input,
359
+ execute "docker system prune" or not.
360
+ """
361
+ if self.__stack_configuration.enforce_docker_system_prune:
362
+ prune_docker = "y"
363
+ elif self.__stack_configuration.suppress_docker_system_prune:
364
+ prune_docker = "n"
365
+ else:
366
+ prune_docker = None
367
+ while prune_docker not in ["y", "n"]:
368
+ prune_docker = input(
369
+ "Allow dsp-tools to execute 'docker system prune'? \n"
370
+ "If you press 'y', all unused containers, networks, and images (both dangling and unused) "
371
+ "in your docker will be deleted.\n"
372
+ "It is recommended that you do this every once in a while "
373
+ "to keep your docker clean and running smoothly. [y/n]"
374
+ )
375
+ if prune_docker == "y":
376
+ logger.debug("Running 'docker system prune --volumes -f' ...")
377
+ subprocess.run("docker system prune --volumes -f".split(), cwd=self.__docker_path_of_user, check=False)
378
+
379
+ def _start_docker_containers(self) -> None:
380
+ """
381
+ Start the fuseki Docker container,
382
+ wait until it is up and running,
383
+ load some basic ontologies and data into it,
384
+ start the other Docker containers,
385
+ and execute "docker system prune" if necessary.
386
+ """
387
+ self._start_up_fuseki()
388
+ self._wait_for_fuseki()
389
+ self._initialize_fuseki()
390
+ self._start_remaining_docker_containers()
391
+ self._wait_for_api()
392
+ self._execute_docker_system_prune()
393
+
394
+ def start_stack(self) -> bool:
395
+ """
396
+ Start the Docker containers of DSP-API and DSP-APP, and load some basic data models and data.
397
+ After startup, ask user if Docker should be pruned or not.
398
+
399
+ Raises:
400
+ InputError: if the stack cannot be started with the parameters passed by the user
401
+
402
+ Returns:
403
+ True if everything went well, False otherwise
404
+ """
405
+ self._copy_resources_to_home_dir()
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
+ )
414
+ self._start_docker_containers()
415
+ return True
416
+
417
+ def stop_stack(self) -> bool:
418
+ """
419
+ Shut down the Docker containers of your local DSP stack and delete all data that is in it.
420
+
421
+ Returns:
422
+ True if everything went well, False otherwise
423
+ """
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
428
+ return True