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,47 @@
1
+ from dataclasses import dataclass
2
+ from typing import Optional
3
+ from typing import Union
4
+
5
+ from dsp_tools.error.exceptions import BaseError
6
+
7
+ # ruff: noqa: E501 (line-too-long)
8
+
9
+
10
+ @dataclass(frozen=True)
11
+ class PropertyElement:
12
+ """
13
+ A PropertyElement object carries more information about a property value than the value itself.
14
+ The "value" is the value that could be passed to a method as plain string/int/float/bool. Use a PropertyElement
15
+ instead to define more precisely what attributes your value tag (e.g. `<text>`, `<uri>`, ...) will have.
16
+
17
+ Args:
18
+ value: This is the content that will be written into the value tag (e.g. `<text>`, `<uri>`, ...)
19
+ permissions: This is the permissions that your `<text>` tag (for example) will have
20
+ comment: This is the comment that your `<text>` tag (for example) will have
21
+ encoding: For `<text>` tags only. If provided, it must be "xml" or "utf8".
22
+
23
+ Examples:
24
+ See the difference between the first and the second example:
25
+
26
+ >>> excel2xml.make_text_prop(":testproperty", "first text")
27
+ <text-prop name=":testproperty">
28
+ <text encoding="utf8" permissions="public">
29
+ first text
30
+ </text>
31
+ </text-prop>
32
+ >>> excel2xml.make_text_prop(":testproperty", excel2xml.PropertyElement("first text", permissions="private", encoding="xml"))
33
+ <text-prop name=":testproperty">
34
+ <text encoding="xml" permissions="private">
35
+ first text
36
+ </text>
37
+ </text-prop>
38
+ """
39
+
40
+ value: Union[str, int, float, bool]
41
+ permissions: str = "public"
42
+ comment: Optional[str] = None
43
+ encoding: Optional[str] = None
44
+
45
+ def __post_init__(self) -> None:
46
+ if self.encoding not in ["utf8", "xml", None]:
47
+ raise BaseError(f"'{self.encoding}' is not a valid encoding for a PropertyElement")
File without changes
@@ -0,0 +1,166 @@
1
+ import json
2
+ import warnings
3
+ from typing import Any
4
+
5
+ import regex
6
+
7
+ from dsp_tools.cli.args import ServerCredentials
8
+ from dsp_tools.clients.authentication_client_live import AuthenticationClientLive
9
+ from dsp_tools.clients.connection import Connection
10
+ from dsp_tools.clients.connection_live import ConnectionLive
11
+ from dsp_tools.commands.get.get_permissions import get_default_permissions
12
+ from dsp_tools.commands.get.legacy_models.group import Group
13
+ from dsp_tools.commands.get.legacy_models.listnode import ListNode
14
+ from dsp_tools.commands.get.legacy_models.ontology import Ontology
15
+ from dsp_tools.commands.get.legacy_models.project import Project
16
+ from dsp_tools.commands.get.legacy_models.user import User
17
+ from dsp_tools.error.exceptions import BaseError
18
+
19
+
20
+ def get_project(
21
+ project_identifier: str,
22
+ outfile_path: str,
23
+ creds: ServerCredentials,
24
+ verbose: bool = False,
25
+ ) -> bool:
26
+ """
27
+ This function writes a project from a DSP server into a JSON file.
28
+
29
+ Args:
30
+ project_identifier: the project identifier, either shortcode, shortname or IRI of the project
31
+ outfile_path: the output file the JSON content should be written to
32
+ creds: the credentials to access the DSP server
33
+ verbose: verbose option for the command, if used more output is given to the user
34
+
35
+ Raises:
36
+ BaseError: if something went wrong
37
+
38
+ Returns:
39
+ True if the process finishes without errors
40
+ """
41
+ auth = AuthenticationClientLive(creds.server, creds.user, creds.password)
42
+ try:
43
+ auth.get_token()
44
+ con = ConnectionLive(creds.server, auth)
45
+ authenticated = True
46
+ except BaseError:
47
+ warnings.warn("WARNING: Missing or wrong credentials. You won't get sensitive data of this project.")
48
+ con = ConnectionLive(creds.server)
49
+ authenticated = False
50
+
51
+ project = _create_project(con, project_identifier)
52
+
53
+ project = project.read()
54
+ project_obj = project.createDefinitionFileObj()
55
+
56
+ prefixes, ontos = _get_ontologies(con, str(project.iri), verbose)
57
+
58
+ if authenticated:
59
+ default_permissions, default_permissions_overrule = get_default_permissions(auth, str(project.iri), prefixes)
60
+ project_obj["default_permissions"] = default_permissions
61
+ if default_permissions_overrule:
62
+ project_obj["default_permissions_overrule"] = default_permissions_overrule
63
+ else:
64
+ project_obj["default_permissions"] = "Please provide credentials to retrieve the permissions of this project."
65
+
66
+ project_obj["groups"] = _get_groups(con, str(project.iri), verbose)
67
+
68
+ if users := _get_users(con, project, verbose):
69
+ project_obj["users"] = users
70
+
71
+ project_obj["lists"] = _get_lists(con, project, verbose)
72
+
73
+ project_obj["ontologies"] = ontos
74
+
75
+ schema = "https://raw.githubusercontent.com/dasch-swiss/dsp-tools/main/src/dsp_tools/resources/schema/project.json"
76
+ outfile_content = {
77
+ "prefixes": prefixes,
78
+ "$schema": schema,
79
+ "project": project_obj,
80
+ }
81
+
82
+ with open(outfile_path, "w", encoding="utf-8") as f:
83
+ json.dump(outfile_content, f, indent=4, ensure_ascii=False)
84
+
85
+ return True
86
+
87
+
88
+ def _create_project(con: Connection, project_identifier: str) -> Project:
89
+ if regex.match("[0-9A-F]{4}", project_identifier): # shortcode
90
+ return Project(con=con, shortcode=project_identifier)
91
+ elif regex.match("^[\\w-]+$", project_identifier): # shortname
92
+ return Project(con=con, shortname=project_identifier.lower())
93
+ elif regex.match("^(http)s?://([\\w\\.\\-~]+:?\\d{,4})(/[\\w\\-~]+)+$", project_identifier): # iri
94
+ return Project(con=con, shortname=project_identifier)
95
+ else:
96
+ raise BaseError(
97
+ f"ERROR Invalid project identifier '{project_identifier}'. Use the project's shortcode, shortname or IRI."
98
+ )
99
+
100
+
101
+ def _get_groups(con: Connection, project_iri: str, verbose: bool) -> list[dict[str, Any]]:
102
+ if verbose:
103
+ print("Getting groups...")
104
+ groups_obj: list[dict[str, Any]] = []
105
+ if groups := Group.getAllGroupsForProject(con=con, proj_iri=project_iri):
106
+ for group in groups:
107
+ groups_obj.append(group.createDefinitionFileObj())
108
+ if verbose:
109
+ print(f" Got group '{group.name}'")
110
+ return groups_obj
111
+
112
+
113
+ def _get_users(con: Connection, project: Project, verbose: bool) -> list[dict[str, Any]] | None:
114
+ if verbose:
115
+ print("Getting users...")
116
+ try:
117
+ users = User.getAllUsersForProject(con=con, proj_shortcode=str(project.shortcode))
118
+ except BaseError:
119
+ return None
120
+ if users is None:
121
+ return None
122
+
123
+ users_obj: list[dict[str, Any]] = []
124
+ for usr in users:
125
+ users_obj.append(
126
+ usr.createDefinitionFileObj(
127
+ con=con,
128
+ proj_shortname=str(project.shortname),
129
+ proj_iri=str(project.iri),
130
+ )
131
+ )
132
+ if verbose:
133
+ print(f" Got user '{usr.username}'")
134
+ return users_obj
135
+
136
+
137
+ def _get_lists(con: Connection, project: Project, verbose: bool) -> list[dict[str, Any]]:
138
+ if verbose:
139
+ print("Getting lists...")
140
+ list_obj: list[dict[str, Any]] = []
141
+ if list_roots := ListNode.getAllLists(con=con, project_iri=project.iri):
142
+ for list_root in list_roots:
143
+ complete_list = list_root.getAllNodes()
144
+ list_obj.append(complete_list.createDefinitionFileObj())
145
+ if verbose:
146
+ print(f" Got list '{list_root.name}'")
147
+ return list_obj
148
+
149
+
150
+ def _get_ontologies(con: Connection, project_iri: str, verbose: bool) -> tuple[dict[str, str], list[dict[str, Any]]]:
151
+ if verbose:
152
+ print("Getting ontologies...")
153
+ ontos = []
154
+ prefixes: dict[str, str] = {}
155
+ ontologies = Ontology.getProjectOntologies(con, project_iri)
156
+ ontology_ids = [onto.iri for onto in ontologies]
157
+ for ontology_id in ontology_ids:
158
+ onto_url_parts = ontology_id.split("/") # an id has the form http://0.0.0.0:3333/ontology/4123/testonto/v2
159
+ name = onto_url_parts[len(onto_url_parts) - 2]
160
+ shortcode = onto_url_parts[len(onto_url_parts) - 3]
161
+ ontology = Ontology.getOntologyFromServer(con=con, shortcode=shortcode, name=name)
162
+ ontos.append(ontology.createDefinitionFileObj())
163
+ prefixes.update(ontology.context.get_externals_used())
164
+ if verbose:
165
+ print(f" Got ontology '{name}'")
166
+ return prefixes, ontos
@@ -0,0 +1,257 @@
1
+ from typing import Any
2
+ from typing import Literal
3
+
4
+ import regex
5
+
6
+ from dsp_tools.clients.authentication_client import AuthenticationClient
7
+ from dsp_tools.clients.permissions_client import PermissionsClient
8
+ from dsp_tools.commands.get.get_permissions_legacy import parse_legacy_doaps
9
+ from dsp_tools.commands.get.models.permissions_models import DoapCategories
10
+ from dsp_tools.error.exceptions import UnknownDOAPException
11
+
12
+
13
+ def get_default_permissions(
14
+ auth: AuthenticationClient, project_iri: str, prefixes: dict[str, str]
15
+ ) -> tuple[str, dict[str, list[str] | Literal["all"]] | None]:
16
+ """
17
+ Retrieve the DOAPs of a project from the server,
18
+ and try to fit them into our system of "default_permissions" and "default_permissions_overrule".
19
+ If an anomaly is found, return an error message for "default_permissions",
20
+ and None for "default_permissions_overrule".
21
+
22
+ Returns:
23
+ "default_permissions": "public" or "private" or error message
24
+ "default_permissions_overrule": {"private": [<classes_or_props>], "limited_view": ["all" or <img_classes>]}
25
+ """
26
+ perm_client = PermissionsClient(auth, project_iri)
27
+ project_doaps = perm_client.get_project_doaps()
28
+ fallback_text = (
29
+ "We cannot determine if this project is public or private. "
30
+ "The DSP-TOOLS devs can assist you in analysing the existing DOAPs, "
31
+ "and help you decide if the original intent was rather public or rather private."
32
+ )
33
+ try:
34
+ default_permissions = _parse_default_permissions(project_doaps)
35
+ default_permissions_overrule = _parse_default_permissions_overrule(project_doaps, prefixes)
36
+ except UnknownDOAPException:
37
+ default_permissions = fallback_text
38
+ default_permissions_overrule = None
39
+ return default_permissions, default_permissions_overrule
40
+
41
+
42
+ def _parse_default_permissions(project_doaps: list[dict[str, Any]]) -> str:
43
+ """
44
+ First tries to parse legacy DOAPs with multiple groups, then falls back to new-style parsing.
45
+ New-style parsing: If the DOAPs exactly match our definition of public/private, return public/private.
46
+ Otherwise, raise an exception.
47
+ """
48
+ # First, try to parse legacy DOAPs (multiple groups: ProjectAdmin, ProjectMember)
49
+ if legacy_result := parse_legacy_doaps(project_doaps):
50
+ return legacy_result
51
+
52
+ # Fall back to new-style parsing (single ProjectMember group)
53
+ unsupported_groups = ("SystemAdmin", "ProjectAdmin", "Creator", "KnownUser", "UnknownUser")
54
+ if [x for x in project_doaps if x.get("forGroup", "").endswith(unsupported_groups)]:
55
+ raise UnknownDOAPException("The only supported target group for DOAPs is ProjectMember.")
56
+ proj_member_doaps = [x for x in project_doaps if x.get("forGroup", "").endswith("ProjectMember")]
57
+ if len(proj_member_doaps) != 1:
58
+ raise UnknownDOAPException("There must be exactly 1 DOAP for ProjectMember.")
59
+ perms = proj_member_doaps[0]["hasPermissions"]
60
+ if len(perms) not in [2, 4]:
61
+ err_msg = "The only allowed permissions are 'private' (with 2 elements), and 'limited_view' (with 4 elements)"
62
+ raise UnknownDOAPException(err_msg)
63
+ proj_adm_perms = [x for x in perms if x["additionalInformation"].endswith("ProjectAdmin")]
64
+ proj_mem_perms = [x for x in perms if x["additionalInformation"].endswith("ProjectMember")]
65
+ knwn_usr_perms = [x for x in perms if x["additionalInformation"].endswith("KnownUser")]
66
+ unkn_usr_perms = [x for x in perms if x["additionalInformation"].endswith("UnknownUser")]
67
+ if not (len(proj_adm_perms) == len(proj_mem_perms) == 1):
68
+ raise UnknownDOAPException("There must be always 1 permission for ProjectAdmin and 1 for ProjectMember")
69
+ if proj_adm_perms[0]["name"] != "CR" or proj_mem_perms[0]["name"] != "D":
70
+ raise UnknownDOAPException("ProjectAdmin must always have CR and ProjectMember must always have D")
71
+ if len(knwn_usr_perms) == len(unkn_usr_perms) == 0:
72
+ return "private"
73
+ if not (len(knwn_usr_perms) == len(unkn_usr_perms) == 1):
74
+ raise UnknownDOAPException("In case of 'limited_view', there must be 1 for KnownUser and 1 for UnknownUser")
75
+ if knwn_usr_perms[0]["name"] != "V" or unkn_usr_perms[0]["name"] != "V":
76
+ raise UnknownDOAPException("In case of 'public', KnownUser and UnknownUser must always have V")
77
+ return "public"
78
+
79
+
80
+ def _parse_default_permissions_overrule(
81
+ project_doaps: list[dict[str, Any]], prefixes: dict[str, str]
82
+ ) -> dict[str, list[str] | Literal["all"]] | None:
83
+ """
84
+ The DOAPs retrieved from the server are examined if they fit into our system of the overrules.
85
+ If yes, an overrule object is returned. Otherwise, an exception is raised.
86
+
87
+ Args:
88
+ project_doaps: DOAPs as retrieved from the server
89
+ prefixes: dict in the form {"my-onto": "http://0.0.0.0:3333/ontology/1234/my-onto/v2"}
90
+
91
+ Returns:
92
+ an overrule object that can be written into the JSON project definition file, in this form:
93
+ "default_permissions_overrule": {"private": [<classes_or_props>], "limited_view": ["all" or <img_classes>]}
94
+
95
+ Raises:
96
+ UnknownDOAPException: if there are DOAPs that do not fit into our system
97
+ """
98
+ prefixes_knora_base_inverted = _convert_prefixes(prefixes)
99
+ doap_categories = _categorize_doaps(project_doaps)
100
+ _validate_doap_categories(doap_categories)
101
+ return _construct_overrule_object(doap_categories, prefixes_knora_base_inverted)
102
+
103
+
104
+ def _convert_prefixes(prefixes: dict[str, str]) -> dict[str, str]:
105
+ """
106
+ Convert knora-api form of prefixes into knora-base form (used by DOAPs), and invert it.
107
+
108
+ Args:
109
+ prefixes: dict in the form of {"my-onto": "http://0.0.0.0:3333/ontology/1234/my-onto/v2"}
110
+
111
+ Returns:
112
+ dict in the form of {"http://www.knora.org/ontology/1234/my-onto": "my-onto"}
113
+ """
114
+ prefixes_knora_base_inverted = {}
115
+ for onto_shorthand, knora_api_iri in prefixes.items():
116
+ if match := regex.search(r"/ontology/([0-9A-Fa-f]{4})/([^/]+)/v2", knora_api_iri):
117
+ shortcode, onto_name = match.groups()
118
+ prefixes_knora_base_inverted[f"http://www.knora.org/ontology/{shortcode}/{onto_name}"] = onto_shorthand
119
+ return prefixes_knora_base_inverted
120
+
121
+
122
+ def _categorize_doaps(project_doaps: list[dict[str, Any]]) -> DoapCategories:
123
+ """
124
+ The overrule object of the JSON project definition file has 2 categories: private and limited_view.
125
+ - "private" is a list of classes/properties that are private.
126
+ The DOAPs for these correspond 1:1 to the classes/properties.
127
+ - "limited_view" is
128
+ - a list of image classes that are limited_view:
129
+ The DOAPs for these are for knora-api:hasStillImageFileValue and the respective class.
130
+ - or the string "all". The DOAPs for these are only for knora-api:hasStillImageFileValue.
131
+
132
+ This function groups the DOAPs into these categories.
133
+
134
+ Args:
135
+ project_doaps: the DOAPs as retrieved from the server
136
+
137
+ Raises:
138
+ UnknownDOAPException: if there are DOAPs that do not fit into our system
139
+
140
+ Returns:
141
+ a DTO with the categories
142
+ """
143
+ class_doaps = []
144
+ prop_doaps = []
145
+ has_img_all_classes_doaps = []
146
+ has_img_specific_class_doaps = []
147
+ other_doaps = []
148
+ for doap in project_doaps:
149
+ match (doap.get("forResourceClass"), doap.get("forProperty")):
150
+ case (for_class, None) if for_class:
151
+ class_doaps.append(doap)
152
+ case (None, for_prop) if for_prop and "hasStillImageFileValue" not in for_prop:
153
+ prop_doaps.append(doap)
154
+ case (None, for_prop) if for_prop and "hasStillImageFileValue" in for_prop:
155
+ has_img_all_classes_doaps.append(doap)
156
+ case (for_class, for_prop) if for_class and for_prop and "hasStillImageFileValue" in for_prop:
157
+ has_img_specific_class_doaps.append(doap)
158
+ case _:
159
+ other_doaps.append(doap)
160
+ # Only validate other_doaps if there are any
161
+ if other_doaps:
162
+ try:
163
+ _parse_default_permissions(other_doaps)
164
+ except UnknownDOAPException:
165
+ raise UnknownDOAPException("Found DOAPs that do not fit into our system") from None
166
+ return DoapCategories(
167
+ class_doaps=class_doaps,
168
+ prop_doaps=prop_doaps,
169
+ has_img_all_classes_doaps=has_img_all_classes_doaps,
170
+ has_img_specific_class_doaps=has_img_specific_class_doaps,
171
+ )
172
+
173
+
174
+ def _validate_doap_categories(doap_categories: DoapCategories) -> None:
175
+ for expected_private_doap in doap_categories.class_doaps + doap_categories.prop_doaps:
176
+ perm = sorted(expected_private_doap["hasPermissions"], key=lambda x: x["name"])
177
+ if len(perm) != 2:
178
+ raise UnknownDOAPException("'private' is defined as CR ProjectAdmin|D ProjectMember")
179
+ CR, D = perm
180
+ if CR["name"] != "CR" or not CR["additionalInformation"].endswith("ProjectAdmin"):
181
+ raise UnknownDOAPException("'private' is defined as CR ProjectAdmin|D ProjectMember")
182
+ if D["name"] != "D" or not D["additionalInformation"].endswith("ProjectMember"):
183
+ raise UnknownDOAPException("'private' is defined as CR ProjectAdmin|D ProjectMember")
184
+
185
+ for expected_limited_view in (
186
+ doap_categories.has_img_all_classes_doaps + doap_categories.has_img_specific_class_doaps
187
+ ):
188
+ err_msg = "'limited_view' is defined as CR ProjectAdmin|D ProjectMember|RV KnownUser|RV UnknownUser"
189
+ perm = sorted(expected_limited_view["hasPermissions"], key=lambda x: x["name"])
190
+ if len(perm) != 4:
191
+ raise UnknownDOAPException(err_msg)
192
+ CR, D, RV1, RV2 = perm
193
+ if CR["name"] != "CR" or not CR["additionalInformation"].endswith("ProjectAdmin"):
194
+ raise UnknownDOAPException(err_msg)
195
+ if D["name"] != "D" or not D["additionalInformation"].endswith("ProjectMember"):
196
+ raise UnknownDOAPException(err_msg)
197
+ if RV1["name"] != "RV" or not RV2["additionalInformation"].endswith("nownUser"):
198
+ raise UnknownDOAPException(err_msg)
199
+ if RV2["name"] != "RV" or not RV2["additionalInformation"].endswith("nownUser"):
200
+ raise UnknownDOAPException(err_msg)
201
+
202
+
203
+ def _construct_overrule_object(
204
+ doap_categories: DoapCategories, prefixes_knora_base_inverted: dict[str, str]
205
+ ) -> dict[str, list[str] | Literal["all"]]:
206
+ """
207
+ Construct the final overrules object that can be written into the JSON project definition file.
208
+ To do so, the fully qualified IRIs of the classes/properties must be converted to prefixed IRIs.
209
+
210
+ Args:
211
+ doap_categories: The categorized DOAPs from the server
212
+ prefixes_knora_base_inverted: lookup from fully qualified IRIs to prefixed IRIs
213
+
214
+ Raises:
215
+ UnknownDOAPException: if the DOAPs do not fit into our system
216
+
217
+ Returns:
218
+ the final overrules object that can be written into the JSON project definition file
219
+ """
220
+ privates: list[str] = []
221
+ for class_doap in doap_categories.class_doaps:
222
+ privates.append(_get_prefixed_iri(class_doap["forResourceClass"], prefixes_knora_base_inverted))
223
+ for prop_doap in doap_categories.prop_doaps:
224
+ privates.append(_get_prefixed_iri(prop_doap["forProperty"], prefixes_knora_base_inverted))
225
+
226
+ limited_views: list[str] | Literal["all"]
227
+ if len(doap_categories.has_img_all_classes_doaps) > 1:
228
+ raise UnknownDOAPException("There can only be 1 all-images DOAP for 'hasStillImageFileValue'")
229
+ if len(doap_categories.has_img_all_classes_doaps) == 1 and len(doap_categories.has_img_specific_class_doaps) > 0:
230
+ raise UnknownDOAPException("If there is a DOAP for all images, there cannot be DOAPs for specific img classes")
231
+ if len(doap_categories.has_img_all_classes_doaps) == 1:
232
+ limited_views = "all"
233
+ else:
234
+ limited_views = []
235
+ for img_doap in doap_categories.has_img_specific_class_doaps:
236
+ limited_views.append(_get_prefixed_iri(img_doap["forResourceClass"], prefixes_knora_base_inverted))
237
+
238
+ result: dict[str, list[str] | Literal["all"]] = {}
239
+ if privates:
240
+ result["private"] = privates
241
+ if limited_views:
242
+ result["limited_view"] = limited_views
243
+ return result
244
+
245
+
246
+ def _get_prefixed_iri(full_iri: str, prefixes_inverted: dict[str, str]) -> str:
247
+ # example:
248
+ # - full_iri = "http://www.knora.org/ontology/1234/my-onto/v2#MyClass"
249
+ # - prefixes_inverted = {"http://www.knora.org/ontology/1234/my-onto": "my-onto"}
250
+ # - output = "my-onto:MyClass"
251
+ if "#" not in full_iri:
252
+ raise ValueError(f"{full_iri} is not a valid full IRI")
253
+ before_hashtag, after_hashtag = full_iri.rsplit("#", maxsplit=1)
254
+ if before_hashtag not in prefixes_inverted:
255
+ raise ValueError(f"{full_iri} belongs to an unknown ontology. It cannot be found in the prefixes.")
256
+ prefix = prefixes_inverted[before_hashtag]
257
+ return f"{prefix}:{after_hashtag}"
@@ -0,0 +1,89 @@
1
+ from typing import Any
2
+ from typing import Literal
3
+
4
+
5
+ def parse_legacy_doaps(project_doaps: list[dict[str, Any]]) -> Literal["public", "private"] | None:
6
+ """
7
+ Check if DOAPs match one of the legacy patterns.
8
+
9
+ Legacy private:
10
+ - For group ProjectAdmin: ProjectAdmin CR, ProjectMember CR/M
11
+ - For group ProjectMember: ProjectAdmin CR, ProjectMember CR/M
12
+
13
+ Legacy public:
14
+ - For group ProjectAdmin: ProjectAdmin CR, (optionally: Creator CR), ProjectMember D/M, KnownUser V, UnknownUser V
15
+ - For group ProjectMember: ProjectAdmin CR, (optionally: Creator CR), ProjectMember D/M, KnownUser V, UnknownUser V
16
+ """
17
+ if len(project_doaps) != 2:
18
+ return None
19
+ admin_doaps = [x for x in project_doaps if x.get("forGroup", "").endswith("ProjectAdmin")]
20
+ member_doaps = [x for x in project_doaps if x.get("forGroup", "").endswith("ProjectMember")]
21
+ if len(admin_doaps) != 1 or len(member_doaps) != 1:
22
+ return None
23
+
24
+ admin_perms = admin_doaps[0]["hasPermissions"]
25
+ member_perms = member_doaps[0]["hasPermissions"]
26
+ if all([_is_legacy_private(admin_perms), _is_legacy_private(member_perms)]):
27
+ return "private"
28
+ if all([_is_legacy_public(admin_perms), _is_legacy_public(member_perms)]):
29
+ return "public"
30
+
31
+ return None
32
+
33
+
34
+ def _is_legacy_private(perms: list[dict[str, Any]]) -> bool:
35
+ """
36
+ Check if permissions match the legacy private pattern: ProjectAdmin CR, ProjectMember M/D
37
+
38
+ Args:
39
+ perms: List of permission objects
40
+ """
41
+ if len(perms) != 2:
42
+ return False
43
+
44
+ sorted_perms = sorted(perms, key=lambda x: x.get("name", ""))
45
+
46
+ # First should be CR for ProjectAdmin
47
+ if sorted_perms[0]["name"] != "CR" or not sorted_perms[0]["additionalInformation"].endswith("ProjectAdmin"):
48
+ return False
49
+
50
+ # Second should be D or M for ProjectMember
51
+ if sorted_perms[1]["name"] not in ["D", "M"] or not sorted_perms[1]["additionalInformation"].endswith(
52
+ "ProjectMember"
53
+ ):
54
+ return False
55
+
56
+ return True
57
+
58
+
59
+ def _is_legacy_public(perms: list[dict[str, Any]]) -> bool:
60
+ """
61
+ Check if permissions match the public pattern:
62
+ ProjectAdmin CR, (optionally: Creator CR), ProjectMember D/M, KnownUser V, UnknownUser V
63
+ """
64
+ # Should have exactly 4 permissions after filtering out Creator
65
+ filtered_perms = [p for p in perms if not p["additionalInformation"].endswith("Creator")]
66
+ if len(filtered_perms) != 4:
67
+ return False
68
+
69
+ sorted_perms = sorted(filtered_perms, key=lambda x: x.get("name", ""))
70
+
71
+ # First should be CR for ProjectAdmin
72
+ if sorted_perms[0]["name"] != "CR" or not sorted_perms[0]["additionalInformation"].endswith("ProjectAdmin"):
73
+ return False
74
+
75
+ # Second should be D or M for ProjectMember
76
+ if sorted_perms[1]["name"] not in ["D", "M"] or not sorted_perms[1]["additionalInformation"].endswith(
77
+ "ProjectMember"
78
+ ):
79
+ return False
80
+
81
+ # Third/Fourth should be V for KnownUser
82
+ if sorted_perms[2]["name"] != "V" or not sorted_perms[2]["additionalInformation"].endswith("nownUser"):
83
+ return False
84
+
85
+ # Third/Fourth should be V for UnknownUser
86
+ if sorted_perms[3]["name"] != "V" or not sorted_perms[3]["additionalInformation"].endswith("nownUser"):
87
+ return False
88
+
89
+ return True
File without changes