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
knora/knora.py DELETED
@@ -1,2108 +0,0 @@
1
- from typing import List, Set, Dict, Tuple, Optional, Any, Union
2
- from urllib.parse import quote_plus
3
- from rdflib import Graph
4
- from lxml import etree
5
- import requests
6
- import json
7
- import urllib
8
- import pprint
9
- import validators
10
- import re
11
- from rfc3987 import parse
12
- from pprint import pprint
13
- import sys
14
-
15
- # TODO: recheck all the documentation of this file
16
- """
17
- Properties in knora-api:
18
-
19
- - :hasValue
20
- - :hasColor
21
- - :hasComment
22
- - :hasGeometry
23
- - :hasLinkTo
24
- - :isPartOf
25
- - :isRegionOf
26
- - :isAnnotationOf
27
- - :seqnum
28
-
29
- Classes in knora-api:
30
- - :Resource
31
- - :StillImageRepresentation
32
- - :TextRepresentation
33
- - :AudioRepresentation
34
- - :DDDRepresentation
35
- - :DocumentRepresentation
36
- - :MovingImageRepresentation
37
- - :Annotation -> :hasComment, :isAnnotationOf, :isAnnotationOfValue
38
- - :LinkObj -> :hasComment, :hasLinkTo, :hasLinkToValue
39
- - :LinkValue [reification node]
40
- - :Region -> :hasColor, :isRegionOf, :hasGeometry, :isRegionOfValue, :hasComment
41
-
42
- For lists:
43
-
44
- - :ListNode -> :hasSubListNode, :listNodePosition, :listNodeName, :isRootNode, :hasRootNode, :attachedToProject
45
-
46
- Values in knora-api:
47
-
48
- - :Value
49
- - :TextValue -> :SimpleText, :TextArea
50
- - :ColorValue -> :Colorpicker
51
- - :DateValue -> :Date
52
- - :DecimalValue -> :SimpleText
53
- - :GeomValue -> :Geometry
54
- - :GeonameValue -> :Geonames
55
- - :IntValue -> :SimpleText, :Spinbox, :Slider
56
- - :BooleanValue -> :Checkbox
57
- - :UriValue -> :SimpleText
58
- - :IntervalValue
59
- - :ListValue -> :Pulldown
60
-
61
- GUI elements
62
-
63
- - :Colorpicker -> ncolors=integer
64
- - :Date
65
- - :Geometry
66
- - :Geonames
67
- - :Interval
68
- - :List -> hlist(required)=<iri>
69
- - :Pulldown -> hlist(required)=<iri>
70
- - :Radio -> hlist(required)=<iri>
71
- - :Richtext
72
- - :Searchbox -> numprops=integer
73
- - :SimpleText -> maxlength=integer, size=integer
74
- - :Slider -> max(required)=decimal, min(required)=decimal
75
- - :Spinbox -> max=decimal, min=decimal
76
- - :Textarea -> cols=integer, rows=integer, width=percent, wrap=string(soft|hard)
77
- - :Checkbox
78
- - :Fileupload
79
- """
80
-
81
-
82
- class KnoraError(Exception):
83
- """Handles errors happening in this file"""
84
-
85
- def __init__(self, message):
86
- self.message = message
87
-
88
-
89
- class KnoraStandoffXml:
90
- """Used to handle XML strings for standoff markup"""
91
-
92
- iriregexp = re.compile(r'IRI:[^:]*:IRI')
93
-
94
- def __init__(self, xmlstr: str):
95
- self.xmlstr = xmlstr
96
-
97
- def getXml(self):
98
- return self.xmlstr
99
-
100
- def findall(self):
101
- return KnoraStandoffXml.iriregexp.findall(self.xmlstr)
102
-
103
- def replace(self, fromStr: str, toStr: str):
104
- self.xmlstr.replace(fromStr, toStr)
105
-
106
-
107
- class KnoraStandoffXmlEncoder(json.JSONEncoder):
108
- """Classes used as wrapper for knora standoff-XML"""
109
- def default(self, obj):
110
- if isinstance(obj, KnoraStandoffXml):
111
- return obj.getXml()
112
- return json.JSONEncoder.default(self, obj)
113
-
114
-
115
- class Knora:
116
- """
117
- This is the main class which holds all the methods for communication with the Knora backend.
118
- """
119
-
120
- def __init__(self, server: str, prefixes: Dict[str, str] = None):
121
- """
122
- Constructor requiring the server address, the user and password of KNORA
123
- :param server: Address of the server, e.g https://api.dasch.swiss
124
- :param prefixes: Ontology prefixes used
125
- """
126
- self.server = server
127
- self.prefixes = prefixes
128
- self.token = None
129
-
130
- def login(self, email: str, password: str) -> None:
131
- """
132
- Method to login into KNORA which creates a session token.
133
- :param email: Email of user, e.g., root@example.com
134
- :param password: Password of the user, e.g. test
135
- """
136
- credentials = {
137
- "email": email,
138
- "password": password
139
- }
140
- jsondata = json.dumps(credentials)
141
-
142
- req = requests.post(
143
- self.server + '/v2/authentication',
144
- headers={'Content-Type': 'application/json; charset=UTF-8'},
145
- data=jsondata
146
- )
147
- self.on_api_error(req)
148
- result = req.json()
149
- self.token = result["token"]
150
-
151
- def get_token(self) -> str:
152
- return self.token
153
-
154
- def logout(self) -> None:
155
- if self.token is not None:
156
- req = requests.delete(
157
- self.server + '/v2/authentication',
158
- headers={'Authorization': 'Bearer ' + self.token}
159
- )
160
- self.on_api_error(req)
161
- self.token = None
162
-
163
- def __del__(self):
164
- self.logout()
165
-
166
- def on_api_error(self, res) -> None:
167
- """
168
- Method to check for any API errors
169
- :param res: The input to check, usually JSON format
170
- :return: Possible KnoraError that is being raised
171
- """
172
-
173
- if (res.status_code != 200):
174
- raise KnoraError("KNORA-ERROR: status code=" + str(res.status_code) + "\nMessage:" + res.text)
175
-
176
- if 'error' in res:
177
- raise KnoraError("KNORA-ERROR: API error: " + res.error)
178
-
179
- #==========================================================================
180
- # project related methods
181
- #
182
-
183
- def get_existing_projects(self, full: bool = False) -> List[Any]:
184
- """Returns a list of existing projects
185
-
186
- :return: List of existing projects
187
- """
188
-
189
- req = requests.get(self.server + '/admin/projects',
190
- headers={'Authorization': 'Bearer ' + self.token})
191
- self.on_api_error(req)
192
- result = req.json()
193
-
194
- if 'projects' not in result:
195
- raise KnoraError("KNORA-ERROR:\n Request got no projects!")
196
- else:
197
- if full:
198
- return result['projects']
199
- else:
200
- return list(map(lambda a: a['id'], result['projects']))
201
-
202
- def get_project(self, shortcode: str) -> Dict[str,Any]:
203
- """Returns project data of given project
204
-
205
- :param shortcode: Shortcode of object
206
- :return: JSON containing the project information
207
- """
208
-
209
- url = self.server + '/admin/projects/shortcode/' + shortcode
210
- req = requests.get(url, headers={'Authorization': 'Bearer ' + self.token})
211
- self.on_api_error(req)
212
-
213
- result = req.json()
214
-
215
- return result["project"]
216
-
217
- def project_exists(self, proj_iri: str) -> bool:
218
- """Checks if a given project exists
219
-
220
- :return: Boolean
221
- """
222
-
223
- projects = self.get_existing_projects()
224
- return proj_iri in projects
225
-
226
- def create_project(
227
- self,
228
- shortcode: str,
229
- shortname: str,
230
- longname: str,
231
- descriptions: Optional[Dict[str, str]] = None,
232
- keywords: Optional[List[str]] = None,
233
- logo: Optional[str] = None) -> str:
234
- """
235
- Create a new project
236
-
237
- :param shortcode: Dedicated shortcode of project
238
- :param shortname: Short name of the project (e.g acronym)
239
- :param longname: Long name of project
240
- :param descriptions: Dict of the form {lang: descr, …} for the description of the project [Default: None]
241
- :param keywords: List of keywords
242
- :param logo: Link to the project logo [default: None]
243
- :return: Project IRI
244
- """
245
-
246
- descriptions = list(map(lambda p: {"language": p[0], "value": p[1]}, descriptions.items()))
247
-
248
- project = {
249
- "shortname": shortname,
250
- "shortcode": shortcode,
251
- "longname": longname,
252
- "status": True,
253
- "selfjoin": False
254
- }
255
- if descriptions is not None:
256
- project['description'] = descriptions
257
- if keywords is not None:
258
- project['keywords'] = keywords
259
- if logo is not None:
260
- project['logo'] = logo
261
-
262
- jsondata = json.dumps(project)
263
- # print(jsondata)
264
-
265
- req = requests.post(self.server + "/admin/projects",
266
- headers={'Content-Type': 'application/json; charset=UTF-8',
267
- 'Authorization': 'Bearer ' + self.token},
268
- data=jsondata)
269
- self.on_api_error(req)
270
-
271
- res = req.json()
272
- return res["project"]["id"]
273
-
274
- def update_project(
275
- self,
276
- shortcode: str,
277
- shortname: Optional[str] = None,
278
- longname: Optional[str] = None,
279
- descriptions: Optional[Dict[str, str]] = None,
280
- keywords: Optional[List[str]] = None,
281
- logo: Optional[str] = None) -> str:
282
- """
283
- Update project information
284
-
285
- :param shortcode:
286
- :param shortname:
287
- :param longname:
288
- :param descriptions:
289
- :param keywords:
290
- :param logo:
291
- :return:
292
- """
293
-
294
- descriptions = list(map(lambda p: {"language": p[0], "value": p[1]}, descriptions.items()))
295
-
296
- project = {
297
- "longname": longname,
298
- "description": descriptions,
299
- "keywords": keywords,
300
- "logo": logo,
301
- "status": True,
302
- "selfjoin": False
303
- }
304
-
305
- jsondata = json.dumps(project)
306
- url = self.server + '/admin/projects/iri/' + quote_plus("http://rdfh.ch/projects/" + shortcode)
307
-
308
- req = requests.put(url,
309
- headers={'Content-Type': 'application/json; charset=UTF-8',
310
- 'Authorization': 'Bearer ' + self.token},
311
- data=jsondata)
312
- self.on_api_error(req)
313
-
314
- res = req.json()
315
- return res['project']['id']
316
-
317
- #==========================================================================
318
- # Group related methods
319
- #
320
-
321
- def get_groups(self) -> List[Dict[str,Any]]:
322
- """
323
- Returns the list of existing groups
324
-
325
- :return: List of projects
326
- """
327
- url = self.server + '/admin/groups'
328
-
329
- req = requests.get(url, headers={'Authorization': 'Bearer ' + self.token})
330
-
331
- self.on_api_error(req)
332
- res = req.json()
333
-
334
- return res['groups']
335
-
336
- def get_group_by_iri(self, group_iri: str) -> Dict[str,Any]:
337
- """
338
- Returns information about the given group
339
- :param group_iri: IRI of the group
340
- :return: Information about the specific group
341
- """
342
- url = self.server + '/admin/groups/' + quote_plus(group_iri)
343
-
344
- req = requests.get(url, headers={'Authorization': 'Bearer ' + self.token})
345
-
346
- self.on_api_error(req)
347
- res = req.json()
348
-
349
- return res['group']
350
-
351
- def get_group_by_pshortname_and_gname(self,
352
- project_shortname: str,
353
- group_name: str) -> Union[str,None]:
354
- """
355
- Get a group by project shortname and group name
356
-
357
- :param project_shortname: Project shortname
358
- :param group_name: Group name
359
- :return: IRI of the group
360
- """
361
- groupinfos = self.get_groups()
362
- for groupinfo in groupinfos:
363
- if groupinfo["project"]["shortname"] == project_shortname and groupinfo["name"] == group_name:
364
- return groupinfo["id"]
365
- return None
366
-
367
- def get_group_by_pshortcode_and_gname(self,
368
- project_shortcode: str,
369
- group_name: str) -> Union[str, None]:
370
- """
371
- Get a group by project shortcode and group name
372
-
373
- :param project_shortname: Project shortcode
374
- :param group_name: Group name
375
- :return: IRI of the group
376
- """
377
- groupinfos = self.get_groups()
378
- for groupinfo in groupinfos:
379
- if groupinfo["project"]["shortcode"] == project_shortcode and groupinfo["name"] == group_name:
380
- return groupinfo["id"]
381
- return None
382
-
383
- def get_group_by_piri_and_gname(self,
384
- project_iri: str,
385
- group_name: str) -> Union[str, None]:
386
- """
387
- Get a group by project shortcode and group name
388
-
389
- :param project_shortname: Project shortcode
390
- :param group_name: Group name
391
- :return: IRI of the group
392
- """
393
- groupinfos = self.get_groups()
394
- for groupinfo in groupinfos:
395
- if groupinfo["project"]["id"] == project_iri and groupinfo["name"] == group_name:
396
- return groupinfo["id"]
397
- return None
398
-
399
- def create_group(self,
400
- project_iri: str,
401
- name: str,
402
- description: Union[str, Dict[str,str]],
403
- status: bool = True,
404
- selfjoin: bool = False) -> str:
405
- """
406
- Create a new group
407
-
408
- :param name: Name of the group
409
- :param description: Either a string with the descrioption, or a List of Dicts in the form [{"value": "descr", "language": "lang"},…]
410
- :param project_iri: IRI of the project where the group belongs to
411
- :param status: Active (True) or not active (False) [default: True]
412
- :param selfjoin: ?? [default: False]
413
- :return: IRI of the group
414
- """
415
-
416
- groupinfo = {
417
- "name": name,
418
- "description": description if isinstance(description, str) else list(map(lambda p: {"@language": p[0], "@value": p[1]}, description.items())),
419
- "project": project_iri,
420
- "status": status,
421
- "selfjoin": selfjoin
422
- }
423
- jsondata = json.dumps(groupinfo)
424
-
425
- url = self.server + '/admin/groups'
426
-
427
- req = requests.post(url,
428
- headers={'Content-Type': 'application/json; charset=UTF-8',
429
- 'Authorization': 'Bearer ' + self.token},
430
- data=jsondata)
431
-
432
- self.on_api_error(req)
433
- res = req.json()
434
- return res['group']['id']
435
-
436
- def update_group(self,
437
- group_iri: str,
438
- name: Optional[str] = None,
439
- description: Optional[Union[str, Dict[str,str]]] = None,
440
- selfjoin: Optional[bool] = None) -> Union[str,None]:
441
- """
442
- Modify the data about a group. Only parameters that have to be changed must be indicated
443
- :param group_iri: IRI of the grouo to be modified
444
- :param name: New name of the group [optional]
445
- :param description: Either a string with the descrioption, or a List of Dicts in the form [{"value": "descr", "language": "lang"},…] [optional]
446
- :param selfjoin: True or False [optional]
447
- :return: ???
448
- """
449
-
450
- groupinfo = {}
451
- done = False
452
- if name is not None:
453
- groupinfo['name'] = name
454
- done = True
455
- if description is not None:
456
- groupinfo['description'] = description if isinstance(description, str) else list(map(lambda p: {"@language": p[0], "@value": p[1]}, description.items()))
457
- done = True
458
- if selfjoin is not None:
459
- groupinfo['selfjoin'] = selfjoin
460
- done = True
461
- if done:
462
- jsondata = json.dumps(groupinfo)
463
-
464
- url = self.server + '/admin/groups/' + quote_plus(group_iri)
465
-
466
- req = requests.put(url,
467
- headers={'Content-Type': 'application/json; charset=UTF-8',
468
- 'Authorization': 'Bearer ' + self.token},
469
- data=jsondata)
470
- self.on_api_error(req)
471
- res = req.json()
472
- pprint(res)
473
- return res['group']['id']
474
- else:
475
- return None
476
-
477
- def change_group_status(self,
478
- group_iri: str,
479
- status: bool) -> None:
480
- """
481
- Change the status of th group
482
-
483
- :param group_iri: IRI of the group
484
- :param status: Status (active: True, inactive: False)
485
-
486
- :return: None
487
- """
488
-
489
- statusinfo = {
490
- "status": status
491
- }
492
- jsondata = json.dumps(statusinfo)
493
-
494
- url = self.server + '/admin/groups/' + quote_plus(group_iri) + '/status'
495
-
496
- req = requests.put(url,
497
- headers={'Content-Type': 'application/json; charset=UTF-8',
498
- 'Authorization': 'Bearer ' + self.token},
499
- data=jsondata)
500
- self.on_api_error(req)
501
- res = req.json()
502
- pprint(res)
503
-
504
- def delete_group(self,
505
- group_iri: str) -> None:
506
- """
507
- Delete a group
508
- :param group_iri: IRI of the group
509
- :return:
510
- """
511
- url = self.server + '/admin/groups/' + quote_plus(group_iri)
512
-
513
- req = requests.delete(url,
514
- headers={'Content-Type': 'application/json; charset=UTF-8',
515
- 'Authorization': 'Bearer ' + self.token})
516
- self.on_api_error(req)
517
- res = req.json()
518
- pprint(res)
519
-
520
- #==========================================================================
521
- # User related methods
522
- #
523
-
524
- def get_users(self) -> List[Dict[str,Any]]:
525
- """
526
- Get a list of all users
527
-
528
- :return: Json result.
529
- """
530
- url = self.server + '/admin/users'
531
- req = requests.get(url, headers={'Authorization': 'Bearer ' + self.token})
532
-
533
- self.on_api_error(req)
534
- res = req.json()
535
- return res['users']
536
-
537
- def get_user_by_iri(self, user_iri: str):
538
- """
539
- Get single user
540
-
541
- :return:
542
- """
543
- url = self.server + '/admin/users/iri/' + quote_plus(user_iri)
544
- req = requests.get(url, headers={'Authorization': 'Bearer ' + self.token})
545
-
546
- self.on_api_error(req)
547
- res = req.json()
548
- return res['user']
549
-
550
- def get_user_by_email(self, email: str):
551
- """
552
- Get a list of all users
553
-
554
- :return:
555
- """
556
- url = self.server + '/admin/users/email/' + quote_plus(email)
557
- req = requests.get(url, headers={'Authorization': 'Bearer ' + self.token})
558
-
559
- self.on_api_error(req)
560
- res = req.json()
561
- return res['user']
562
-
563
- def create_user(self,
564
- username: str,
565
- email: str,
566
- given_name: str,
567
- family_name: str,
568
- password: str,
569
- lang: str = "en",
570
- sysadmin: bool = False):
571
- """
572
- Create a new user
573
-
574
- :param username: The username for login purposes (must be unique)
575
- :param email: The email address of the user
576
- :param given_name: The given name (surname, "Vorname", ...)
577
- :param family_name: The family name
578
- :param password: The password for the user
579
- :param lang: language code, either "en", "de", "fr", "it" [default: "en"]
580
- :param sysadmin: True if the user has system admin rights
581
- :return: The user ID as IRI
582
- """
583
-
584
- userinfo = {
585
- "username": username,
586
- "email": email,
587
- "givenName": given_name,
588
- "familyName": family_name,
589
- "password": password,
590
- "status": True,
591
- "lang": lang,
592
- "systemAdmin": sysadmin
593
- }
594
-
595
- jsondata = json.dumps(userinfo)
596
- url = self.server + '/admin/users'
597
-
598
- req = requests.post(url,
599
- headers={'Content-Type': 'application/json; charset=UTF-8',
600
- 'Authorization': 'Bearer ' + self.token},
601
- data=jsondata)
602
-
603
- self.on_api_error(req)
604
- res = req.json()
605
-
606
- return res['user']['id']
607
-
608
- def update_user(self,
609
- user_iri: str,
610
- username: Optional[str] = None,
611
- email: Optional[str] = None,
612
- given_name: Optional[str] = None,
613
- family_name: Optional[str] = None,
614
- password: Optional[str] = None,
615
- lang: Optional[str] = None):
616
- userinfo: Dict[str,Any] = {};
617
- if username is not None:
618
- userinfo["username"] = username
619
- if email is not None:
620
- userinfo["email"] = email
621
- if given_name is not None:
622
- userinfo["givenName"] = given_name
623
- if family_name is not None:
624
- userinfo["familyName"] = family_name
625
- #if password is not None:
626
- # update_user["password"] = password
627
- if lang is not None:
628
- userinfo["lang"] = lang
629
- if len(userinfo) > 0:
630
- url = self.server + '/admin/users/iri/' + quote_plus(user_iri) + '/BasicUserInformation'
631
- jsondata = json.dumps(userinfo)
632
- req = requests.put(url,
633
- headers={'Content-Type': 'application/json; charset=UTF-8',
634
- 'Authorization': 'Bearer ' + self.token},
635
- data=jsondata)
636
- self.on_api_error(req)
637
-
638
- def change_user_password(self,
639
- user_iri: str,
640
- admin_password: str,
641
- new_password: str):
642
- data = {
643
- "requesterPassword": admin_password,
644
- "newPassword": new_password
645
- }
646
- url = self.server + '/admin/users/iri/' + quote_plus(user_iri) + '/Password'
647
- jsondata = json.dumps(data)
648
- req = requests.put(url,
649
- headers={'Content-Type': 'application/json; charset=UTF-8',
650
- 'Authorization': 'Bearer ' + self.token},
651
- data=jsondata)
652
- self.on_api_error(req)
653
-
654
- def add_user_to_project(self,
655
- user_iri: str,
656
- project_iri: str):
657
- """
658
- Add a user to a project
659
-
660
- :param user_iri: IRI of the user
661
- :param project_iri: IRI of the project
662
- :return: None
663
- """
664
- url = self.server + '/admin/users/iri/' + quote_plus(user_iri) + '/project-memberships/'\
665
- + quote_plus(project_iri)
666
- req = requests.post(url, headers={'Authorization': 'Bearer ' + self.token})
667
- self.on_api_error(req)
668
-
669
- return None
670
-
671
- def rm_user_from_project(self,
672
- user_iri: str,
673
- project_iri: str):
674
- """
675
- Remove a user from a project
676
-
677
- :param user_iri: IRI of the user
678
- :param project_iri: IRI of the project
679
- :return: None
680
- """
681
- url = self.server + '/admin/users/iri/' + quote_plus(user_iri) + '/project-memberships/' + quote_plus(
682
- project_iri)
683
- req = requests.delete(url, headers={'Authorization': 'Bearer ' + self.token})
684
- self.on_api_error(req)
685
-
686
- return None
687
-
688
- def add_user_to_project_admin(self,
689
- user_iri: str,
690
- project_iri: str) -> None:
691
- """
692
- Add a user to the project admin group (knora-admin:ProjectAdmin)
693
-
694
- :param user_iri: IRI of user
695
- :param project_iri: IRI of project
696
- :return: None
697
- """
698
- url = self.server + '/admin/users/iri/' + quote_plus(user_iri) + '/project-admin-memberships/' + quote_plus(
699
- project_iri)
700
- req = requests.post(url, headers={'Authorization': 'Bearer ' + self.token})
701
- self.on_api_error(req)
702
- return None
703
-
704
- def rm_user_from_project_admin(self,
705
- user_iri: str,
706
- project_iri: str) -> None:
707
- """
708
- Remove a user from the project admin group
709
- :param user_iri: IRI of user
710
- :param project_iri: IRI of project
711
- :return: None
712
- """
713
- url = self.server + '/admin/users/iri/' + quote_plus(user_iri) + '/project-admin-memberships/' + quote_plus(
714
- project_iri)
715
- req = requests.delete(url, headers={'Authorization': 'Bearer ' + self.token})
716
- self.on_api_error(req)
717
- return None
718
-
719
- def add_user_to_sysadmin(self, user_iri: str) -> None:
720
- """
721
- Add a user to the project admin group (knora-admin:ProjectAdmin)
722
-
723
- :param user_iri: IRI of user
724
- :param project_iri: IRI of project
725
- :return: None
726
- """
727
- data = {
728
- "systemAdmin": True
729
- }
730
- url = self.server + '/admin/users/iri/' + quote_plus(user_iri) + '/SystemAdmin'
731
- jsondata = json.dumps(data)
732
- req = requests.put(url, headers={'Content-Type': 'application/json; charset=UTF-8',
733
- 'Authorization': 'Bearer ' + self.token},
734
- data=jsondata)
735
- self.on_api_error(req)
736
- return None
737
-
738
- def rm_user_from_sysadmin(self, user_iri: str) -> None:
739
- """
740
- Remove a user from the system admin group
741
-
742
- :param user_iri: IRI of user
743
- :param project_iri: IRI of project
744
- :return: None
745
- """
746
- data = {
747
- "systemAdmin": False
748
- }
749
- url = self.server + '/admin/users/iri/' + quote_plus(user_iri) + '/SystemAdmin'
750
- jsondata = json.dumps(data)
751
- req = requests.put(url, headers={'Content-Type': 'application/json; charset=UTF-8',
752
- 'Authorization': 'Bearer ' + self.token},
753
- data=jsondata)
754
- self.on_api_error(req)
755
- return None
756
-
757
- def add_user_to_group(self,
758
- user_iri: str,
759
- group_iri: str) -> None:
760
- url = self.server + '/admin/users/iri/' + quote_plus(user_iri) + '/group-memberships/' + quote_plus(group_iri)
761
-
762
- req = requests.post(url, headers={'Authorization': 'Bearer ' + self.token})
763
- self.on_api_error(req)
764
- return None
765
-
766
- def rm_user_from_group(self,
767
- user_iri: str,
768
- group_iri: str) -> None:
769
- url = self.server + '/admin/users/iri/' + quote_plus(user_iri) + '/group-memberships/' + quote_plus(group_iri)
770
-
771
- req = requests.delete(url, headers={'Authorization': 'Bearer ' + self.token})
772
- self.on_api_error(req)
773
- return None
774
-
775
- #==========================================================================
776
- # Ontology methods
777
- #
778
-
779
- def get_existing_ontologies(self):
780
- """
781
-
782
- :return: Returns the metadata of all existing ontologies on v2/ontologies
783
- """
784
-
785
- req = requests.get(self.server + '/v2/ontologies/metadata',
786
- headers={'Authorization': 'Bearer ' + self.token})
787
- result = req.json()
788
-
789
- if not '@graph' in result:
790
- raise KnoraError("KNORA-ERROR:\n Request got no graph!")
791
- else:
792
- names = list(map(lambda a: a['@id'], result['@graph']))
793
- return names
794
-
795
- def get_project_ontologies(self, project_code: str) -> Optional[dict]:
796
- """
797
-
798
- :param project_code:
799
- :return:
800
- """
801
-
802
- proj = quote_plus("http://rdfh.ch/projects/" + project_code)
803
- req = requests.get(self.server + "/v2/ontologies/metadata/" + proj,
804
- headers={'Authorization': 'Bearer ' + self.token})
805
- self.on_api_error(req)
806
- result = req.json()
807
-
808
- if '@graph' in result: # multiple ontologies
809
- ontos = list(map(lambda a: {
810
- 'iri': a['@id'],
811
- 'label': a['rdfs:label'],
812
- 'moddate': a.get('knora-api:lastModificationDate')
813
- }, result['@graph']))
814
- return ontos
815
- elif '@id' in result: # single ontology
816
- return [{
817
- 'iri': result['@id'],
818
- 'label': result['rdfs:label'],
819
- 'moddate': result.get('knora-api:lastModificationDate')
820
- }]
821
- else:
822
- return None
823
-
824
- def ontology_exists(self, onto_iri: str):
825
- """
826
- Checks if an ontology exists
827
-
828
- :param onto_iri: The possible ontology iri
829
- :return: boolean
830
- """
831
-
832
- ontos = self.get_existing_ontologies()
833
-
834
- return onto_iri in ontos
835
-
836
- def get_ontology_lastmoddate(self, onto_iri: str):
837
- """
838
- Retrieves the lastModificationDate of a Ontology
839
-
840
- :param onto_iri: The ontology to retrieve the lastModificationDate from.
841
- :return: The lastModificationDate if it exists. Else, this method returns a dict with (id, None). If the ontology does not exist, it return None.
842
- """
843
-
844
- req = requests.get(self.server + '/v2/ontologies/metadata',
845
- headers={'Authorization': 'Bearer ' + self.token})
846
- result = req.json()
847
-
848
- all_ontos = {}
849
-
850
- for onto in result['@graph']:
851
- if 'knora-api:lastModificationDate' in onto:
852
- all_ontos.__setitem__(onto['@id'], onto['knora-api:lastModificationDate'])
853
- else:
854
- all_ontos.__setitem__(onto['@id'], None)
855
-
856
- return all_ontos[onto_iri]
857
-
858
- def create_ontology(self,
859
- onto_name: str,
860
- project_iri: str,
861
- label: str) -> Dict[str, str]:
862
- """
863
- Create a new ontology
864
-
865
- :param onto_name: Name of the omntology
866
- :param project_iri: IRI of the project
867
- :param label: A label property for this ontology
868
- :return: Dict with "onto_iri" and "last_onto_date"
869
- """
870
-
871
- ontology = {
872
- "knora-api:ontologyName": onto_name,
873
- "knora-api:attachedToProject": {
874
- "@id": project_iri
875
- },
876
- "rdfs:label": label,
877
- "@context": {
878
- "rdfs": 'http://www.w3.org/2000/01/rdf-schema#',
879
- "knora-api": 'http://api.knora.org/ontology/knora-api/v2#'
880
- }
881
- }
882
-
883
- jsondata = json.dumps(ontology)
884
-
885
- req = requests.post(self.server + "/v2/ontologies",
886
- headers={'Content-Type': 'application/json; charset=UTF-8',
887
- 'Authorization': 'Bearer ' + self.token},
888
- data=jsondata)
889
-
890
- self.on_api_error(req)
891
-
892
- res = req.json()
893
- # TODO: return also ontology name
894
- return {"onto_iri": res['@id'], "last_onto_date": res['knora-api:lastModificationDate']}
895
-
896
- def delete_ontology(self, onto_iri: str, last_onto_date=None):
897
- """
898
- A method to delete an ontology from /v2/ontologies
899
-
900
- :param onto_iri: The ontology to delete
901
- :param last_onto_date: the lastModificationDate of an ontology. None by default
902
- :return:
903
- """"" # TODO: add return documentation
904
- url = self.server + "/v2/ontologies/" + urllib.parse.quote_plus(onto_iri)
905
- req = requests.delete(url,
906
- params={"lastModificationDate": last_onto_date},
907
- headers={'Authorization': 'Bearer ' + self.token})
908
- self.on_api_error(req)
909
- res = req.json()
910
- return res
911
-
912
- def get_ontology_graph(self,
913
- shortcode: str,
914
- name: str):
915
- """
916
- Returns the turtle definition of the ontology.
917
-
918
- :param shortcode: Shortcode of the project
919
- :param name: Name of the ontology
920
- :return:
921
- """
922
- url = self.server + "/ontology/" + shortcode + "/" + name + "/v2"
923
- turtle = requests.get(url,
924
- headers={"Accept": "text/turtle",
925
- 'Authorization': 'Bearer ' + self.token})
926
- self.on_api_error(turtle)
927
- return turtle.text
928
-
929
- def create_res_class(self,
930
- onto_iri: str,
931
- onto_name: str,
932
- last_onto_date: str,
933
- class_name: str,
934
- super_class: List[str],
935
- labels: Dict[str, str],
936
- comments: Optional[Dict[str, str]] = None,
937
- permissions: Optional[Dict[str, str]] = None) -> Dict[str, str]:
938
- """Creates a knora resource class
939
-
940
- :param onto_iri: IRI of the ontology
941
- :param onto_name: Name of the ontology
942
- :param last_onto_date: Last modification date as returned by last call
943
- :param class_name: Name of the class to be created
944
- :param super_class: List of super classes
945
- :param labels: Dict with labels in the form { lang: labeltext }
946
- :param comments: Dict with comments in the form { lang: commenttext }
947
- :param permissions: Dict with permissions in the form
948
- :return: Dict with "class_iri" and "last_onto_date"
949
- """
950
-
951
- #
952
- # using map and iterable to get the proper format
953
- #
954
- labels = list(map(lambda p: {"@language": p[0], "@value": p[1]}, labels.items()))
955
-
956
- if not comments:
957
- comments = {"en": "none"}
958
-
959
- #
960
- # using map and iterable to get the proper format
961
- #
962
- comments = list(map(lambda p: {"@language": p[0], "@value": p[1]}, comments.items()))
963
-
964
- res_class = {
965
- "@id": onto_iri,
966
- "@type": "owl:Ontology",
967
- "knora-api:lastModificationDate": last_onto_date,
968
- "@graph": [{
969
- "@id": onto_name + ":" + class_name,
970
- "@type": "owl:Class",
971
- "rdfs:label": labels,
972
- "rdfs:comment": comments,
973
- "rdfs:subClassOf": {
974
- "@id": super_class
975
- }
976
- }],
977
- "@context": {
978
- "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
979
- "knora-api": "http://api.knora.org/ontology/knora-api/v2#",
980
- "owl": "http://www.w3.org/2002/07/owl#",
981
- "rdfs": "http://www.w3.org/2000/01/rdf-schema#",
982
- "xsd": "http://www.w3.org/2001/XMLSchema#",
983
- onto_name: onto_iri + "#"
984
- }
985
- }
986
-
987
- jsondata = json.dumps(res_class, indent=3, separators=(',', ': '))
988
-
989
- req = requests.post(self.server + "/v2/ontologies/classes",
990
- headers={'Content-Type': 'application/json; charset=UTF-8',
991
- 'Authorization': 'Bearer ' + self.token},
992
- data=jsondata)
993
- self.on_api_error(req)
994
-
995
- res = req.json()
996
- return {"class_iri": res['@graph'][0]['@id'], "last_onto_date": res['knora-api:lastModificationDate']}
997
-
998
- def create_property(
999
- self,
1000
- onto_iri: str,
1001
- onto_name: str,
1002
- last_onto_date: str,
1003
- prop_name: str,
1004
- super_props: List[str],
1005
- labels: Dict[str, str],
1006
- gui_element: str,
1007
- gui_attributes: List[str] = None,
1008
- subject: Optional[str] = None,
1009
- object: Optional[str] = None,
1010
- comments: Optional[Dict[str, str]] = None
1011
- ) -> Dict[str, str]:
1012
- """Create a Knora property
1013
-
1014
- :param onto_iri: IRI of the ontology
1015
- :param onto_name: Name of the Ontology (prefix)
1016
- :param last_onto_date: Last modification date as returned by last call
1017
- :param prop_name: Name of the property
1018
- :param super_props: List of super-properties
1019
- :param labels: Dict with labels in the form { lang: labeltext }
1020
- :param gui_element: Valid GUI-Element
1021
- :param gui_attributes: Valid GUI-Attributes (or None)
1022
- :param subject: Full name (prefix:name) of subject resource class
1023
- :param object: Full name (prefix:name) of object resource class
1024
- :param comments: Dict with comments in the form { lang: commenttext }
1025
- :return: Dict with "prop_iri" and "last_onto_date" keys
1026
- """
1027
- #
1028
- # using map and iterable to get the proper format
1029
- #
1030
- labels = list(map(lambda p: {"@language": p[0], "@value": p[1]}, labels.items()))
1031
-
1032
- if not comments:
1033
- comments = {"en": "none"}
1034
-
1035
- #
1036
- # using map and iterable to get the proper format
1037
- #
1038
- comments = list(map(lambda p: {"@language": p[0], "@value": p[1]}, comments.items()))
1039
-
1040
- additional_context = {}
1041
- for sprop in super_props:
1042
- pp = sprop.split(':')
1043
- if pp[0] != "knora-api":
1044
- additional_context[pp[0]] = self.prefixes[pp[0]]
1045
-
1046
- #
1047
- # using map and iterable to get the proper format
1048
- #
1049
- super_props = list(map(lambda x: {"@id": x}, super_props))
1050
- if len(super_props) == 1:
1051
- super_props = super_props[0]
1052
-
1053
- propdata = {
1054
- "@id": onto_name + ":" + prop_name,
1055
- "@type": "owl:ObjectProperty",
1056
- "rdfs:label": labels,
1057
- "rdfs:comment": comments,
1058
- "rdfs:subPropertyOf": super_props,
1059
- "salsah-gui:guiElement": {
1060
- "@id": gui_element
1061
- }
1062
- }
1063
- if subject:
1064
- propdata["knora-api:subjectType"] = {
1065
- "@id": subject
1066
- }
1067
-
1068
- if object:
1069
- propdata["knora-api:objectType"] = {
1070
- "@id": object
1071
- }
1072
-
1073
- if gui_attributes:
1074
- propdata["salsah-gui:guiAttribute"] = gui_attributes
1075
-
1076
- property = {
1077
- "@id": onto_iri,
1078
- "@type": "owl:Ontology",
1079
- "knora-api:lastModificationDate": last_onto_date,
1080
- "@graph": [
1081
- propdata
1082
- ],
1083
- "@context": {
1084
- "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
1085
- "knora-api": "http://api.knora.org/ontology/knora-api/v2#",
1086
- "salsah-gui": "http://api.knora.org/ontology/salsah-gui/v2#",
1087
- "owl": "http://www.w3.org/2002/07/owl#",
1088
- "rdfs": "http://www.w3.org/2000/01/rdf-schema#",
1089
- "xsd": "http://www.w3.org/2001/XMLSchema#",
1090
- onto_name: onto_iri + "#"
1091
- }
1092
- }
1093
- property["@context"].update(additional_context)
1094
- jsondata = json.dumps(property, indent=3, separators=(',', ': '))
1095
- req = requests.post(self.server + "/v2/ontologies/properties",
1096
- headers={'Content-Type': 'application/json; charset=UTF-8',
1097
- 'Authorization': 'Bearer ' + self.token},
1098
- data=jsondata)
1099
- self.on_api_error(req)
1100
-
1101
- res = req.json()
1102
- return {"prop_iri": res['@graph'][0]['@id'], "last_onto_date": res['knora-api:lastModificationDate']}
1103
-
1104
- def create_cardinality(
1105
- self,
1106
- onto_iri: str,
1107
- onto_name: str,
1108
- last_onto_date: str,
1109
- class_iri: str,
1110
- prop_iri: str,
1111
- occurrence: str,
1112
- gui_order: Optional[int] = None
1113
- ) -> Dict[str, str]:
1114
- """Add a property with a given cardinality to a class
1115
-
1116
- :param onto_iri: IRI of the ontology
1117
- :param onto_name: Name of the ontology (prefix)
1118
- :param last_onto_date: Last modification date as returned by last call
1119
- :param class_iri: IRI of the class to which the property will be added
1120
- :param prop_iri: IRI of the property that should be added
1121
- :param occurrence: Occurrence: "1", "0-1", "0-n" or "1-n"
1122
- :param gui_order: Ordering of properties in GUI
1123
- :return: Dict with "last_onto_date" key
1124
- """
1125
- switcher = {
1126
- "1": ("owl:cardinality", 1),
1127
- "0-1": ("owl:maxCardinality", 1),
1128
- "0-n": ("owl:minCardinality", 0),
1129
- "1-n": ("owl:minCardinality", 1)
1130
- }
1131
- occurrence = switcher.get(occurrence)
1132
- if not occurrence:
1133
- KnoraError("KNORA-ERROR:\n Invalid occurrence!")
1134
-
1135
- cardinality = {
1136
- "@id": onto_iri,
1137
- "@type": "owl:Ontology",
1138
- "knora-api:lastModificationDate": last_onto_date,
1139
- "@graph": [{
1140
- "@id": class_iri,
1141
- "@type": "owl:Class",
1142
- "rdfs:subClassOf": {
1143
- "@type": "owl:Restriction",
1144
- occurrence[0]: occurrence[1],
1145
- "owl:onProperty": {
1146
- "@id": prop_iri
1147
- }
1148
- }
1149
- }],
1150
- "@context": {
1151
- "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
1152
- "owl": "http://www.w3.org/2002/07/owl#",
1153
- "rdfs": "http://www.w3.org/2000/01/rdf-schema#",
1154
- "xsd": "http://www.w3.org/2001/XMLSchema#",
1155
- "knora-api": "http://api.knora.org/ontology/knora-api/v2#",
1156
- "salsah-gui": "http://api.knora.org/ontology/salsah-gui/v2#",
1157
- onto_name: onto_iri + "#"
1158
- }
1159
- }
1160
- if gui_order is not None:
1161
- cardinality['@graph'][0]["rdfs:subClassOf"]["salsah-gui:guiOrder"] = int(gui_order)
1162
-
1163
-
1164
-
1165
-
1166
- jsondata = json.dumps(cardinality, indent=3, separators=(',', ': '))
1167
-
1168
- req = requests.post(self.server + "/v2/ontologies/cardinalities",
1169
- headers={'Content-Type': 'application/ld+json; charset=UTF-8',
1170
- 'Authorization': 'Bearer ' + self.token},
1171
- data=jsondata)
1172
- self.on_api_error(req)
1173
-
1174
- res = req.json()
1175
-
1176
- return {"last_onto_date": res["knora-api:lastModificationDate"]}
1177
-
1178
- def create_list_node(self,
1179
- project_iri: str,
1180
- labels: Dict[str, str],
1181
- comments: Optional[Dict[str, str]] = None,
1182
- name: Optional[str] = None,
1183
- parent_iri: Optional[str] = None) -> str:
1184
- """
1185
- Creates a new list node. If there is no parent, a root node is created
1186
-
1187
- :param project_iri: IRI of the project
1188
- :param labels: Dict in the form {lang: label, …} giving the label(s)
1189
- :param comments: Dict in the form {lang: comment, …} giving the comment(s)
1190
- :param name: Name of the list node
1191
- :param parent_iri: None for root node (or omit), otherwise IRI of parent node
1192
- :return: IRI of list node
1193
- """
1194
-
1195
- #
1196
- # using map and iterable to get the proper format
1197
- #
1198
- labels = list(map(lambda p: {"language": p[0], "value": p[1]}, labels.items()))
1199
-
1200
- listnode = {
1201
- "projectIri": project_iri,
1202
- "labels": labels,
1203
- }
1204
-
1205
- #
1206
- # using map and iterable to get the proper format
1207
- #
1208
- if comments is not None:
1209
- listnode["comments"] = list(map(lambda p: {"language": p[0], "value": p[1]}, comments.items()))
1210
- else:
1211
- listnode["comments"] = []
1212
-
1213
- if name is not None:
1214
- listnode["name"] = name
1215
-
1216
- if parent_iri is not None:
1217
- listnode["parentNodeIri"] = parent_iri
1218
- url = self.server + "/admin/lists/" + quote_plus(parent_iri)
1219
- else:
1220
- url = self.server + "/admin/lists"
1221
-
1222
- jsondata = json.dumps(listnode, indent=3, separators=(',', ': '))
1223
-
1224
- req = requests.post(url,
1225
- headers={'Content-Type': 'application/json; charset=UTF-8',
1226
- 'Authorization': 'Bearer ' + self.token},
1227
- data=jsondata)
1228
- self.on_api_error(req)
1229
-
1230
- res = req.json()
1231
-
1232
- if parent_iri is not None:
1233
- return res['nodeinfo']['id']
1234
- else:
1235
- return res['list']['listinfo']['id']
1236
-
1237
- def get_lists(self, shortcode: str):
1238
- """
1239
- Get the lists belonging to a certain project identified by its shortcode
1240
- :param shortcode: Project shortcode
1241
-
1242
- :return: JSON with the lists
1243
- """
1244
- url = self.server + "/admin/lists?projectIri=" + quote_plus("http://rdfh.ch/projects/" + shortcode)
1245
- req = requests.get(url, headers={'Authorization': 'Bearer ' + self.token})
1246
- self.on_api_error(req)
1247
- return req.json()
1248
-
1249
- def get_complete_list(self, list_iri: str):
1250
- """
1251
- Get all the data (nodes) of a specific list
1252
-
1253
- :param list_iri: IRI of the list
1254
- :return: JSON containing the list info including all nodes
1255
- """
1256
- url = self.server + "/admin/lists/" + quote_plus(list_iri)
1257
- req = requests.get(url, headers={'Authorization': 'Bearer ' + self.token})
1258
- self.on_api_error(req)
1259
- return req.json()
1260
-
1261
- def get_resource_by_label(self,
1262
- label: str,
1263
- res_class: Optional[str] = None,
1264
- limit_to_project: Optional[str] = None,
1265
- offset: Optional[int] = None):
1266
- url = self.server + "/v2/searchbylabel/" + label
1267
- option = False
1268
- if res_class is not None:
1269
- url += '?limitToResourceClass=' + quote_plus(res_class)
1270
- option = True
1271
- if limit_to_project is not None:
1272
- if option:
1273
- url += '&limitToProject=' + quote_plus(limit_to_project)
1274
- else:
1275
- url += '?limitToProject=' + quote_plus(limit_to_project)
1276
- option = True;
1277
- if offset is not None:
1278
- if option:
1279
- url += '&offset=' + quote_plus(limit_to_project)
1280
- else:
1281
- url += '&offset=' + quote_plus(limit_to_project)
1282
- req = requests.get(url, headers={'Authorization': 'Bearer ' + self.token})
1283
- self.on_api_error(req)
1284
- return req.json()
1285
-
1286
- def create_resource(self,
1287
- schema: Dict,
1288
- res_class: str,
1289
- label: str,
1290
- values: Dict,
1291
- permissions: Optional[str] = None,
1292
- stillimage: Optional[str] = None):
1293
- """
1294
- This method creates a new resource (instance of a resource class) with the
1295
- default permissions.
1296
-
1297
- :param schema: The schema of the ontology as returned by the method "create_schema()"
1298
- :param res_class: The resource class of the resource to be created
1299
- :param label: The "rdfs:label" to be given to the new resource
1300
- :param values: A dict with the property values. It has the form
1301
- { property_name: value, property_name: value,… } or { property_name: [value1, value2,…],… }
1302
- The format of the values depends on the value types. E.g. a calendar date has the form
1303
- "GREGORIAN:CE:1920-03-12:CE:1921:05:21" where all values except the start year are optional.
1304
- :param stillimage: Path to a still image...
1305
- :return: A dict in the form { 'iri': resource_iri, 'ark': ark_id, 'vark': dated_ark_id }
1306
- """
1307
-
1308
- ontoname = schema["ontoname"]
1309
- props = schema['resources'][res_class] # this is an array of all properties defined in the ontology
1310
-
1311
- # we start building the dict that will be transformed into the JSON-LD
1312
- jsondata = {
1313
- '@type': ontoname + ":" + res_class,
1314
- 'rdfs:label': label,
1315
- "knora-api:attachedToProject": {
1316
- "@id": schema['proj_iri']
1317
- }
1318
- }
1319
-
1320
- if permissions is not None:
1321
- jsondata["knora-api:hasPermissions"] = permissions
1322
-
1323
- if stillimage is not None:
1324
- jsondata["knora-api:hasStillImageFileValue"] = {
1325
- "@type": "knora-api:StillImageFileValue",
1326
- "knora-api:fileValueHasFilename": stillimage
1327
- }
1328
-
1329
- def create_valdict(val):
1330
- """
1331
- Internal function to create the JSON-LD for one value
1332
- :param val: the value
1333
- :return: Dict propared for the JSON-LD for one value
1334
-
1335
- """
1336
-
1337
- if type(val) is dict:
1338
- pprint(val)
1339
- comment = val.get('comment')
1340
- permissions = val.get('permissions')
1341
- mapping = val.get('mapping')
1342
- val = val.get('value')
1343
- else:
1344
- comment = None
1345
- permissions = None
1346
- mapping = None
1347
-
1348
- valdict = {
1349
- '@type': 'knora-api:' + prop["otype"]
1350
- }
1351
-
1352
- if permissions is not None:
1353
- valdict["knora-api:hasPermissions"] = permissions
1354
-
1355
- if comment is not None:
1356
- valdict["knora-api:valueHasComment"] = comment
1357
-
1358
- if prop["otype"] == "TextValue":
1359
- if isinstance(val, KnoraStandoffXml): # text with XML markup
1360
- valdict['knora-api:textValueAsXml'] = val # no conversion to string
1361
- valdict['knora-api:textValueHasMapping'] = {
1362
- '@id': 'http://rdfh.ch/standoff/mappings/StandardMapping' if mapping is None else mapping
1363
- }
1364
- else: # normal text string without markup
1365
- valdict['knora-api:valueAsString'] = str(val)
1366
- elif prop["otype"] == "ColorValue":
1367
- #
1368
- # a color value as used in HTML (e.g. "#aaccff"
1369
- #
1370
- res = re.match('^#(?:[0-9a-fA-F]{3}){1,2}$', str(val))
1371
- if res is None:
1372
- raise KnoraError("Invalid ColorValue format! " + str(val))
1373
- valdict['knora-api:colorValueAsColor'] = str(val)
1374
- elif prop["otype"] == "DateValue":
1375
- #
1376
- # A knora date value
1377
- #
1378
- res = re.match(
1379
- '(GREGORIAN:|JULIAN:)?(CE:|BCE:)?(\d{4})?(-\d{1,2})?(-\d{1,2})?(:CE|:BCE)?(:\d{4})?(-\d{1,2})?(-\d{1,2})?',
1380
- str(val))
1381
- if res is None:
1382
- raise KnoraError("Invalid date format! " + str(val))
1383
- dp = res.groups()
1384
- calendar = 'GREGORIAN' if dp[0] is None else dp[0].strip('-: ')
1385
- e1 = 'CE' if dp[1] is None else dp[1].strip('-: ')
1386
- y1 = None if dp[2] is None else int(dp[2].strip('-: '))
1387
- m1 = None if dp[3] is None else int(dp[3].strip('-: '))
1388
- d1 = None if dp[4] is None else int(dp[4].strip('-: '))
1389
- e2 = 'CE' if dp[5] is None else dp[5].strip('-: ')
1390
- y2 = None if dp[6] is None else int(dp[6].strip('-: '))
1391
- m2 = None if dp[7] is None else int(dp[7].strip('-: '))
1392
- d2 = None if dp[8] is None else int(dp[8].strip('-: '))
1393
- if y1 is None:
1394
- raise KnoraError("Invalid date format! " + str(val))
1395
- if y2 is not None:
1396
- date1 = y1 * 10000
1397
- if m1 is not None:
1398
- date1 += m1 * 100
1399
- if d1 is not None:
1400
- date1 += d1
1401
- date2 = y2 * 10000
1402
- if m2 is not None:
1403
- date2 += m2 * 100
1404
- if d2 is not None:
1405
- date2 += d2
1406
- if date1 > date2:
1407
- y1, y2 = y2, y1
1408
- m1, m2 = m2, m1
1409
- d1, d2 = d2, d1
1410
- valdict["knora-api:dateValueHasCalendar"] = calendar
1411
- valdict["knora-api:dateValueHasStartEra"] = e1
1412
- valdict["knora-api:dateValueHasStartYear"] = int(y1)
1413
- if m1 is not None:
1414
- valdict["knora-api:dateValueHasStartMonth"] = int(m1)
1415
- if d1 is not None:
1416
- valdict["knora-api:dateValueHasStartDay"] = int(d1)
1417
- valdict["knora-api:dateValueHasEndEra"] = e2
1418
- if y2 is not None:
1419
- valdict["knora-api:dateValueHasEndYear"] = int(y2)
1420
- else:
1421
- valdict["knora-api:dateValueHasEndYear"] = int(y1)
1422
- if m2 is not None:
1423
- valdict["knora-api:dateValueHasEndMonth"] = int(m2)
1424
- if d2 is not None:
1425
- valdict["knora-api:dateValueHasEndDay"] = int(d2)
1426
- elif prop["otype"] == "DecimalValue":
1427
- #
1428
- # a decimal value
1429
- #
1430
- valdict['knora-api:decimalValueAsDecimal'] = {
1431
- '@type': 'xsd:decimal',
1432
- '@value': str(val)
1433
- }
1434
- elif prop["otype"] == "GeomValue":
1435
- #
1436
- # A geometry ID
1437
- #
1438
- valdict['knora-api:geometryValueAsGeometry'] = str(val)
1439
- elif prop["otype"] == "GeonameValue":
1440
- #
1441
- # A geoname ID
1442
- #
1443
- valdict['knora-api:geonameValueAsGeonameCode'] = str(val)
1444
- elif prop["otype"] == "IntValue":
1445
- #
1446
- # an integer value
1447
- #
1448
- valdict['knora-api:intValueAsInt'] = int(val)
1449
- elif prop["otype"] == "BooleanValue":
1450
- #
1451
- # a boolean value
1452
- #
1453
- if type(val) == bool:
1454
- valdict['knora-api:booleanValueAsBoolean'] = val
1455
- elif type(val) == str:
1456
- if val.upper() == 'TRUE':
1457
- valdict['knora-api:booleanValueAsBoolean'] = True
1458
- elif val.upper() == 'FALSE':
1459
- valdict['knora-api:booleanValueAsBoolean'] = False
1460
- else:
1461
- raise KnoraError("Invalid boolean format! " + str(val))
1462
- elif type(val) == int:
1463
- if val == 0:
1464
- valdict['knora-api:booleanValueAsBoolean'] = False
1465
- else:
1466
- valdict['knora-api:booleanValueAsBoolean'] = True
1467
- elif prop["otype"] == "UriValue":
1468
- #
1469
- # an URI
1470
- #
1471
- valdict['knora-api:uriValueAsUri'] = {
1472
- "@type": "xsd:anyURI",
1473
- "@value": str(val)
1474
- }
1475
- elif prop["otype"] == "TimeValue":
1476
- #
1477
- # an URI
1478
- #
1479
- valdict['knora-api:timeValueAsTime'] = {
1480
- "@type": "xsd:dateTime",
1481
- "@value": str(val)
1482
- }
1483
- elif prop["otype"] == "IntervalValue":
1484
- #
1485
- # an interval in the form "1.356:2.456"
1486
- #
1487
- iv = val.split(':')
1488
- valdict["knora-api:intervalValueHasEnd"] = {
1489
- "@type": "xsd:decimal",
1490
- "@value": str(iv[0])
1491
- }
1492
- valdict["knora-api:intervalValueHasStart"] = {
1493
- "@type": "xsd:decimal",
1494
- "@value": str(iv[1])
1495
- }
1496
- elif prop["otype"] == "ListValue":
1497
- try:
1498
- iriparts = parse(str(val), rule='IRI')
1499
- if iriparts['scheme'] == 'http' or iriparts['scheme'] == 'https':
1500
- valdict['knora-api:listValueAsListNode'] = {
1501
- '@id': str(val)
1502
- }
1503
- else:
1504
- if iriparts['authority'] is not None:
1505
- raise KnoraError("Invalid list node: \"" + str(val) + "\" !")
1506
- listname = iriparts['scheme']
1507
- nodename = iriparts['path']
1508
- for node in schema['lists'][listname]['nodes']:
1509
- found = False
1510
- if node['name'] == nodename:
1511
- valdict['knora-api:listValueAsListNode'] = {
1512
- '@id': node['id']
1513
- }
1514
- found = True
1515
- break
1516
- if not found:
1517
- raise KnoraError("Invalid list node: \"" + str(val) + "\" !")
1518
- except ValueError as err:
1519
- raise KnoraError("Invalid list node: \"" + str(val) + "\" !")
1520
-
1521
- elif prop["otype"] == "LinkValue":
1522
- valdict['@type'] = 'knora-api:LinkValue'
1523
- valdict['knora-api:linkValueHasTargetIri'] = {
1524
- '@id': str(val)
1525
- }
1526
- else:
1527
- if prop['otype'] in schema['link_otypes']:
1528
- valdict['@type'] = 'knora-api:LinkValue'
1529
- valdict['knora-api:linkValueHasTargetIri'] = {
1530
- '@id': str(val)
1531
- }
1532
- else:
1533
- raise KnoraError("Invalid otype: " + prop['otype'])
1534
-
1535
- return valdict
1536
-
1537
- for key, value in values.items():
1538
- prop = None
1539
- for tmpprop in props:
1540
- if tmpprop['propname'] == key:
1541
- prop = tmpprop
1542
- if prop is None:
1543
- raise KnoraError("Property " + key + " not known!")
1544
-
1545
- if prop['otype'] == "LinkValue" or prop['otype'] in schema['link_otypes']:
1546
- nkey = key + "Value"
1547
- else:
1548
- nkey = key
1549
- if type(value) is list:
1550
- valarr = []
1551
- for val in value:
1552
- valarr.append(create_valdict(val))
1553
- jsondata[ontoname + ':' + nkey] = valarr
1554
- else:
1555
- jsondata[ontoname + ':' + nkey] = create_valdict(value)
1556
-
1557
- jsondata['@context'] = {
1558
- "rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
1559
- "knora-api": "http://api.knora.org/ontology/knora-api/v2#",
1560
- "rdfs": "http://www.w3.org/2000/01/rdf-schema#",
1561
- "xsd": "http://www.w3.org/2001/XMLSchema#",
1562
- ontoname: schema['onto_iri'] + '#'
1563
- }
1564
-
1565
- jsonstr = json.dumps(jsondata, indent=3, separators=(',', ': '), cls=KnoraStandoffXmlEncoder)
1566
- print(jsonstr)
1567
- url = self.server + "/v2/resources"
1568
- req = requests.post(url,
1569
- headers={'Content-Type': 'application/json; charset=UTF-8',
1570
- 'Authorization': 'Bearer ' + self.token},
1571
- data=jsonstr)
1572
- self.on_api_error(req)
1573
-
1574
- res = req.json()
1575
-
1576
- return {
1577
- 'iri': res['@id'],
1578
- 'ark': res['knora-api:arkUrl']['@value'],
1579
- 'vark': res['knora-api:versionArkUrl']['@value']
1580
- }
1581
-
1582
- def list_creator(self, children: List):
1583
- """
1584
- internal Helper function
1585
-
1586
- :param children:
1587
- :return:
1588
- """
1589
- if len(children) == 0:
1590
- res = list(map(lambda a: {"name": a["name"], "id": a["id"]}, children))
1591
- else:
1592
- res = list(
1593
- map(lambda a: {"name": a["name"], "id": a["id"], "nodes": self.list_creator(a["children"])}, children))
1594
- return res
1595
-
1596
- def create_schema(self, shortcode: str, shortname: str):
1597
- """
1598
- This method extracts the ontology from the ontology information it gets from Knora. It
1599
- gets the ontology information as n3-data using the Knora API and concerts into a convenient
1600
- python dict that can be used for further processing. It is required by the bulk import processing
1601
- routines.
1602
-
1603
- :param shortcode: Shortcode of the project
1604
- :param shortname: Short name of the ontolopgy
1605
- :return: Dict with a simple description of the ontology
1606
- """
1607
- turtle = self.get_ontology_graph(shortcode, shortname)
1608
- # print(turtle)
1609
- g = Graph()
1610
- g.parse(format='n3', data=turtle)
1611
-
1612
- # Get project and ontology IRI's
1613
- sparql = """
1614
- SELECT ?onto ?proj
1615
- WHERE {
1616
- ?onto a owl:Ontology .
1617
- ?onto knora-api:attachedToProject ?proj
1618
- }
1619
- """
1620
- qres = g.query(sparql)
1621
- for row in qres:
1622
- proj_iri = row.proj.toPython() # project IRI
1623
- onto_iri = row.onto.toPython() # ontology IRI
1624
-
1625
- sparql = """
1626
- SELECT ?res ?prop ?superprop ?otype ?guiele ?attr ?card ?cardval
1627
- WHERE {
1628
- ?res a owl:Class .
1629
- ?res rdfs:subClassOf ?restriction .
1630
- ?restriction a owl:Restriction .
1631
- ?restriction owl:onProperty ?prop .
1632
- ?restriction ?card ?cardval .
1633
- ?prop a owl:ObjectProperty .
1634
- ?prop knora-api:objectType ?otype .
1635
- ?prop salsah-gui:guiElement ?guiele .
1636
- ?prop rdfs:subPropertyOf ?superprop .
1637
- OPTIONAL { ?prop salsah-gui:guiAttribute ?attr } .
1638
- FILTER(?card = owl:cardinality || ?card = owl:maxCardinality || ?card = owl:minCardinality)
1639
- }
1640
- ORDER BY ?res ?prop
1641
- """
1642
- qres = g.query(sparql)
1643
-
1644
- resources = {}
1645
- resclass = ''
1646
- propname = ''
1647
- link_otypes = []
1648
- propcnt = 0
1649
- propindex = {} # we have to keep the order of the properties as given in the ontology....
1650
- for row in qres:
1651
-
1652
- nresclass = row.res.toPython()
1653
- nresclass = nresclass[nresclass.find('#') + 1:]
1654
- if resclass != nresclass:
1655
- resclass = nresclass
1656
- resources[resclass] = []
1657
- propcnt = 0
1658
- superprop = row.superprop.toPython()
1659
- superprop = superprop[superprop.find('#') + 1:]
1660
- if superprop == 'hasLinkToValue': # we ignore this one....
1661
- continue
1662
- npropname = row.prop.toPython()
1663
- npropname = npropname[npropname.find('#') + 1:]
1664
- attr = row.attr.toPython() if row.attr is not None else None
1665
- if attr is not None:
1666
- attr = attr.split('=')
1667
- if propname == npropname:
1668
- propcnt -= 1
1669
-
1670
- # process attribute (there might be multiple attributes)
1671
- if attr is not None:
1672
- if resources[resclass][propcnt]["attr"] is not None: # TODO: why is this necessary???
1673
- resources[resclass][propcnt]["attr"][attr[0]] = attr[1].strip('<>')
1674
-
1675
- # process superprop (there might be multiple superprops)
1676
- if superprop not in resources[resclass][propcnt]["superprop"]:
1677
- resources[resclass][propcnt]["superprop"].append(superprop)
1678
- # pprint.pprint(resources[resclass])
1679
- propcnt += 1
1680
- continue
1681
- else:
1682
- propname = npropname
1683
- objtype = row.otype.toPython()
1684
- objtype = objtype[objtype.find('#') + 1:]
1685
- card = row.card.toPython()
1686
- card = card[card.find('#') + 1:]
1687
- guiele = row.guiele.toPython()
1688
- guiele = guiele[guiele.find('#') + 1:]
1689
- resources[resclass].append({
1690
- "propname": propname,
1691
- "otype": objtype,
1692
- "superprop": [superprop],
1693
- "guiele": guiele,
1694
- "attr": {attr[0]: attr[1].strip('<>')} if attr is not None else None,
1695
- "card": card,
1696
- "cardval": row.cardval.toPython()
1697
- })
1698
- # pprint.pprint(resources[resclass])
1699
- if superprop == "hasLinkTo":
1700
- link_otypes.append(objtype)
1701
- propindex[propname] = propcnt
1702
- propcnt += 1
1703
-
1704
- # Get info about lists attached to the project
1705
- listdata = {}
1706
- lists = self.get_lists(shortcode)
1707
- lists = lists["lists"]
1708
- for list in lists:
1709
- tmp = self.get_complete_list(list["id"])
1710
- clist = tmp["list"]
1711
- listdata[clist["listinfo"]["name"]] = {
1712
- "id": clist["listinfo"]["id"],
1713
- "nodes": self.list_creator(clist["children"])
1714
- }
1715
- schema = {
1716
- "proj_iri": proj_iri,
1717
- "shortcode": shortcode,
1718
- "ontoname": shortname,
1719
- "onto_iri": onto_iri,
1720
- "lists": listdata,
1721
- "resources": resources,
1722
- "link_otypes": link_otypes
1723
- }
1724
- return schema
1725
-
1726
- def reset_triplestore_content(self):
1727
- rdfdata = [
1728
- {
1729
- "path": "./knora-ontologies/knora-admin.ttl",
1730
- "name": "http://www.knora.org/ontology/knora-admin"
1731
- },
1732
- {
1733
- "path": "./knora-ontologies/knora-base.ttl",
1734
- "name": "http://www.knora.org/ontology/knora-base"
1735
- },
1736
- {
1737
- "path": "./knora-ontologies/standoff-onto.ttl",
1738
- "name": "http://www.knora.org/ontology/standoff"
1739
- },
1740
- {
1741
- "path": "./knora-ontologies/standoff-data.ttl",
1742
- "name": "http://www.knora.org/data/standoff"
1743
- },
1744
- {
1745
- "path": "./knora-ontologies/salsah-gui.ttl",
1746
- "name": "http://www.knora.org/ontology/salsah-gui"
1747
- },
1748
- {
1749
- "path": "./_test_data/all_data/admin-data.ttl",
1750
- "name": "http://www.knora.org/data/admin"
1751
- },
1752
- {
1753
- "path": "./_test_data/all_data/permissions-data.ttl",
1754
- "name": "http://www.knora.org/data/permissions"
1755
- },
1756
- {
1757
- "path": "./_test_data/all_data/system-data.ttl",
1758
- "name": "http://www.knora.org/data/0000/SystemProject"
1759
- }
1760
- ]
1761
- jsondata = json.dumps(rdfdata)
1762
- url = self.server + '/admin/store/ResetTriplestoreContent?prependdefaults=false'
1763
-
1764
- req = requests.post(url,
1765
- headers={'Content-Type': 'application/json; charset=UTF-8'},
1766
- data=jsondata)
1767
- self.on_api_error(req)
1768
- res = req.json()
1769
- # pprint(res)
1770
- return res
1771
-
1772
-
1773
- class Sipi:
1774
- def __init__(self, sipiserver: str, token: str):
1775
- self.sipiserver = sipiserver
1776
- self.token = token
1777
-
1778
- def on_api_error(self, res):
1779
- """
1780
- Method to check for any API errors
1781
- :param res: The input to check, usually JSON format
1782
- :return: Possible KnoraError that is being raised
1783
- """
1784
-
1785
- if (res.status_code != 200):
1786
- raise KnoraError("SIPI-ERROR: status code=" + str(res.status_code) + "\nMessage:" + res.text)
1787
-
1788
- if 'error' in res:
1789
- raise KnoraError("SIPI-ERROR: API error: " + res.error)
1790
-
1791
- def upload_image(self, filepath):
1792
- files = {
1793
- 'file': (filepath, open(filepath, 'rb')),
1794
- }
1795
- req = requests.post(self.sipiserver + "/upload?token=" + self.token,
1796
- files=files)
1797
- self.on_api_error(req)
1798
- res = req.json()
1799
- return res
1800
-
1801
-
1802
- class BulkImport:
1803
- def __init__(self, schema: Dict):
1804
- self.schema = schema
1805
- self.proj_prefix = 'p' + schema['shortcode'] + '-' + schema["ontoname"]
1806
- self.proj_iri = "http://api.knora.org/ontology/" + schema['shortcode'] + "/" + schema[
1807
- "ontoname"] + "/xml-import/v1#"
1808
- self.xml_prefixes = {
1809
- None: self.proj_iri,
1810
- "xsi": "http://www.w3.org/2001/XMLSchema-instance",
1811
- self.proj_prefix: self.proj_iri,
1812
- "knoraXmlImport": "http://api.knora.org/ontology/knoraXmlImport/v1#"
1813
- }
1814
- self.root = etree.Element('{http://api.knora.org/ontology/knoraXmlImport/v1#}resources',
1815
- nsmap=self.xml_prefixes)
1816
- self.project_shortcode = schema["shortcode"]
1817
-
1818
- def new_xml_element(self, tag: str, options: Dict = None, value: str = None):
1819
- tagp = tag.split(':')
1820
- if len(tagp) > 1:
1821
- fulltag = '{' + self.xml_prefixes.get(tagp[0]) + '}' + tagp[1]
1822
- else:
1823
- fulltag = tagp[0]
1824
- if options is None:
1825
- ele = etree.Element(fulltag)
1826
- else:
1827
- ele = etree.Element(fulltag, options)
1828
- if value is not None:
1829
- ele.text = value
1830
- return ele
1831
-
1832
- def write_xml(self, filename: str):
1833
- # print(etree.tostring(self.root, pretty_print=True, xml_declaration=True, encoding='utf-8'))
1834
- f = open(filename, "wb")
1835
- f.write(etree.tostring(self.root, pretty_print=True, xml_declaration=True, encoding='utf-8'))
1836
- f.close()
1837
-
1838
- def get_xml_string(self):
1839
- """
1840
- This method returns the Bulk-Import XML as an UTF-8 encoded string.
1841
- :return: UTF-8 encoded string.
1842
- """
1843
- string = etree.tostring(self.root, pretty_print=True, xml_declaration=True, encoding='utf-8')
1844
- return string
1845
-
1846
- def upload(self, user, password, hostname, port):
1847
- """
1848
- Upload the Bulk-Import XML to the server.
1849
- :param user: the email of the user
1850
- :param password: the password of the user
1851
- :param hostname: the hostname (e.g., localhost, api.example.org, etc.)
1852
- :param port: the port where the API is running (e.g., 3333)
1853
- :return: the JSON response
1854
- """
1855
- project_iri = "http://rdfh.ch/projects/" + self.project_shortcode
1856
- url_encoded_project_iri = urllib.parse.quote_plus(project_iri)
1857
- bulkimport_api_url = "http://" + hostname + ":" + port + "/v1/resources/xmlimport/" + url_encoded_project_iri
1858
- headers = {"Content-Type": "application/xml"}
1859
- r = requests.post(bulkimport_api_url, data=self.get_xml_string(), headers=headers, auth=(user, password))
1860
- return r.json()
1861
-
1862
- def add_resource(self, resclass: str, id: str, label: str, properties: Dict):
1863
- """
1864
-
1865
- :param resclass:
1866
- :param id:
1867
- :param label:
1868
- :param properties:
1869
- :return:
1870
- """
1871
-
1872
- def find_list_node_id(nodename: str, nodes: List):
1873
- """
1874
- Finds a list node ID from the nodename in a (eventually hierarchical) list of nodes
1875
-
1876
- :param nodename: Name of the node
1877
- :param nodes: List of nodes
1878
- :return: the id of the list node (an IRI)
1879
- """
1880
- for node in nodes:
1881
- if node["name"] == nodename:
1882
- return node["id"]
1883
- if node.get("nodes") is not None and len(node.get("nodes")) > 0:
1884
- node_id = find_list_node_id(nodename, node["nodes"])
1885
- if node_id is not None:
1886
- return node_id
1887
- return None
1888
-
1889
- def process_properties(propinfo: Dict, valuestr: any):
1890
- """
1891
- Processes a property in order to generate the approptiate XML for V1 bulk import.
1892
-
1893
- :param pname: property name
1894
- :param valuestr: value of the property
1895
- :return: Tuple with xml options and processed value: (xmlopt, val)
1896
- """
1897
- switcher = {
1898
- 'TextValue': {'knoraType': 'richtext_value'},
1899
- 'ColorValue': {'knoraType': 'color_value'},
1900
- 'DateValue': {'knoraType': 'date_value'},
1901
- 'DecimalValue': {'knoraType': 'decimal_value'},
1902
- 'GeomValue': {'knoraType': 'geom_value'},
1903
- 'GeonameValue': {'knoraType': 'geoname_value'},
1904
- 'IntValue': {'knoraType': 'int_value'},
1905
- 'BooleanValue': {'knoraType': 'boolean_value'},
1906
- 'UriValue': {'knoraType': 'uri_value'},
1907
- 'IntervalValue': {'knoraType': 'interval_value'},
1908
- 'ListValue': {'knoraType': 'hlist_value'},
1909
- 'LinkValue': {'knoraType': 'link_value'}
1910
- }
1911
- for link_otype in self.schema["link_otypes"]:
1912
- switcher[link_otype] = {'knoraType': 'link_value'}
1913
- xmlopt = switcher.get(propinfo["otype"])
1914
- if xmlopt is None:
1915
- raise KnoraError("Did not find " + propinfo["otype"] + " in switcher!")
1916
- if xmlopt['knoraType'] == 'link_value':
1917
- xmlopt['target'] = str(valuestr)
1918
- if validators.url(str(valuestr)):
1919
- xmlopt['linkType'] = 'iri'
1920
- else:
1921
- xmlopt['linkType'] = 'ref'
1922
- value = None
1923
- elif propinfo["otype"] == 'ListValue':
1924
- if validators.url(str(valuestr)):
1925
- # it's a full IRI identifying the node
1926
- value = valuestr
1927
- else:
1928
- # it's only a node name. First let's get the node list from the ontology schema
1929
- list_id = propinfo["attr"]["hlist"]
1930
- for listname in self.schema["lists"]:
1931
- if self.schema["lists"][listname]["id"] == list_id:
1932
- nodes = self.schema["lists"][listname]["nodes"]
1933
- value = find_list_node_id(str(valuestr), nodes)
1934
- # if value == 'http://rdfh.ch/lists/0808/X6bb-JerQyu5ULruCGEO0w':
1935
- # print("BANG!")
1936
- # exit(0)
1937
- elif propinfo["otype"] == 'DateValue':
1938
- # processing and validating date format
1939
- res = re.match('(GREGORIAN:|JULIAN:)?(\d{4})?(-\d{1,2})?(-\d{1,2})?(:\d{4})?(-\d{1,2})?(-\d{1,2})?',
1940
- str(valuestr))
1941
- if res is None:
1942
- raise KnoraError("Invalid date format! " + str(valuestr))
1943
- dp = res.groups()
1944
- calendar = 'GREGORIAN:' if dp[0] is None else dp[0]
1945
- y1 = None if dp[1] is None else int(dp[1].strip('-: '))
1946
- m1 = None if dp[2] is None else int(dp[2].strip('-: '))
1947
- d1 = None if dp[3] is None else int(dp[3].strip('-: '))
1948
- y2 = None if dp[4] is None else int(dp[4].strip('-: '))
1949
- m2 = None if dp[5] is None else int(dp[5].strip('-: '))
1950
- d2 = None if dp[6] is None else int(dp[6].strip('-: '))
1951
- if y1 is None:
1952
- raise KnoraError("Invalid date format! " + str(valuestr))
1953
- if y2 is not None:
1954
- date1 = y1 * 10000;
1955
- if m1 is not None:
1956
- date1 += m1 * 100
1957
- if d1 is not None:
1958
- date1 += d1
1959
- date2 = y2 * 10000;
1960
- if m2 is not None:
1961
- date2 += m2 * 100
1962
- if d1 is not None:
1963
- date2 += d2
1964
- if date1 > date2:
1965
- y1, y2 = y2, y1
1966
- m1, m2 = m2, m1
1967
- d1, d2 = d2, d1
1968
- value = calendar + str(y1)
1969
- if m1 is not None:
1970
- value += f'-{m1:02d}'
1971
- if d1 is not None:
1972
- value += f'-{d1:02d}'
1973
- if y2 is not None:
1974
- value += f':{y2:04d}'
1975
- if m2 is not None:
1976
- value += f'-{m2:02d}'
1977
- if d2 is not None:
1978
- value += f'-{d2:02d}'
1979
- else:
1980
- value = str(valuestr)
1981
- return xmlopt, value
1982
-
1983
- if self.schema["resources"].get(resclass) is None:
1984
- raise KnoraError('Resource class is not defined in ontology!')
1985
- resnode = self.new_xml_element(self.proj_prefix + ':' + resclass, {'id': str(id)})
1986
-
1987
- labelnode = self.new_xml_element('knoraXmlImport:label')
1988
- labelnode.text = str(label)
1989
- resnode.append(labelnode)
1990
-
1991
- for prop_info in self.schema["resources"][resclass]:
1992
- # first we check if the cardinality allows to add this property
1993
- if properties.get(prop_info["propname"]) is None: # this property-value is missing
1994
- if prop_info["card"] == 'cardinality' \
1995
- and prop_info["cardval"] == 1:
1996
- raise KnoraError(
1997
- resclass + " requires exactly one " + prop_info["propname"] + "-value: none supplied!")
1998
- if prop_info["card"] == 'minCardinality' \
1999
- and prop_info["cardval"] == 1:
2000
- raise KnoraError(
2001
- resclass + " requires at least one " + prop_info["propname"] + "-value: none supplied!")
2002
- continue
2003
- if type(properties[prop_info["propname"]]) is list:
2004
- if len(properties[prop_info["propname"]]) > 1:
2005
- if prop_info["card"] == 'maxCardinality' \
2006
- and prop_info["cardval"] == 1:
2007
- raise KnoraError(
2008
- resclass + " allows maximal one " + prop_info["propname"] + "-value: several supplied!")
2009
- if prop_info["card"] == 'cardinality' \
2010
- and prop_info["cardval"] == 1:
2011
- raise KnoraError(
2012
- resclass + " requires exactly one " + prop_info["propname"] + "-value: several supplied!")
2013
- for p in properties[prop_info["propname"]]:
2014
- xmlopt, value = process_properties(prop_info, p)
2015
- if xmlopt['knoraType'] == 'link_value':
2016
- pnode = self.new_xml_element(self.proj_prefix + ':' + prop_info["propname"])
2017
- pnode.append(self.new_xml_element(self.proj_prefix + ':' + prop_info["otype"], xmlopt, value))
2018
- else:
2019
- pnode = self.new_xml_element(self.proj_prefix + ':' + prop_info["propname"], xmlopt, value)
2020
- resnode.append(pnode)
2021
- else:
2022
- xmlopt, value = process_properties(prop_info, properties[prop_info["propname"]])
2023
- if xmlopt['knoraType'] == 'link_value':
2024
- pnode = self.new_xml_element(self.proj_prefix + ':' + prop_info["propname"])
2025
- pnode.append(self.new_xml_element(self.proj_prefix + ':' + prop_info["otype"], xmlopt, value))
2026
- else:
2027
- pnode = self.new_xml_element(self.proj_prefix + ':' + prop_info["propname"], xmlopt, value)
2028
- resnode.append(pnode)
2029
- self.root.append(resnode)
2030
-
2031
-
2032
- class IrisLookup:
2033
- def __init__(self, local_id_to_iri_json):
2034
- self.iris = local_id_to_iri_json
2035
-
2036
- def get_resource_iri(self, local_id):
2037
- """
2038
- Given the result of the bulk-import as json, allow retrieving the resource
2039
- IRI based on the local ID.
2040
- {'createdResources': [{'clientResourceID': 'LM_1',
2041
- 'label': '1',
2042
- 'resourceIri': 'http://rdfh.ch/0807/rNxoIK-oR_i0-lO21Y9-CQ'},
2043
- {'clientResourceID': 'LM_2']}
2044
-
2045
- :param local_id: the local id. resulting JSON from a bulk import upload.
2046
- :return:
2047
- """
2048
-
2049
- try:
2050
- resources = self.iris["createdResources"]
2051
- iri = ""
2052
- for resource in resources:
2053
- try:
2054
- res_id = resource["clientResourceID"]
2055
- if res_id == local_id:
2056
- iri = resource["resourceIri"]
2057
- else:
2058
- pass
2059
- except KeyError:
2060
- pass
2061
-
2062
- if iri == "":
2063
- return None
2064
- else:
2065
- return iri
2066
- except KeyError:
2067
- print("IrisLookup.get_resource_iri - 'createdResources' not found")
2068
-
2069
- def get_iris_json(self):
2070
- return self.iris
2071
-
2072
-
2073
- class ListsLookup:
2074
- def __init__(self, lists_json):
2075
- self.lists = lists_json
2076
-
2077
- def get_list_iri(self, listname):
2078
- return self.lists[listname]["id"]
2079
-
2080
- def get_list_node_iri(self, listname, nodename):
2081
- if nodename is not None:
2082
- nodes = self.lists[listname]["nodes"]
2083
- res = ""
2084
- for node in nodes:
2085
- try:
2086
- res = node[nodename]["id"]
2087
- except KeyError:
2088
- pass
2089
-
2090
- if res == "":
2091
- return None
2092
- else:
2093
- return res
2094
- else:
2095
- return None
2096
-
2097
- def get_lists_json(self):
2098
- return self.lists
2099
-
2100
-
2101
- if __name__ == '__main__':
2102
- con = Knora('http://localhost:3333')
2103
- con.login('root@example.com', 'test')
2104
- res = con.get_resource_by_label('Bertschy, Leon',
2105
- res_class="http://0.0.0.0:3333/ontology/0807/mls/v2#Lemma")
2106
- print('RES-IRI: ', res['@id'])
2107
- con.logout()
2108
-