From 19393aa8035864755ba81a2d36bfcb0dec96c569 Mon Sep 17 00:00:00 2001 From: Johannes Nussbaum <39048939+jnussbaum@users.noreply.github.com> Date: Tue, 6 Sep 2022 12:15:07 +0200 Subject: [PATCH] feat: add module csv2xml to convert tabular data to DSP-XML (DEV-134) (#219) --- Pipfile | 2 + Pipfile.lock | 152 +- dev-requirements.txt | 16 +- docs/assets/images/img-excel2xml.png | Bin 0 -> 110789 bytes .../templates/excel2xml_sample_data.csv | 10 + .../templates/excel2xml_sample_onto.json | 1526 +++++++++++++ .../templates/excel2xml_sample_script.py | 101 + docs/dsp-tools-excel.md | 39 +- docs/dsp-tools-excel2xml.md | 109 + docs/dsp-tools-usage.md | 30 + docs/index.md | 4 + knora/dsp_tools.py | 87 +- knora/dsplib/models/value.py | 17 +- knora/dsplib/schemas/data.xsd | 21 +- knora/dsplib/utils/excel_to_json_lists.py | 4 +- knora/excel2xml.py | 2002 +++++++++++++++++ mkdocs.yml | 1 + requirements.txt | 14 +- setup.py | 2 +- test/e2e/test_tools.py | 29 +- test/unittests/test_create_ontology.py | 1 - test/unittests/test_excel2xml.py | 594 +++++ test/unittests/test_excel_to_json_lists.py | 12 +- test/unittests/test_excel_to_properties.py | 14 +- test/unittests/test_excel_to_resource.py | 14 +- test/unittests/test_id_to_iri.py | 12 +- testdata/excel2xml-expected-output.xml | 117 + .../excel2xml-template-expected-output.xml | 264 +++ testdata/excel2xml-testdata.csv | 56 + testdata/excel2xml-testdata.xls | Bin 0 -> 54784 bytes testdata/excel2xml-testdata.xlsx | Bin 0 -> 43464 bytes testdata/test-data-systematic.xml | 38 +- testdata/test-project-systematic.json | 56 +- 33 files changed, 5185 insertions(+), 159 deletions(-) create mode 100644 docs/assets/images/img-excel2xml.png create mode 100644 docs/assets/templates/excel2xml_sample_data.csv create mode 100644 docs/assets/templates/excel2xml_sample_onto.json create mode 100644 docs/assets/templates/excel2xml_sample_script.py create mode 100644 docs/dsp-tools-excel2xml.md create mode 100644 knora/excel2xml.py create mode 100644 test/unittests/test_excel2xml.py create mode 100644 testdata/excel2xml-expected-output.xml create mode 100644 testdata/excel2xml-template-expected-output.xml create mode 100644 testdata/excel2xml-testdata.csv create mode 100644 testdata/excel2xml-testdata.xls create mode 100644 testdata/excel2xml-testdata.xlsx diff --git a/Pipfile b/Pipfile index 6e5842566..94b2998bf 100644 --- a/Pipfile +++ b/Pipfile @@ -18,6 +18,8 @@ openpyxl = "*" pyparsing = "==2.4.7" networkx = "*" pandas = "*" +xlrd = "*" +regex = "*" [dev-packages] mkdocs = "*" diff --git a/Pipfile.lock b/Pipfile.lock index c6f41514f..e6797871d 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "e5cec93e6913befc4ebfc9825babe72541f016aa05e3f88b6d87d9af03a662fc" + "sha256": "dba16a0708756d54af2eb8e061285afcc94535f8e8d073b744759e8bfd4834f5" }, "pipfile-spec": 6, "requires": { @@ -37,7 +37,7 @@ "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d", "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412" ], - "markers": "python_full_version >= '3.6.0'", + "markers": "python_version >= '3.6'", "version": "==2022.6.15" }, "charset-normalizer": { @@ -45,7 +45,7 @@ "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845", "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f" ], - "markers": "python_full_version >= '3.6.0'", + "markers": "python_version >= '3.6'", "version": "==2.1.1" }, "click": { @@ -69,7 +69,7 @@ "sha256:8eb9e2bc2f8c97e37a2dc85a09ecdcdec9d8a396530a6d5a33b30b9a92da0c5c", "sha256:a2ba85d1d6a74ef63837eed693bcb89c3f752169b0e3e7ae5b16ca5e1b3deada" ], - "markers": "python_full_version >= '3.6.0'", + "markers": "python_version >= '3.6'", "version": "==1.1.0" }, "idna": { @@ -182,11 +182,11 @@ }, "networkx": { "hashes": [ - "sha256:15a7b81a360791c458c55a417418ea136c13378cfdc06a2dcdc12bd2f9cf09c1", - "sha256:a762f4b385692d9c3a6f2912d058d76d29a827deaedf9e63ed14d397b8030687" + "sha256:2a30822761f34d56b9a370d96a4bf4827a535f5591a4078a453425caeba0c5bb", + "sha256:bd2b7730300860cbd2dafe8e5af89ff5c9a65c3975b352799d87a6238b4301a6" ], "index": "pypi", - "version": "==2.8.5" + "version": "==2.8.6" }, "numpy": { "hashes": [ @@ -330,6 +330,86 @@ "index": "pypi", "version": "==6.2.0" }, + "regex": { + "hashes": [ + "sha256:02b6dc102123f5178796dcdb5a90f6e88895607fd1a1d115d8de1af8161ca2b4", + "sha256:0843cc977b9cc00eb2299b624db6481d25e7f5b093f7a7c2bb727028d4a26eda", + "sha256:085ca3dc9360c0210e0a70e5d34d66454a06077644e7679fef6358b1f053e62e", + "sha256:0a9d5a64e974bc5f160f30f76aaf993d49eeddb405676be6bf76a5a2c131e185", + "sha256:0de0ce11c0835e1117eacbfe8fa6fa98dc0e8e746b486735cb0fdebe46a02222", + "sha256:1418d3506a9582b23a27373f125ea2b0da523c581e7cf678a6f036254d134faa", + "sha256:14750172c0a616140a8f496dfef28ed24080e87d06d5838e008f959ad307a8c5", + "sha256:1b6d2c579ffdcbb3d93f63b6a7f697364594e1c1b6856958b3e61e3ca22c140a", + "sha256:1df31eaf147ecff3665ba861acb8f78221cd5501df072c9151dfa341dd24599f", + "sha256:21b6f939916aa61beea56393ebc8a9999060632ac22b8193c2cb67d6fd7cb2c3", + "sha256:2240fce3af236e4586a045c1be8bbf16c4f8831e68b7df918b72fc31a80143be", + "sha256:242f546fc5e49bb7395624ac3b4fc168bf454e11ace9804c58c4c3a90d84e38f", + "sha256:25bffa248b99b53a61b1f20fc7d19f711e38e9f0bc90d44c26670f8dc282ad7d", + "sha256:2ada67e02fa3fcca9e3b90cf24c2c6bc77f0abc126209937956aea10eeba40c7", + "sha256:2c198921afc811bc0f105c6e5150fbdebf9520c9b7d43cfc0ab156ca97f506d7", + "sha256:370b1d7aed26e29915c3fb3e72e327f194824a76cedb60c0b9f6c6af53e89d72", + "sha256:3aafbbf5076f2a48bcf31ceb42b410323daaa0ddb42544640592957bc906ace6", + "sha256:3d3d769b3d485b28d6a591b46723dbacc696e6503f48a3ef52e6fc2c90edb482", + "sha256:3d83fd6dd4263595d0e4f595d4abd54397cbed52c0147f7dd148a7b72910301e", + "sha256:45cb798095b886e4df6ff4a1f7661eb70620ccdef127e3c3e00a1aaa22d30e53", + "sha256:4bd9443f7ff6e6288dd4496215c5d903f851e55cbc09d5963587af0c6d565a0a", + "sha256:4bdfd016ab12c4075ef93f025b3cf4c8962b9b7a5e52bb7039ab64cb7755930c", + "sha256:4c6554073e3e554fbb3dff88376ada3da32ca789ea1b9e381f684d49ddb61199", + "sha256:4dad9d68574e93e1e23be53b4ecfb0f083bd5cc08cc7f1984a4ee3ebf12aa446", + "sha256:4e12a3c2d4781ee5d03f229c940934fa1e4ea4f4995e68ab97a2815b139e0804", + "sha256:53c9eca0d6070a8a3de42182ad26daf90ba12132eb74a2f45702332762aff84e", + "sha256:5910bb355f9517309f77101238dbacb7151ede3434a2f1fad26ecc62f13d8324", + "sha256:5c77eab46f3a2b2cd8bbe06467df783543bf7396df431eb4a144cc4b89e9fb3c", + "sha256:5d541bc430a74c787684d1ebcd205a5212a88c3de73848143e77489b2c25b911", + "sha256:5e7c8f9f8824143c219dd93cdc733c20d2c12f154034c89bcb4911db8e45bd92", + "sha256:5f14430535645712f546f1e07013507d1cc0c8abd851811dacce8c7fb584bf52", + "sha256:6059ae91667932d256d9dc03abd3512ebcade322b3a42d1b8354bd1db7f66dcc", + "sha256:61f6966371fa1cbf26c6209771a02bef80336cdaca0c0af4dfa33d51019c0b93", + "sha256:62d56a9d3c1e5a83076db4da060dad7ea35ac2f3cbd3c53ba5a51fe0caedb500", + "sha256:634f090a388351eadf1dcc1d168a190718fb68efb4b8fdc1b119cf837ca01905", + "sha256:64ecfcc386420192fbe98fdde777d993f7f2dfec9552e4f4024d3447d3a3e637", + "sha256:6af38997f178889d417851bae8fb5c00448f7405cfcab38734d771f1dd5d5973", + "sha256:6b30c8d299ba48ee919064628fd8bc296bdc6e4827d315491bea39437130d3e1", + "sha256:6f0c8807bac16984901c0573725bad786f2f004f9bd5df8476c6431097b6c5b3", + "sha256:6f62c8a59f6b8e608880c61b138ae22668184bc266b025d33200dcf2cebe0872", + "sha256:74d4aabd612d32282f3cb3ebb4436046fb840d25c754157a755bc9f66e7cd307", + "sha256:7658d2dfc1dabfb008ffe12ae47b98559e2aedd8237bee12f5aafb74d90479e3", + "sha256:777ceea2860a48e9e362a4e2a9a691782ea97bd05c24627c92e876fdd2c22e61", + "sha256:79f34d5833cd0d53ecf48bc030e4da3216bd4846224d17eeb64509be5cb098fd", + "sha256:7a52d547259495a53e61e37ffc6d5cecf8d298aeb1bc0d9b25289d65ddb31183", + "sha256:840063aa8eeb1dda07d7d7dee15648838bffef1d415f5f79061854a182a429aa", + "sha256:8e8ec94d1b1a0a297c2c69a0bf000baf9a79607ca0c084f577f811a9b447c319", + "sha256:95fb62a3980cf43e76c2fe95edab06ec70dc495b8aa660975eb9f0b2ffdae1e1", + "sha256:9668da78bcc219542467f51c2cd01894222be6aceec4b5efb806705900b794d8", + "sha256:99a7c5786de9e92ff5ffee2e8bed745f5d25495206f3f14656c379031e518334", + "sha256:a1e283ad918df44bad3ccf042c2fe283c63d17617570eb91b8c370ef677b0b83", + "sha256:a25d251546acb5edb1635631c4ae0e330fa4ec7c6316c01d256728fbfb9bbff2", + "sha256:abe1adb32e2535aaa171e8b2b2d3f083f863c9974a3e6e7dae6bf4827fc8b983", + "sha256:ae85112da2d826b65aa7c7369c56ca41d9a89644312172979cbee5cf788e0b09", + "sha256:b3379a83dc63fe06538c751961f9ed730b5d7f08f96a57bbad8d52db5820df1f", + "sha256:b3c7c6c4aac19b964c1d12784aecae7f0315314640b0f41dd6f0d4e2bf439072", + "sha256:b7ddecc80e87acf12c2cf12bf3721def47188c403f04e706f104b5e71fed2f31", + "sha256:bbaf6785d3f1cd3e617b9d0fb3c5528023ef7bc7cc1356234801dc1941df8ce9", + "sha256:be6f5b453f7ed2219a9555bb6840663950b9ab1dc034216f68eac64db66633c2", + "sha256:c2b6404631b22617b5127c6de2355393ccda693ca733a098b6802e7dabb3457a", + "sha256:c4f6609f6e867a58cdf173e1cbe1f3736d25962108bd5cb01ad5a130875ff2c8", + "sha256:c76dd2c0615a28de21c97f9f6862e84faef58ff4d700196b4e395ef6a52291e4", + "sha256:c78c72f7878071a78337510ec78ab856d60b4bdcd3a95fd68b939e7cb30434b3", + "sha256:cb0c9a1476d279524538ba9a00ecec9eadcef31a6a60b2c8bd2f29f62044a559", + "sha256:ccb986e80674c929f198464bce55e995178dea26833421e2479ff04a6956afac", + "sha256:cfa62063c5eafb04e4435459ce15746b4ae6c14efeae8f16bd0e3d2895dad698", + "sha256:d13bd83284b46c304eb10de93f8a3f2c80361f91f4e8a4e1273caf83e16c4409", + "sha256:d76e585368388d99ddd2f95989e6ac80a8fe23115e93931faad99fa34550612f", + "sha256:dc32029b9cc784a529f9201289d4f841cc24a2ae3126a112cd467bc41bbc2f10", + "sha256:e0b55651db770b4b5a6c7d015f24d1a6ede307296bbdf0c47fc5f6a6adc7abee", + "sha256:e37886929ee83a5fa5c73164abada00e7f3cc1cbf3f8f6e1e8cfecae9d6cfc47", + "sha256:f7b88bc7306136b123fd1a9beed16ca02900ee31d1c36e73fa33d9e525a5562d", + "sha256:fac611bde2609a46fcbd92da7171286faa2f5c191f84d22f61cd7dc27213f51d", + "sha256:fafed60103132e74cdfbd651abe94801eb87a9765ce275b3dca9af8f3e06622a" + ], + "index": "pypi", + "version": "==2022.8.17" + }, "requests": { "hashes": [ "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983", @@ -348,11 +428,11 @@ }, "setuptools": { "hashes": [ - "sha256:7f4bc85450898a09f76ebf28b72fa25bc7111f6c7d665d514a60bba9c75ef2a9", - "sha256:a3ca5857c89f82f5c9410e8508cb32f4872a3bafd4aa7ae122a24ca33bccc750" + "sha256:2e24e0bec025f035a2e72cdd1961119f557d78ad331bb00ff82efb2ab8da8e82", + "sha256:7732871f4f7fa58fb6bdcaeadb0161b2bd046c85905dbaa066bdcbcc81953b57" ], "markers": "python_version >= '3.7'", - "version": "==65.2.0" + "version": "==65.3.0" }, "six": { "hashes": [ @@ -364,11 +444,11 @@ }, "urllib3": { "hashes": [ - "sha256:c33ccba33c819596124764c23a97d25f32b28433ba0dedeb77d873a38722c9bc", - "sha256:ea6e8fb210b19d950fab93b60c9009226c63a28808bc8386e05301e25883ac0a" + "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e", + "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' and python_version < '4'", - "version": "==1.26.11" + "version": "==1.26.12" }, "validators": { "hashes": [ @@ -376,6 +456,14 @@ ], "index": "pypi", "version": "==0.20.0" + }, + "xlrd": { + "hashes": [ + "sha256:6a33ee89877bd9abc1158129f6e94be74e2679636b8a205b43b85206c3f0bbdd", + "sha256:f72f148f54442c6b056bf931dbc34f986fd0c3b0b6b5a58d013c9aef274d0c88" + ], + "index": "pypi", + "version": "==2.0.1" } }, "develop": { @@ -413,7 +501,7 @@ "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d", "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412" ], - "markers": "python_full_version >= '3.6.0'", + "markers": "python_version >= '3.6'", "version": "==2022.6.15" }, "chardet": { @@ -429,7 +517,7 @@ "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845", "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f" ], - "markers": "python_full_version >= '3.6.0'", + "markers": "python_version >= '3.6'", "version": "==2.1.1" }, "click": { @@ -450,10 +538,10 @@ }, "distlib": { "hashes": [ - "sha256:a7f75737c70be3b25e2bee06288cec4e4c221de18455b2dd037fe2a795cab2fe", - "sha256:b710088c59f06338ca514800ad795a132da19fda270e3ce4affc74abf955a26c" + "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46", + "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e" ], - "version": "==0.3.5" + "version": "==0.3.6" }, "ghp-import": { "hashes": [ @@ -573,11 +661,11 @@ }, "mkdocs-material": { "hashes": [ - "sha256:319a6254819ce9d864ff79de48c43842fccfdebb43e4e6820eef75216f8cfb0a", - "sha256:92c70f94b2e1f8a05d9e05eec1c7af9dffc516802d69222329db89503c97b4f3" + "sha256:166287bb0e4197804906bf0389a852d5ced43182c30127ac8b48a4e497ecd7e5", + "sha256:704c64c3fff126a3923c2961d95f26b19be621342a6a4e49ed039f0bb7a5c540" ], "index": "pypi", - "version": "==8.4.1" + "version": "==8.4.2" }, "mkdocs-material-extensions": { "hashes": [ @@ -824,11 +912,11 @@ }, "setuptools": { "hashes": [ - "sha256:7f4bc85450898a09f76ebf28b72fa25bc7111f6c7d665d514a60bba9c75ef2a9", - "sha256:a3ca5857c89f82f5c9410e8508cb32f4872a3bafd4aa7ae122a24ca33bccc750" + "sha256:2e24e0bec025f035a2e72cdd1961119f557d78ad331bb00ff82efb2ab8da8e82", + "sha256:7732871f4f7fa58fb6bdcaeadb0161b2bd046c85905dbaa066bdcbcc81953b57" ], "markers": "python_version >= '3.7'", - "version": "==65.2.0" + "version": "==65.3.0" }, "six": { "hashes": [ @@ -851,7 +939,7 @@ "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" ], - "markers": "python_version < '3.11'", + "markers": "python_version >= '3.7'", "version": "==2.0.1" }, "tomlkit": { @@ -887,19 +975,19 @@ }, "urllib3": { "hashes": [ - "sha256:c33ccba33c819596124764c23a97d25f32b28433ba0dedeb77d873a38722c9bc", - "sha256:ea6e8fb210b19d950fab93b60c9009226c63a28808bc8386e05301e25883ac0a" + "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e", + "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' and python_version < '4'", - "version": "==1.26.11" + "version": "==1.26.12" }, "vistir": { "hashes": [ - "sha256:6506888420ce1842f70c314739ad9853eb5823d195b5bec4a16251d172a48fc4", - "sha256:66e56f31ede7181bef1406a394b07c7b4db9910698c8269d26a12815c8e3ccf0" + "sha256:1a89a612fb667c26ed6b4ed415b01e0261e13200a350c43d1990ace0ef44d35b", + "sha256:a8beb7643d07779cdda3941a08dad77d48de94883dbd3cb2b9b5ecb7eb7c0994" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==0.5.6" + "markers": "python_version not in '3.0, 3.1, 3.2, 3.3' and python_version >= '3.7'", + "version": "==0.6.1" }, "watchdog": { "hashes": [ diff --git a/dev-requirements.txt b/dev-requirements.txt index 29e5f36af..f0021afc7 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -3,12 +3,12 @@ attrs~=22.1.0; python_version >= '3.5' autopep8~=1.7.0 cached-property~=1.5.2 cerberus~=1.3.4 -certifi~=2022.6.15; python_full_version >= '3.6.0' +certifi~=2022.6.15; python_version >= '3.6' chardet~=5.0.0; python_version >= '3.6' -charset-normalizer~=2.1.1; python_full_version >= '3.6.0' +charset-normalizer~=2.1.1; python_version >= '3.6' click~=8.1.3 colorama~=0.4.5; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' -distlib~=0.3.5 +distlib~=0.3.6 ghp-import~=2.1.0 idna~=3.3; python_version >= '3.5' importlib-metadata~=4.12.0; python_version >= '3.7' @@ -19,7 +19,7 @@ markupsafe~=2.1.1; python_version >= '3.7' mergedeep~=1.3.4; python_version >= '3.6' mkdocs~=1.3.1 mkdocs-include-markdown-plugin~=3.6.1 -mkdocs-material~=8.4.1 +mkdocs-material~=8.4.2 mkdocs-material-extensions~=1.0.3; python_version >= '3.6' mypy~=0.971 mypy-extensions~=0.4.3 @@ -44,16 +44,16 @@ pyyaml~=6.0; python_version >= '3.6' pyyaml-env-tag~=0.1; python_version >= '3.6' requests~=2.28.1 requirementslib~=1.6.9; python_version >= '3.7' -setuptools~=65.2.0; python_version >= '3.7' +setuptools~=65.3.0; python_version >= '3.7' six~=1.16.0; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' toml~=0.10.2; python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3' -tomli~=2.0.1; python_version < '3.11' +tomli~=2.0.1; python_version >= '3.7' tomlkit~=0.11.4; python_version >= '3.6' and python_version < '4.0' types-requests~=2.28.9 types-urllib3~=1.26.23 typing-extensions~=4.3.0; python_version >= '3.7' -urllib3~=1.26.11; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' and python_version < '4' -vistir~=0.5.6; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' +urllib3~=1.26.12; python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' and python_version < '4' +vistir~=0.6.1; python_version not in '3.0, 3.1, 3.2, 3.3' and python_version >= '3.7' watchdog~=2.1.9; python_version >= '3.6' wheel~=0.37.1 zipp~=3.8.1; python_version >= '3.7' diff --git a/docs/assets/images/img-excel2xml.png b/docs/assets/images/img-excel2xml.png new file mode 100644 index 0000000000000000000000000000000000000000..45840ea30e28a8a683b4fce1f70b552c2e82a4e0 GIT binary patch literal 110789 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 a_%x=K^4kDaSCH Service Platform. Es soll einerseits die Möglichkeiten illustrieren, die die Plattform aktuell bietet, andererseits aber auch intern aufzeigen, wo noch Verbesserungsbedarf besteht.", + "fr": "Rosetta est le projet exemplaire de la DaSCH Service Platform. D'une part, il vise à illustrer les possibilités actuellement offertes par la plate-forme, mais d'autre part, il montre également en interne les domaines où il est encore possible d'apporter des améliorations.", + "en": "Rosetta is the sample project for the DaSCH Service Platform. On one hand, it is intended to illustrate the possibilities currently offered by the platform, but on the other hand, it also shows internally where there is still room for improvement." + }, + "keywords": [ + "Textquellen", + "Objekte", + "Bilder", + "Audio", + "Kyrillisch", + "Griechisch", + "Keilschrift", + "Hieroglyphen", + "Hebräisch", + "Arabisch", + "Japanisch", + "Sonderzeichen", + "XML", + "Markup", + "Annotation", + "Texteigenschaften", + "textual sources", + "objects", + "images", + "audio", + "Cyrillic", + "Greek", + "cuneiform", + "hieroglyphs", + "Hebrew", + "Arabic", + "Japanese", + "special characters", + "textual properties", + "sources", + "objets", + "Cyrillique", + "Grec", + "cunéiforme", + "hiéroglyphes", + "Hébreu", + "Arabe", + "Japonais", + "caractères spéciaux", + "propriétés de texte", + "Data and Service Center for the Humanities (DaSCH)" + ], + "lists": [ + { + "name": "flatlist", + "labels": { + "en": "Flat list" + }, + "comments": { + "en": "Flat list" + }, + "nodes": [ + { + "name": "first-node", + "labels": { + "en": "First node" + } + }, + { + "name": "second-node", + "labels": { + "en": "Second node" + } + } + ] + }, + { + "name": "category", + "labels": { + "de": "Kategorie", + "en": "Category", + "fr": "Catégorie" + }, + "comments": { + "en": "A list containing categories", + "de": "Die Liste enthält Kategorien" + }, + "nodes": [ + { + "name": "artwork", + "labels": { + "de": "Kunstwerk", + "en": "Artwork", + "fr": "Oeuvre d'art" + } + }, + { + "name": "vehicles", + "labels": { + "de": "Fahrzeuge", + "en": "Vehicles", + "fr": "Véhicules" + } + }, + { + "name": "nature", + "labels": { + "de": "Natur", + "en": "Nature", + "fr": "Nature" + }, + "nodes": [ + { + "name": "humans", + "labels": { + "de": "Menschen", + "en": "Humans", + "fr": "Humains" + } + }, + { + "name": "animals", + "labels": { + "de": "Tiere", + "en": "Animals", + "fr": "Animaux" + }, + "nodes": [ + { + "name": "mammals", + "labels": { + "de": "Säugetiere", + "en": "Mammals", + "fr": "Mammifères" + } + }, + { + "name": "insects", + "labels": { + "de": "Insekten", + "en": "Insects", + "fr": "Insectes" + } + }, + { + "name": "birds", + "labels": { + "de": "Vögel", + "en": "Birds", + "fr": "Oiseaux" + } + }, + { + "name": "amphibians", + "labels": { + "de": "Amphibien", + "en": "Amphibians", + "fr": "Amphibiens" + } + }, + { + "name": "reptiles", + "labels": { + "de": "Reptilien", + "en": "Reptiles", + "fr": "Reptiles" + } + } + ] + }, + { + "name": "plants", + "labels": { + "de": "Pflanzen", + "en": "Plants", + "fr": "Plantes" + } + }, + { + "name": "weather", + "labels": { + "de": "Wetter", + "en": "Weather", + "fr": "Météo" + } + }, + { + "name": "physics", + "labels": { + "de": "Physik", + "en": "Physics", + "fr": "Physique" + } + } + ] + } + ] + } + ], + "groups": [ + { + "name": "rosetta-editors", + "descriptions": { + "en": "Editors for the rosetta-project" + }, + "selfjoin": false, + "status": true + } + ], + "users": [ + { + "username": "rosettaedit", + "email": "rosettaedit@test.org", + "givenName": "rosetta-given", + "familyName": "rosetta-family", + "password": "rosetta1234", + "lang": "de", + "groups": [ + ":rosetta-editors" + ], + "projects": [ + ":admin" + ] + } + ], + "ontologies": [ + { + "name": "rosetta", + "label": "rosetta", + "properties": [ + { + "name": "hasTime", + "super": [ + "hasValue" + ], + "object": "TimeValue", + "labels": { + "de": "Zeit", + "en": "Time", + "fr": "Heure" + }, + "gui_element": "TimeStamp" + }, + { + "name": "hasTextMedium", + "super": [ + "hasLinkTo" + ], + "object": "StillImageRepresentation", + "labels": { + "de": "Textträger", + "en": "Text medium", + "fr": "Support de texte" + }, + "gui_element": "Searchbox", + "gui_attributes": { + "numprops": 1 + } + }, + { + "name": "hasImage", + "super": [ + "hasLinkTo" + ], + "object": ":Image2D", + "labels": { + "de": "Bild", + "en": "Image", + "fr": "Image" + }, + "gui_element": "Searchbox", + "gui_attributes": { + "numprops": 1 + } + }, + { + "name": "hasOriginalText", + "super": [ + "hasValue" + ], + "object": "TextValue", + "labels": { + "de": "Text", + "en": "Text", + "fr": "Texte" + }, + "gui_element": "Richtext" + }, + { + "name": "hasTranscription", + "super": [ + "hasValue" + ], + "object": "TextValue", + "labels": { + "de": "Transkription", + "en": "Transcription", + "fr": "Transcription" + }, + "gui_element": "Richtext" + }, + { + "name": "hasTransliteration", + "super": [ + "hasValue" + ], + "object": "TextValue", + "labels": { + "de": "Transliteration", + "en": "Transliteration", + "fr": "Translittération" + }, + "gui_element": "Richtext" + }, + { + "name": "hasTranslation", + "super": [ + "hasValue" + ], + "object": "TextValue", + "labels": { + "de": "Übersetzung", + "en": "Translation", + "fr": "Traduction" + }, + "gui_element": "Richtext" + }, + { + "name": "hasDescription", + "super": [ + "hasValue", + "dcterms:description" + ], + "object": "TextValue", + "labels": { + "de": "Beschreibung", + "en": "Description", + "fr": "Description" + }, + "gui_element": "Richtext" + }, + { + "name": "hasAuthor", + "super": [ + "hasLinkTo", + "dcterms:creator" + ], + "object": ":Person", + "labels": { + "de": "Autor", + "en": "Author", + "fr": "Auteur" + }, + "gui_element": "Searchbox", + "gui_attributes": { + "numprops": 1 + } + }, + { + "name": "hasName", + "super": [ + "hasValue" + ], + "object": "TextValue", + "labels": { + "de": "Name", + "en": "Name", + "fr": "Nom" + }, + "gui_element": "SimpleText", + "gui_attributes": { + "maxlength": 128, + "size": 32 + } + }, + { + "name": "hasTitle", + "super": [ + "hasValue", + "dcterms:title" + ], + "object": "TextValue", + "labels": { + "de": "Titel", + "en": "Title", + "fr": "Titre" + }, + "gui_element": "SimpleText", + "gui_attributes": { + "maxlength": 255, + "size": 80 + } + }, + { + "name": "hasDate", + "super": [ + "hasValue" + ], + "object": "DateValue", + "labels": { + "de": "Datierung", + "en": "Dating", + "fr": "Datation" + }, + "gui_element": "Date" + }, + { + "name": "hasFindspot", + "super": [ + "hasValue" + ], + "object": "GeonameValue", + "labels": { + "de": "Fundort", + "en": "Find spot", + "fr": "Gisement" + }, + "gui_element": "Geonames" + }, + { + "name": "hasLocation", + "super": [ + "hasValue" + ], + "object": "GeonameValue", + "labels": { + "de": "Ort", + "en": "Location", + "fr": "Lieu" + }, + "gui_element": "Geonames" + }, + { + "name": "hasBibliographicReference", + "super": [ + "hasValue" + ], + "object": "TextValue", + "labels": { + "de": "Literatur", + "en": "Literature", + "fr": "Littérature" + }, + "gui_element": "SimpleText", + "gui_attributes": { + "maxlength": 128, + "size": 128 + } + }, + { + "name": "hasExternalLink", + "super": [ + "hasValue" + ], + "object": "UriValue", + "labels": { + "de": "Externer Link", + "en": "External link", + "fr": "Lien hypertexte externe" + }, + "gui_element": "SimpleText", + "gui_attributes": { + "maxlength": 128, + "size": 128 + } + }, + { + "name": "hasIdentifier", + "super": [ + "hasValue" + ], + "object": "UriValue", + "labels": { + "de": "GND", + "en": "GND", + "fr": "GND" + }, + "gui_element": "SimpleText", + "gui_attributes": { + "maxlength": 128, + "size": 128 + } + }, + { + "name": "hasRelatedArtwork", + "super": [ + "hasLinkTo" + ], + "object": "Resource", + "labels": { + "de": "Verwandtes Werk", + "en": "Related Artwork", + "fr": "Oeuvre reliée" + }, + "gui_element": "Searchbox", + "gui_attributes": { + "numprops": 1 + } + }, + { + "name": "hasCreator", + "super": [ + "hasLinkTo", + "dcterms:creator" + ], + "object": ":Person", + "labels": { + "de": "Künstler", + "en": "Artist", + "fr": "Artiste" + }, + "gui_element": "Searchbox" + }, + { + "name": "inInstitution", + "super": [ + "hasLinkTo" + ], + "object": ":Institution", + "labels": { + "de": "Aufbewahrende Institution", + "en": "Custodian institution", + "fr": "Institution de garde" + }, + "gui_element": "Searchbox", + "gui_attributes": { + "numprops": 1 + } + }, + { + "name": "hasInventoryNumber", + "super": [ + "hasValue" + ], + "object": "TextValue", + "labels": { + "de": "Inventarnummer", + "en": "Inventory number", + "fr": "Numéro d'inventaire" + }, + "gui_element": "SimpleText", + "gui_attributes": { + "maxlength": 80, + "size": 25 + } + }, + { + "name": "hasPagenum", + "super": [ + "seqnum" + ], + "object": "IntValue", + "labels": { + "de": "Seitenzahl", + "en": "Page number", + "fr": "Numéro de page" + }, + "gui_element": "SimpleText", + "gui_attributes": { + "maxlength": 8, + "size": 8 + } + }, + { + "name": "partOf", + "super": [ + "isPartOf" + ], + "object": ":Book", + "labels": { + "de": "ist Teil von", + "en": "is part of", + "fr": "fait partie de" + }, + "gui_element": "Searchbox", + "gui_attributes": { + "numprops": 1 + } + }, + { + "name": "hasCategory", + "super": [ + "hasValue" + ], + "object": "ListValue", + "labels": { + "de": "Kategorie", + "en": "Category", + "fr": "Catégorie" + }, + "gui_element": "List", + "gui_attributes": { + "hlist": "category" + } + }, + { + "name": "hasFlatList", + "super": [ + "hasValue" + ], + "object": "ListValue", + "labels": { + "de": "Flatlist", + "en": "Flatlist", + "fr": "Flatlist" + }, + "gui_element": "Radio", + "gui_attributes": { + "hlist": "flatlist" + } + }, + { + "name": "hasColor", + "super": [ + "hasColor" + ], + "object": "ColorValue", + "labels": { + "de": "Farbe", + "en": "Colour", + "fr": "Couleur" + }, + "gui_element": "Colorpicker" + }, + { + "name": "hasCopyright", + "super": [ + "hasValue" + ], + "object": "TextValue", + "labels": { + "de": "Copyright", + "en": "Copyright", + "fr": "Droit d'auteur" + }, + "gui_element": "SimpleText", + "gui_attributes": { + "maxlength": 128, + "size": 64 + } + }, + { + "name": "isPublic", + "super": [ + "hasValue" + ], + "object": "BooleanValue", + "labels": { + "de": "Öffentlich", + "en": "Public", + "fr": "Public" + }, + "gui_element": "Checkbox" + }, + { + "name": "hasChildren", + "super": [ + "hasValue" + ], + "object": "IntValue", + "labels": { + "de": "Anzahl der Kinder", + "en": "Number of children", + "fr": "Nombre d'enfants" + }, + "gui_element": "Spinbox", + "gui_attributes": { + "max": 25.0, + "min": 0.0 + } + }, + { + "name": "hasWeight", + "super": [ + "hasValue" + ], + "object": "DecimalValue", + "labels": { + "de": "Gewicht", + "en": "Weight", + "fr": "Poids" + }, + "gui_element": "SimpleText", + "gui_attributes": { + "maxlength": 255, + "size": 80 + } + }, + { + "name": "hasFiles", + "super": [ + "hasLinkTo" + ], + "object": ":Document", + "labels": { + "de": "Modell-Dateien", + "en": "Files belonging to the model", + "fr": "Fichiers appartenants au modèle" + }, + "gui_element": "Searchbox" + }, + { + "name": "linksToRegion", + "super": [ + "hasLinkTo" + ], + "object": "Region", + "labels": { + "de": "verweist auf eine Region in einem Bild", + "en": "links to a region of an image", + "fr": "réfère à une région d'une image" + }, + "gui_element": "Searchbox" + }, + { + "name": "hasComment", + "super": [ + "hasComment" + ], + "object": "TextValue", + "labels": { + "de": "Kommentar", + "en": "Comment", + "fr": "Commentaire" + }, + "gui_element": "SimpleText" + }, + { + "name": "hasRepresentation", + "super": [ + "hasRepresentation" + ], + "object": "Representation", + "labels": { + "en": "Represented by" + }, + "gui_element": "Searchbox" + } + ], + "resources": [ + { + "name": "Text", + "labels": { + "de": "Text", + "en": "Text", + "fr": "Texte" + }, + "super": "Resource", + "cardinalities": [ + { + "propname": ":hasTextMedium", + "cardinality": "0-1", + "gui_order": 0 + }, + { + "propname": ":hasAuthor", + "cardinality": "0-1", + "gui_order": 1 + }, + { + "propname": ":hasTitle", + "cardinality": "0-1", + "gui_order": 2 + }, + { + "propname": ":hasOriginalText", + "cardinality": "0-1", + "gui_order": 3 + }, + { + "propname": ":hasTranscription", + "cardinality": "0-1", + "gui_order": 4 + }, + { + "propname": ":hasTransliteration", + "cardinality": "0-1", + "gui_order": 5 + }, + { + "propname": ":hasTranslation", + "cardinality": "0-n", + "gui_order": 6 + }, + { + "propname": ":hasFindspot", + "cardinality": "0-1", + "gui_order": 7 + }, + { + "propname": ":hasBibliographicReference", + "cardinality": "0-n", + "gui_order": 8 + }, + { + "propname": ":inInstitution", + "cardinality": "0-n", + "gui_order": 9 + }, + { + "propname": ":hasLocation", + "cardinality": "0-1", + "gui_order": 10 + }, + { + "propname": ":hasCopyright", + "cardinality": "0-1", + "gui_order": 11 + }, + { + "propname": ":hasExternalLink", + "cardinality": "0-n", + "gui_order": 12 + }, + { + "propname": ":hasRelatedArtwork", + "cardinality": "0-1", + "gui_order": 13 + } + ] + }, + { + "name": "Image2D", + "labels": { + "de": "2D-Bild", + "en": "2D image", + "fr": "image 2D" + }, + "super": "StillImageRepresentation", + "cardinalities": [ + { + "propname": ":hasTitle", + "cardinality": "1", + "gui_order": 0 + }, + { + "propname": ":hasCreator", + "cardinality": "0-1", + "gui_order": 1 + }, + { + "propname": ":hasDate", + "cardinality": "0-1", + "gui_order": 2 + }, + { + "propname": ":hasDescription", + "cardinality": "0-n", + "gui_order": 3 + }, + { + "propname": ":hasCopyright", + "cardinality": "0-1", + "gui_order": 4 + }, + { + "propname": ":inInstitution", + "cardinality": "0-n", + "gui_order": 5 + }, + { + "propname": ":hasLocation", + "cardinality": "0-1", + "gui_order": 6 + }, + { + "propname": ":hasExternalLink", + "cardinality": "0-n", + "gui_order": 7 + }, + { + "propname": ":linksToRegion", + "cardinality": "0-n", + "gui_order": 8 + } + ] + }, + { + "name": "ImagePartOfABook", + "labels": { + "de": "Bild in einem Buch", + "en": "Image in a book", + "fr": "Image dans un livre" + }, + "comments": { + "en": "Image that forms part of a book" + }, + "super": "StillImageRepresentation", + "cardinalities": [ + { + "propname": ":hasTitle", + "cardinality": "1" + }, + { + "propname": "seqnum", + "cardinality": "1" + }, + { + "propname": "isPartOf", + "cardinality": "1" + } + ] + }, + { + "name": "Image3D", + "labels": { + "de": "3D-Bild", + "en": "3D image", + "fr": "image 3D" + }, + "super": "DDDRepresentation", + "cardinalities": [ + { + "propname": ":hasTitle", + "cardinality": "1", + "gui_order": 0 + }, + { + "propname": ":hasCreator", + "cardinality": "0-1", + "gui_order": 1 + }, + { + "propname": ":hasDate", + "cardinality": "0-1", + "gui_order": 2 + }, + { + "propname": ":hasDescription", + "cardinality": "0-1", + "gui_order": 3 + }, + { + "propname": ":hasCopyright", + "cardinality": "0-1", + "gui_order": 4 + }, + { + "propname": ":hasFiles", + "cardinality": "0-1", + "gui_order": 5 + }, + { + "propname": ":inInstitution", + "cardinality": "0-n", + "gui_order": 6 + }, + { + "propname": ":hasLocation", + "cardinality": "0-1", + "gui_order": 7 + }, + { + "propname": ":hasExternalLink", + "cardinality": "0-n", + "gui_order": 8 + } + ] + }, + { + "name": "Audio", + "labels": { + "de": "Audio", + "en": "Audio", + "fr": "Audio" + }, + "super": "AudioRepresentation", + "cardinalities": [ + { + "propname": ":hasTitle", + "cardinality": "1", + "gui_order": 1 + }, + { + "propname": ":hasCreator", + "cardinality": "0-1", + "gui_order": 2 + }, + { + "propname": ":hasDate", + "cardinality": "0-1", + "gui_order": 3 + }, + { + "propname": ":hasDescription", + "cardinality": "0-1", + "gui_order": 4 + }, + { + "propname": ":hasCopyright", + "cardinality": "0-1", + "gui_order": 5 + }, + { + "propname": ":inInstitution", + "cardinality": "0-n", + "gui_order": 6 + }, + { + "propname": ":hasLocation", + "cardinality": "0-1", + "gui_order": 7 + }, + { + "propname": ":hasExternalLink", + "cardinality": "0-n", + "gui_order": 8 + } + ] + }, + { + "name": "Video", + "labels": { + "de": "Video", + "en": "Video", + "fr": "Video" + }, + "super": "MovingImageRepresentation", + "cardinalities": [ + { + "propname": ":hasTitle", + "cardinality": "1", + "gui_order": 1 + }, + { + "propname": ":hasCreator", + "cardinality": "0-1", + "gui_order": 2 + }, + { + "propname": ":hasDate", + "cardinality": "0-1", + "gui_order": 3 + }, + { + "propname": ":hasDescription", + "cardinality": "0-1", + "gui_order": 4 + }, + { + "propname": ":hasCopyright", + "cardinality": "0-1", + "gui_order": 5 + }, + { + "propname": ":inInstitution", + "cardinality": "0-n", + "gui_order": 6 + }, + { + "propname": ":hasLocation", + "cardinality": "0-1", + "gui_order": 7 + }, + { + "propname": ":hasExternalLink", + "cardinality": "0-n", + "gui_order": 8 + } + ] + }, + { + "name": "VideoSequence", + "labels": { + "de": "Sequenz einer Video-Ressource", + "en": "Sequence of a video resource" + }, + "super": "Resource", + "cardinalities": [ + { + "propname": ":hasTitle", + "cardinality": "1" + }, + { + "propname": "isSequenceOf", + "cardinality": "1" + }, + { + "propname": "hasSequenceBounds", + "cardinality": "1" + }, + { + "propname": ":hasCreator", + "cardinality": "0-1" + }, + { + "propname": ":hasDate", + "cardinality": "0-1" + }, + { + "propname": ":hasDescription", + "cardinality": "0-1" + } + ] + }, + { + "name": "AudioSequence", + "labels": { + "de": "Sequenz einer Audio-Ressource", + "en": "Sequence of an audio resource" + }, + "super": "Resource", + "cardinalities": [ + { + "propname": ":hasTitle", + "cardinality": "1" + }, + { + "propname": "isSequenceOf", + "cardinality": "1" + }, + { + "propname": "hasSequenceBounds", + "cardinality": "1" + }, + { + "propname": ":hasCreator", + "cardinality": "0-1" + }, + { + "propname": ":hasDate", + "cardinality": "0-1" + }, + { + "propname": ":hasDescription", + "cardinality": "0-1" + } + ] + }, + { + "name": "Book", + "labels": { + "de": "Buch / Manuskript", + "en": "book / manuscript", + "fr": "livre / manuscrit" + }, + "super": "Resource", + "cardinalities": [ + { + "propname": ":hasTitle", + "cardinality": "1-n", + "gui_order": 1 + }, + { + "propname": ":hasDescription", + "cardinality": "0-1", + "gui_order": 2 + }, + { + "propname": ":hasAuthor", + "cardinality": "0-n", + "gui_order": 3 + }, + { + "propname": ":hasDate", + "cardinality": "0-1", + "gui_order": 4 + }, + { + "propname": ":inInstitution", + "cardinality": "0-n", + "gui_order": 5 + }, + { + "propname": ":hasLocation", + "cardinality": "0-1", + "gui_order": 6 + }, + { + "propname": ":hasCopyright", + "cardinality": "0-1", + "gui_order": 7 + }, + { + "propname": ":hasExternalLink", + "cardinality": "0-n", + "gui_order": 8 + } + ] + }, + { + "name": "Page", + "super": "Resource", + "labels": { + "de": "Buchseite / Manuskriptseite", + "en": "Book page / Manuscript page", + "fr": "Page du livre / Page du manuscrit" + }, + "comments": { + "en": "A page is a part of a book or manuscript" + }, + "cardinalities": [ + { + "propname": ":hasTextMedium", + "cardinality": "0-1", + "gui_order": 0 + }, + { + "propname": ":hasPagenum", + "cardinality": "1", + "gui_order": 1 + }, + { + "propname": ":hasDescription", + "cardinality": "0-1", + "gui_order": 2 + }, + { + "propname": ":partOf", + "cardinality": "1", + "gui_order": 3 + }, + { + "propname": ":hasTranscription", + "cardinality": "0-1", + "gui_order": 4 + }, + { + "propname": ":hasTransliteration", + "cardinality": "0-1", + "gui_order": 5 + }, + { + "propname": ":hasTranslation", + "cardinality": "0-n", + "gui_order": 6 + }, + { + "propname": ":hasBibliographicReference", + "cardinality": "0-n", + "gui_order": 7 + } + ] + }, + { + "name": "Person", + "labels": { + "de": "Person", + "en": "Person", + "fr": "Personne" + }, + "super": "Resource", + "cardinalities": [ + { + "propname": ":hasName", + "cardinality": "1", + "gui_order": 1 + }, + { + "propname": ":hasIdentifier", + "cardinality": "0-1", + "gui_order": 2 + }, + { + "propname": ":hasChildren", + "cardinality": "0-1", + "gui_order": 3 + }, + { + "propname": ":hasExternalLink", + "cardinality": "0-n", + "gui_order": 4 + } + ] + }, + { + "name": "Institution", + "labels": { + "de": "Institution", + "en": "Institution", + "fr": "Institution" + }, + "super": "Resource", + "cardinalities": [ + { + "propname": ":hasName", + "cardinality": "1", + "gui_order": 1 + }, + { + "propname": ":hasLocation", + "cardinality": "1", + "gui_order": 2 + }, + { + "propname": ":hasIdentifier", + "cardinality": "0-1", + "gui_order": 3 + }, + { + "propname": ":hasExternalLink", + "cardinality": "0-n", + "gui_order": 4 + } + ] + }, + { + "name": "Object", + "labels": { + "de": "Objekt", + "en": "Object", + "fr": "Objet" + }, + "super": "Resource", + "cardinalities": [ + { + "propname": ":hasImage", + "cardinality": "0-n", + "gui_order": 0 + }, + { + "propname": ":hasCategory", + "cardinality": "0-n", + "gui_order": 1 + }, + { + "propname": ":hasFlatList", + "cardinality": "0-n", + "gui_order": 1 + }, + { + "propname": ":hasName", + "cardinality": "0-n", + "gui_order": 2 + }, + { + "propname": ":hasDescription", + "cardinality": "0-n", + "gui_order": 3 + }, + { + "propname": ":hasWeight", + "cardinality": "0-n", + "gui_order": 4 + }, + { + "propname": ":hasCreator", + "cardinality": "0-1", + "gui_order": 5 + }, + { + "propname": ":hasFindspot", + "cardinality": "0-n", + "gui_order": 6 + }, + { + "propname": ":hasDate", + "cardinality": "0-n", + "gui_order": 7 + }, + { + "propname": ":inInstitution", + "cardinality": "0-1", + "gui_order": 8 + }, + { + "propname": ":hasLocation", + "cardinality": "0-1", + "gui_order": 9 + }, + { + "propname": ":hasInventoryNumber", + "cardinality": "0-1", + "gui_order": 10 + }, + { + "propname": ":isPublic", + "cardinality": "0-1", + "gui_order": 11 + }, + { + "propname": ":hasColor", + "cardinality": "0-n", + "gui_order": 12 + }, + { + "propname": ":hasExternalLink", + "cardinality": "0-n", + "gui_order": 13 + }, + { + "propname": ":hasComment", + "cardinality": "0-n", + "gui_order": 14 + }, + { + "propname": ":hasTime", + "cardinality": "0-n", + "gui_order": 15 + } + ] + }, + { + "name": "Document", + "labels": { + "de": "Dokument", + "en": "Document", + "fr": "Document" + }, + "super": "DocumentRepresentation", + "cardinalities": [ + { + "propname": ":hasName", + "cardinality": "1", + "gui_order": 1 + }, + { + "propname": ":hasDescription", + "cardinality": "1", + "gui_order": 2 + }, + { + "propname": ":hasTime", + "cardinality": "0-1", + "gui_order": 3 + } + ] + }, + { + "name": "Archive", + "labels": { + "de": "Archiv", + "en": "Archive", + "fr": "Archive" + }, + "super": "ArchiveRepresentation", + "cardinalities": [ + { + "propname": ":hasName", + "cardinality": "1", + "gui_order": 1 + }, + { + "propname": ":hasDescription", + "cardinality": "1", + "gui_order": 2 + } + ] + }, + { + "name": "TextDocument", + "labels": { + "de": "Text-Dokument", + "en": "Text document", + "fr": "Document texte" + }, + "super": "TextRepresentation", + "cardinalities": [ + { + "propname": ":hasName", + "cardinality": "1", + "gui_order": 1 + }, + { + "propname": ":hasDescription", + "cardinality": "1", + "gui_order": 2 + } + ] + }, + { + "name": "ObjectWithDifferentRepresentations", + "labels": { + "de": "Objekt mit unterschiedlichen Repräsentationen", + "en": "Object with different representations" + }, + "comments": { + "en": "an object that can have representations of different types (audio, images,...)" + }, + "super": "Resource", + "cardinalities": [ + { + "propname": ":hasRepresentation", + "cardinality": "0-n", + "gui_order": 1 + } + ] + } + ] + }, + { + "name": "rosetta2", + "label": "rosetta2", + "properties": [ + { + "name": "hasLinkToOtherOnto", + "super": [ + "hasLinkTo" + ], + "object": "rosetta:Image2D", + "labels": { + "de": "Textträger", + "en": "Text medium", + "fr": "Support de texte" + }, + "gui_element": "Searchbox", + "gui_attributes": { + "numprops": 1 + } + } + ], + "resources": [ + { + "name": "TestResource", + "labels": { + "en": "TestResource" + }, + "super": "Resource", + "cardinalities": [ + { + "propname": ":hasLinkToOtherOnto", + "cardinality": "1", + "gui_order": 1 + } + ] + } + ] + } + ] + } +} diff --git a/docs/assets/templates/excel2xml_sample_script.py b/docs/assets/templates/excel2xml_sample_script.py new file mode 100644 index 000000000..25429c600 --- /dev/null +++ b/docs/assets/templates/excel2xml_sample_script.py @@ -0,0 +1,101 @@ +import pandas as pd +import warnings +from knora import excel2xml + +# general preparation +# ------------------- +path_to_json = "excel2xml_sample_onto.json" +main_df = pd.read_csv("excel2xml_sample_data.csv", dtype="str", sep=",") +# main_df = pd.read_excel("path-to-your-data-source", dtype="str") +# main_df.drop_duplicates(inplace = True) +# main_df.dropna(how = "all", inplace = True) +root = excel2xml.make_root(shortcode="0123", default_ontology="onto-name") +root = excel2xml.append_permissions(root) + +# create list mappings +# -------------------- +category_dict = excel2xml.create_json_list_mapping( + path_to_json=path_to_json, + list_name="category", + language_label="en" +) +category_dict_fallback = excel2xml.create_json_excel_list_mapping( + path_to_json=path_to_json, + list_name="category", + excel_values=main_df["Category"], + sep="," +) + +# create all resources +# -------------------- +for index, row in main_df.iterrows(): + resource = excel2xml.make_resource( + label=row["Resource name"], + restype=":MyResource", + id=excel2xml.make_xsd_id_compatible(row["Resource identifier"]) + ) + if excel2xml.check_notna(row["Image"]): + resource.append(excel2xml.make_bitstream_prop(row["Image"], permissions="prop-default")) + resource.append(excel2xml.make_text_prop(":name", row["Resource name"])) + resource.append(excel2xml.make_text_prop( + ":longtext", + excel2xml.PropertyElement(value=row["Long text"], permissions="prop-restricted", comment="long text", + encoding="xml") + )) + + # to get the correct category values, first split the cell, then look up the values in "category_dict", + # and if it's not there, look in "category_dict_fallback" + category_values = [category_dict.get(x.strip(), category_dict_fallback[x.strip()]) for x in + row["Category"].split(",")] + resource.append(excel2xml.make_list_prop("category", ":hasCategory", values=category_values)) + if excel2xml.check_notna(row["Complete?"]): + resource.append(excel2xml.make_boolean_prop(name=":isComplete", value=row["Complete?"])) + if excel2xml.check_notna(row["Color"]): + resource.append(excel2xml.make_color_prop(":colorprop", row["Color"])) + if pd.notna(row["Date discovered"]): + potential_date = excel2xml.find_date_in_string(row["Date discovered"]) + if potential_date: + resource.append(excel2xml.make_date_prop(":date", potential_date)) + else: + warnings.warn(f"Error in row {index + 2}: The column 'Date discovered' should contain a date, " + f"but no date was detected in the string '{row['Date discovered']}'") + if excel2xml.check_notna(row["Exact time"]): + resource.append(excel2xml.make_time_prop(":timeprop", row["Exact time"])) + if excel2xml.check_notna(row["Weight (kg)"]): + resource.append(excel2xml.make_decimal_prop(":weight", row["Weight (kg)"])) + if excel2xml.check_notna(row["Find location"]): + resource.append(excel2xml.make_geoname_prop(":location", row["Find location"])) + resource.append(excel2xml.make_integer_prop(":descendantsCount", row["Number of descendants"])) + if excel2xml.check_notna(row["Similar to"]): + resource.append(excel2xml.make_resptr_prop(":similarTo", row["Similar to"])) + if excel2xml.check_notna(row["See also"]): + resource.append(excel2xml.make_uri_prop(":url", row["See also"])) + + root.append(resource) + +# Annotation, Region, Link +# ------------------------ +annotation = excel2xml.make_annotation("Annotation of Resource 0", "annotation_of_res_0") +annotation.append(excel2xml.make_text_prop("hasComment", "This is a comment")) +annotation.append(excel2xml.make_resptr_prop("isAnnotationOf", "res_0")) +root.append(annotation) + +region = excel2xml.make_region("Region of Image 0", "region_of_image_0") +region.append(excel2xml.make_text_prop("hasComment", "This is a comment")) +region.append(excel2xml.make_color_prop("hasColor", "#5d1f1e")) +region.append(excel2xml.make_resptr_prop("isRegionOf", "image_0")) +region.append(excel2xml.make_geometry_prop( + "hasGeometry", + '{"type": "rectangle", "lineColor": "#ff3333", "lineWidth": 2, "points": [{"x": 0.08, "y": 0.16}, {"x": 0.73, ' + '"y": 0.72}], "original_index": 0}' +)) +root.append(region) + +link = excel2xml.make_link("Link between Resource 0 and 1", "link_res_0_res_1") +link.append(excel2xml.make_text_prop("hasComment", "This is a comment")) +link.append(excel2xml.make_resptr_prop("hasLinkTo", values=["res_0", "res_1"])) +root.append(link) + +# write file +# ---------- +excel2xml.write_xml(root, "data.xml") diff --git a/docs/dsp-tools-excel.md b/docs/dsp-tools-excel.md index 0e1b08dd8..526088de5 100644 --- a/docs/dsp-tools-excel.md +++ b/docs/dsp-tools-excel.md @@ -83,13 +83,6 @@ For further information about properties, see [here](./dsp-tools-create-ontologi -## Create a DSP-conform XML file from an Excel file - -[not yet implemented] - - - - ## Create a list from one or several Excel files With dsp-tools, a JSON list can be created from one or several Excel files. The command for this is documented @@ -170,3 +163,35 @@ the `name`. After the creation of the list, a validation against the JSON schema for lists is performed. An error message is printed out if the list is not valid. Furthermore, it is checked that no two nodes are the same. + + + + +## Create a DSP-conform XML file from an Excel/CSV file + +There are two use cases for a transformation from Excel/CSV to XML: + + - The CLI command `dsp-tools excel2xml` creates an XML file from an Excel/CSV file which is already structured + according to the DSP specifications. This is mostly used for DaSCH-interal data migration. + - The module `excel2xml` can be imported into a custom Python script that transforms any tabular data into an XML. This + use case is more frequent, because data from research projects have a variety of formats/structures. The module + `excel2xml` is documented [here](./dsp-tools-excel2xml.md). + + +### CLI command `excel2xml` + +The command line tool is used as follows: +```bash +dsp-tools excel2xml data-source.xlsx 1234 shortname +``` + +There are no flags/options for this command. + +The Excel file must be structured as in this image: +![img-excel2xml.png](assets/images/img-excel2xml.png) + +Some notes: + + - The special tags ``, ``, and `` are represented as resources of restype `Annotation`, +`LinkObj`, and `Region`. + - The columns "ark" and "iri" are only used for DaSCH-internal data migration. diff --git a/docs/dsp-tools-excel2xml.md b/docs/dsp-tools-excel2xml.md new file mode 100644 index 000000000..8f86e73db --- /dev/null +++ b/docs/dsp-tools-excel2xml.md @@ -0,0 +1,109 @@ +[![PyPI version](https://badge.fury.io/py/dsp-tools.svg)](https://badge.fury.io/py/dsp-tools) + +# `excel2xml`: Convert a data source to XML +dsp-tools assists you in converting a data source in CSV/XLS(X) format to an XML file. There are two use cases for a +transformation from Excel/CSV to XML: + + - The CLI command `dsp-tools excel2xml` creates an XML file from an Excel/CSV file which is already structured + according to the DSP specifications. This is mostly used for DaSCH-interal data migration. The CLI command is + documented [here](dsp-tools-excel.md#cli-command-excel2xml). + - The module `excel2xml` can be imported into a custom Python script that transforms any tabular data into an XML. This + use case is more frequent, because data from research projects have a variety of formats/structures. **This document + only treats the `excel2xml` module.** + + +## How to use the module excel2xml +At the end of this document, you find a sample Python script. In the following, it is explained how to use it. + + +### General preparation +Insert your ontology name, project shortcode, and the path to your data source. If necessary, activate one of the lines +that are commented out. +Then, the `root` element is created, which represents the `` tag of the XML document. As first children of +``, some standard permissions are added. At the end, please carefully check the permissions of the finished XML +file if they meet your requirements, and adapt them if necessary. +The standard permission of a resource is "res-default", and of a property "prop-default". If you don't specify it +otherwise, all resources and properties get these permissions. With excel2xml, it is not possible to create resources/ +properties that don't have permissions, because they would be invisible for all users except project admins and system +admins. Read more about permissions [here](./dsp-tools-xmlupload.md#how-to-use-the-permissions-attribute-in-resourcesproperties). + + +### Create list mappings +Let's assume that your data source has a column containing list values named after the "label" of the JSON project list, +instead of the "name" which is needed for the `dsp-tools xmlupload`. You need a way to get the names from the labels. +If your data source uses the labels correctly, this is an easy task: The method `create_json_list_mapping()` creates a +dictionary that maps the labels to the names. +If, however, your data source has spelling variants, you need the more sophisticated approach of +`create_json_excel_list_mapping()`: This method creates a dictionary that maps the list values in your data source to their +correct JSON project node name. This happens based on string similarity. Please carefully check the result if there are +no false matches! + + +### Create all resources +With the help of the [Python pandas library](https://pandas.pydata.org/), you can then iterate through the rows of your +Excel/CSV, and create resources and properties. Some examples of useful helper methods are: + + +#### Create an ID for a resource +The method `make_xsd_id_compatible(string)` makes a string compatible with the constraints of xsd:ID, so that it can be +used as ID of a resource. + + +#### Create a property +For every property, there is a helper function that explains itself when you hover over it. It also has a link to +the dsp-tools documentation of this property. So you don't need to worry how to construct a certain XML value for a +certain property. + +For `make_boolean_prop(cell)`, the following formats are supported: + + - true: True, "true", "True", "1", 1, "yes", "Yes" + - false: False, "false", "False", "0", 0, "no", "No" + + +#### Check if a cell contains a usable value +The method `check_notna(cell)` checks a value if it is usable in the context of data archiving. A value is considered +usable if it is + - a number (integer or float, but not np.nan) + - a boolean + - a string with at least one Unicode letter, underscore, or number, but not "None", "", "N/A", or "-" + - a PropertyElement whose "value" fulfills the above criteria + + +#### Calendar date parsing +The method `find_date_in_string(string)` tries to find a calendar date in a string. If successful, it +returns the DSP-formatted date string. + +Notes: + + - The date can be embedded in text. + - Only the first date found is returned. + - By default, dates are interpreted as CE (Christian era) in the Gregorian calendar. + - The years 0000-2999 are supported, in 4-digit form. + - Dates written with slashes are always interpreted in a European manner: 5/11/2021 is the 5th of November. + +Currently supported date formats: + +| Input | Output | +|-------------------|---------------------------------------| +| 0476_09_04 | GREGORIAN:CE:0476-09-04:CE:0476-09-04 | +| 0476-09-04 | GREGORIAN:CE:0476-09-04:CE:0476-09-04 | +| 30.4.2021 | GREGORIAN:CE:2021-04-30:CE:2021-04-30 | +| 5/11/2021 | GREGORIAN:CE:2021-11-05:CE:2021-11-05 | +| Jan 26, 1993 | GREGORIAN:CE:1993-01-26:CE:1993-01-26 | +| February26,2051 | GREGORIAN:CE:2051-02-26:CE:2051-02-26 | +| 28.2.-1.12.1515 | GREGORIAN:CE:1515-02-28:CE:1515-12-01 | +| 25.-26.2.0800 | GREGORIAN:CE:0800-02-25:CE:0800-02-26 | +| 1.9.2022-3.1.2024 | GREGORIAN:CE:2022-09-01:CE:2024-01-03 | +| 1848 | GREGORIAN:CE:1848:CE:1848 | +| 1849/1850 | GREGORIAN:CE:1849:CE:1850 | +| 1849/50 | GREGORIAN:CE:1849:CE:1850 | +| 1845-50 | GREGORIAN:CE:1845:CE:1850 | + + +## Complete example +Save the following files into a directory, and run the Python script. The features discussed in this document are +contained therein. + + - sample data: [excel2xml_sample_data.csv](assets/templates/excel2xml_sample_data.csv) + - sample ontology: [excel2xml_sample_onto.json](assets/templates/excel2xml_sample_onto.json) + - sample script: [excel2xml_sample_script.py](assets/templates/excel2xml_sample_script.py) diff --git a/docs/dsp-tools-usage.md b/docs/dsp-tools-usage.md index 1e64c519e..c3df150b4 100644 --- a/docs/dsp-tools-usage.md +++ b/docs/dsp-tools-usage.md @@ -194,6 +194,36 @@ More information about the usage of this command can be found +## Create an XML file from Excel/CSV +```bash +dsp-tools excel2xml data-source.xlsx project_shortcode ontology_name +``` + +Arguments: + + - data-source.xlsx: An Excel/CSV file that is structured according to [these requirements](dsp-tools-excel.md#cli-command-excel2xml) + - project_shortcode: The four-digit hexadecimal shortcode of the project + - ontology_name: the name of the ontology that the data belongs to + +If your data source is already structured according to the DSP specifications, but it is not in XML format yet, the +command `excel2xml` will transform it into XML. This is mostly used for DaSCH-interal data migration. There are no +flags/options for this command. The details of this command are documented [here](dsp-tools-excel.md#cli-command-excel2xml). + +If your data source is not yet structured according to the DSP specifications, you need a custom Python script for the +data transformation. For this, you might want to import the module `excel2xml` into your Python script, which is +described in the next paragraph. + + + +## Use the module `excel2xml` to convert a data source to XML +dsp-tools assists you in converting a data source in CSV/XLS(X) format to an XML file. Unlike the other features of +dsp-tools, this doesn't work via command line, but via helper methods that you can import into your own Python script. +Because every data source is different, there is no single algorithm to convert them to a DSP conform XML. Every user +has to deal with the specialties of his/her data source, but `excel2xml`'s helper methods can help a lot. Read more +about it [here](./dsp-tools-excel2xml.md). + + + ## Replace internal IDs with IRIs in XML file diff --git a/docs/index.md b/docs/index.md index d4aacc24d..06349b6bd 100644 --- a/docs/index.md +++ b/docs/index.md @@ -32,4 +32,8 @@ dsp-tools helps you with the following tasks: - [`dsp-tools id2iri`](./dsp-tools-usage.md#replace-internal-ids-with-iris-in-xml-file) takes an XML file for bulk data import and replaces referenced internal IDs with IRIs. The mapping has to be provided with a JSON file. +- [`dsp-tools excel2xml`](./dsp-tools-usage.md#create-an-xml-file-from-excelcsv) transforms a data source that is + already structured according to the DSP specifications to XML. +- [The module excel2xml](./dsp-tools-usage.md#use-the-module-excel2xml-to-convert-a-data-source-to-xml) provides helper + methods that can be used in a Python script to convert data from a tabular format into XML. diff --git a/knora/dsp_tools.py b/knora/dsp_tools.py index 0c74db520..c7cce225a 100644 --- a/knora/dsp_tools.py +++ b/knora/dsp_tools.py @@ -15,6 +15,7 @@ from knora.dsplib.utils.onto_get import get_ontology from knora.dsplib.utils.onto_validate import validate_project from knora.dsplib.utils.xml_upload import xml_upload +from knora.excel2xml import excel2xml def program(user_args: list[str]) -> None: @@ -37,34 +38,31 @@ def program(user_args: list[str]) -> None: default_localhost = 'http://0.0.0.0:3333' default_user = 'root@example.com' default_pw = 'test' - dsp_tools_version = version('dsp-tools') now = datetime.datetime.now() # parse the arguments of the command line - parser = argparse.ArgumentParser( - description=f'dsp-tools (Version {dsp_tools_version}) DaSCH Service Platform data modelling tools (© {now.year} by DaSCH).') - - subparsers = parser.add_subparsers(title='Subcommands', description='Valid subcommands are', - help='sub-command help') + parser = argparse.ArgumentParser(description=f'dsp-tools (Version {dsp_tools_version}) DaSCH Service Platform data ' + f'modelling tools (© {now.year} by DaSCH).') + subparsers = parser.add_subparsers(title='Subcommands', description='Valid subcommands are', help='sub-command help') - parser_create = subparsers.add_parser('create', - help='Upload an ontology and/or list(s) from a JSON file to the DaSCH ' - 'Service Platform') + # create + parser_create = subparsers.add_parser('create', help='Upload an ontology and/or list(s) from a JSON file to the ' + 'DaSCH Service Platform') parser_create.set_defaults(action='create') parser_create.add_argument('-s', '--server', type=str, default=default_localhost, help=url_text) parser_create.add_argument('-u', '--user', default=default_user, help=username_text) parser_create.add_argument('-p', '--password', default=default_pw, help=password_text) - parser_create.add_argument('-V', '--validate-only', action='store_true', - help='Do only validation of JSON, no upload of the ' - 'ontology') + parser_create.add_argument('-V', '--validate-only', action='store_true', help='Do only validation of JSON, no ' + 'upload of the ontology') parser_create.add_argument('-l', '--lists-only', action='store_true', help='Upload only the list(s)') parser_create.add_argument('-v', '--verbose', action='store_true', help=verbose_text) parser_create.add_argument('-d', '--dump', action='store_true', help='dump test files for DSP-API requests') parser_create.add_argument('datamodelfile', help='path to data model file') - parser_get = subparsers.add_parser('get', - help='Get the ontology (data model) of a project from the DaSCH Service Platform.') + # get + parser_get = subparsers.add_parser('get', help='Get the ontology (data model) of a project from the DaSCH Service ' + 'Platform.') parser_get.set_defaults(action='get') parser_get.add_argument('-u', '--user', default=default_user, help=username_text) parser_get.add_argument('-p', '--password', default=default_pw, help=password_text) @@ -74,8 +72,8 @@ def program(user_args: list[str]) -> None: parser_get.add_argument('datamodelfile', help='Path to the file the ontology should be written to', default='onto.json') - parser_upload = subparsers.add_parser('xmlupload', - help='Upload data from an XML file to the DaSCH Service Platform.') + # xmlupload + parser_upload = subparsers.add_parser('xmlupload', help='Upload data from an XML file to the DaSCH Service Platform.') parser_upload.set_defaults(action='xmlupload') parser_upload.add_argument('-s', '--server', type=str, default=default_localhost, help=url_text) parser_upload.add_argument('-u', '--user', type=str, default=default_user, help=username_text) @@ -88,43 +86,43 @@ def program(user_args: list[str]) -> None: parser_upload.add_argument('-I', '--incremental', action='store_true', help='Incremental XML upload') parser_upload.add_argument('xmlfile', help='path to xml file containing the data', default='data.xml') - parser_excel_lists = subparsers.add_parser('excel', - help='Create a JSON list from one or multiple Excel files. The JSON ' - 'list can be integrated into a JSON ontology. If the list should ' - 'contain multiple languages, an Excel file has to be used for ' - 'each language. The filenames should contain the language as ' - 'label, p.ex. liste_de.xlsx, list_en.xlsx. The language is then ' - 'taken from the filename. Only files with extension .xlsx are ' - 'considered.') + # excel + parser_excel_lists = subparsers.add_parser( + 'excel', + help='Create a JSON list from one or multiple Excel files. The JSON list can be integrated into a JSON ' + 'ontology. If the list should contain multiple languages, an Excel file has to be used for each language. ' + 'The filenames should contain the language as label, p.ex. liste_de.xlsx, list_en.xlsx. The language is ' + 'then taken from the filename. Only files with extension .xlsx are considered.' + ) parser_excel_lists.set_defaults(action='excel') parser_excel_lists.add_argument('-l', '--listname', type=str, - help='Name of the list to be created (filename is taken if ' - 'omitted)', default=None) + help='Name of the list to be created (filename is taken if omitted)', default=None) parser_excel_lists.add_argument('excelfolder', help='Path to the folder containing the Excel file(s)', default='lists') parser_excel_lists.add_argument('outfile', help='Path to the output JSON file containing the list data', default='list.json') - parser_excel_resources = subparsers.add_parser('excel2resources', - help='Create a JSON file from an Excel file containing ' - 'resources for a DSP ontology. ') + # excel2resources + parser_excel_resources = subparsers.add_parser('excel2resources', help='Create a JSON file from an Excel file ' + 'containing resources for a DSP ontology. ') parser_excel_resources.set_defaults(action='excel2resources') parser_excel_resources.add_argument('excelfile', help='Path to the Excel file containing the resources', default='resources.xlsx') parser_excel_resources.add_argument('outfile', help='Path to the output JSON file containing the resource data', default='resources.json') - parser_excel_properties = subparsers.add_parser('excel2properties', - help='Create a JSON file from an Excel file containing ' - 'properties for a DSP ontology. ') + # excel2properties + parser_excel_properties = subparsers.add_parser('excel2properties', help='Create a JSON file from an Excel file ' + 'containing properties for a DSP ontology. ') parser_excel_properties.set_defaults(action='excel2properties') parser_excel_properties.add_argument('excelfile', help='Path to the Excel file containing the properties', default='properties.xlsx') parser_excel_properties.add_argument('outfile', help='Path to the output JSON file containing the properties data', default='properties.json') - parser_id2iri = subparsers.add_parser('id2iri', - help='Replace internal IDs in an XML with their corresponding IRIs from a provided JSON file.') + # id2iri + parser_id2iri = subparsers.add_parser('id2iri', help='Replace internal IDs in an XML with their corresponding IRIs ' + 'from a provided JSON file.') parser_id2iri.set_defaults(action='id2iri') parser_id2iri.add_argument('xmlfile', help='Path to the XML file containing the data to be replaced') parser_id2iri.add_argument('jsonfile', @@ -133,6 +131,16 @@ def program(user_args: list[str]) -> None: help='Path to the XML output file containing the replaced IDs (optional)') parser_id2iri.add_argument('-v', '--verbose', action='store_true', help=verbose_text) + # excel2xml + parser_excel2xml = subparsers.add_parser('excel2xml', help='Transform a tabular data source in CSV/XLS(X) format ' + 'to DSP-conforming XML. ') + parser_excel2xml.set_defaults(action='excel2xml') + parser_excel2xml.add_argument('datafile', help='Path to the CSV or XLS(X) file containing the data') + parser_excel2xml.add_argument('shortcode', help='Shortcode of the project that this data belongs to') + parser_excel2xml.add_argument('default_ontology', help='Name of the ontology that this data belongs to') + + + # call the requested action args = parser.parse_args(user_args) if not hasattr(args, 'action'): @@ -150,10 +158,9 @@ def program(user_args: list[str]) -> None: password=args.password, dump=args.dump) else: - if args.validate_only: - if validate_project(args.datamodelfile): - print('Data model is syntactically correct and passed validation.') - exit(0) + if args.validate_only and validate_project(args.datamodelfile): + print('Data model is syntactically correct and passed validation.') + exit(0) else: create_project(input_file=args.datamodelfile, server=args.server, @@ -193,6 +200,10 @@ def program(user_args: list[str]) -> None: json_file=args.jsonfile, out_file=args.outfile, verbose=args.verbose) + elif args.action == 'excel2xml': + excel2xml(datafile=args.datafile, + shortcode=args.shortcode, + default_ontology=args.default_ontology) def main() -> None: diff --git a/knora/dsplib/models/value.py b/knora/dsplib/models/value.py index 4d0f63f63..da77f13cb 100644 --- a/knora/dsplib/models/value.py +++ b/knora/dsplib/models/value.py @@ -1,6 +1,7 @@ import re from typing import Optional, Any, Union +import regex from pystrict import strict from .helpers import IriTest, Actions, BaseError @@ -269,7 +270,7 @@ def __init__(self, # A knora date value # m = re.match( - '(GREGORIAN:|JULIAN:)?(CE:|BCE:)?(\\d{4})?(-\\d{1,2})?(-\\d{1,2})?(:CE|:BCE)?(:\\d{4})?(-\\d{1,2})?(-\\d{1,2})?', + r"^(GREGORIAN:|JULIAN:)?(CE:|BCE:)?(\d{4})(-\d{1,2})?(-\d{1,2})?((:CE|:BCE)?(:\d{4})(-\d{1,2})?(-\d{1,2})?)?$", str(value)) if not m: raise BaseError("Invalid date format: \"{}\"!".format(str(value))) @@ -279,10 +280,10 @@ def __init__(self, self._y1 = None if dp[2] is None else int(dp[2].strip('-: ')) self._m1 = None if dp[3] is None else int(dp[3].strip('-: ')) self._d1 = None if dp[4] is None else int(dp[4].strip('-: ')) - self._e2 = 'CE' if dp[5] is None else dp[5].strip('-: ') - self._y2 = None if dp[6] is None else int(dp[6].strip('-: ')) - self._m2 = None if dp[7] is None else int(dp[7].strip('-: ')) - self._d2 = None if dp[8] is None else int(dp[8].strip('-: ')) + self._e2 = 'CE' if dp[6] is None else dp[6].strip('-: ') + self._y2 = None if dp[7] is None else int(dp[7].strip('-: ')) + self._m2 = None if dp[8] is None else int(dp[8].strip('-: ')) + self._d2 = None if dp[9] is None else int(dp[9].strip('-: ')) if self._y1 is None: raise BaseError("Invalid date format! " + str(value)) @@ -636,7 +637,9 @@ def __init__(self, iri: Optional[str] = None, ark_url: Optional[str] = None, vark_url: Optional[str] = None): - m = re.match("^(http)s?://([\\w\\.\\-~]+)?(:\\d{,6})?(/[\\w\\-~]+)*(#[\\w\\-~]*)?", str(value)) + # URI = scheme ":" ["//" host [":" port]] path ["?" query] ["#" fragment] + m = regex.match(r"(?[a-z][a-z0-9+.\-]*):(//(?[\w_.\-\[\]:~]+)(?:\d{0,6})?)(?/[\w%()_\-.~]*)*" + r"(?\?[\w_.\-=]+)*(?#[\w_/\-~:.]*)?", str(value), flags=regex.UNICODE) if m: self._value = str(value) else: @@ -688,7 +691,7 @@ def __init__(self, iri: Optional[str] = None, ark_url: Optional[str] = None, vark_url: Optional[str] = None): - m = re.match("^([+-])?(\\d{4}-[0-1]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d)(.\\d+)?(Z|[+-][0-2]\\d:[0-5]\\d)$", + m = re.match(r"^\d{4}-[0-1]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d(.\d{1,12})?(Z|[+-][0-1]\d:[0-5]\d)$", str(value)) if m: self._value = str(value) diff --git a/knora/dsplib/schemas/data.xsd b/knora/dsplib/schemas/data.xsd index a863466b6..e49b81d72 100644 --- a/knora/dsplib/schemas/data.xsd +++ b/knora/dsplib/schemas/data.xsd @@ -23,7 +23,15 @@ + value="(GREGORIAN:|JULIAN:)?(CE:|BCE:)?(\d{4})(-\d{1,2})?(-\d{1,2})?((:CE|:BCE)?(:\d{4})(-\d{1,2})?(-\d{1,2})?)?"/> + + + + + + + @@ -34,6 +42,13 @@ + + + + + + + @@ -163,7 +178,7 @@ - + @@ -183,7 +198,7 @@ - + diff --git a/knora/dsplib/utils/excel_to_json_lists.py b/knora/dsplib/utils/excel_to_json_lists.py index bb70e6e42..d82528da5 100644 --- a/knora/dsplib/utils/excel_to_json_lists.py +++ b/knora/dsplib/utils/excel_to_json_lists.py @@ -137,7 +137,7 @@ def _get_values_from_excel( f"{cell.column}, row {cell.row}:\n'{cell.value.strip()}'") # create a simplified version of the cell value and use it as name of the node - nodename = _simplify_name(cell.value.strip()) + nodename = simplify_name(cell.value.strip()) list_of_previous_node_names.append(nodename) # append a number (p.ex. node-name-2) if there are list nodes with identical names @@ -244,7 +244,7 @@ def _contains_duplicates(list_to_check: list[Any]) -> bool: return has_duplicates -def _simplify_name(value: str) -> str: +def simplify_name(value: str) -> str: """ Simplifies a given value in order to use it as node name diff --git a/knora/excel2xml.py b/knora/excel2xml.py new file mode 100644 index 000000000..0a41a70a4 --- /dev/null +++ b/knora/excel2xml.py @@ -0,0 +1,2002 @@ +import datetime +import json +import os +import re +import uuid +import warnings +import difflib +from operator import xor +import regex + +import pandas as pd +from typing import Any, Iterable, Optional, Union +from lxml import etree +from lxml.builder import E +import dataclasses + +from knora.dsplib.models.helpers import BaseError +from knora.dsplib.utils.excel_to_json_lists import simplify_name + +############################## +# global variables and classes +############################## +xml_namespace_map = { + None: "https://dasch.swiss/schema", + "xsi": "http://www.w3.org/2001/XMLSchema-instance" +} + + +@dataclasses.dataclass(frozen=True) +class PropertyElement: + """ + A PropertyElement object carries more information about a property value than the value itself. + The "value" is the value that could be passed to a method as plain string/int/float/bool. Use a PropertyElement + instead to define more precisely what attributes your tag (for example) will have. + + Args: + value: This is the content that will be written between the tags (for example) + permissions: This is the permissions that your tag (for example) will have + comment: This is the comment that your tag (for example) will have + encoding: For tags only. Can be "xml" or "utf8". + + Examples: + See the difference between the first and the second example: + + >>> make_text_prop(":testproperty", "first text") + + + first text + + + >>> make_text_prop(":testproperty", PropertyElement("first text", permissions="prop-restricted", encoding="xml")) + + + first text + + + """ + value: Union[str, int, float, bool] + permissions: str = "prop-default" + comment: Optional[str] = None + encoding: Optional[str] = None + + def __post_init__(self) -> None: + if not check_notna(self.value): + raise BaseError(f"'{self.value}' is not a valid value for a PropertyElement") + if self.encoding not in ["utf8", "xml", None]: + raise BaseError(f"'{self.encoding}' is not a valid encoding for a PropertyElement") + + +########### +# functions +########### +def make_xsd_id_compatible(string: str) -> str: + """ + Make a string compatible with the constraints of xsd:ID as defined in http://www.datypic.com/sc/xsd/t-xsd_ID.html. + An xsd:ID cannot contain special characters, and it must be unique in the document. + + This method replaces the illegal characters by "_" and appends a random number to the string to make it unique. + + The string must contain at least one word-character (regex [A-Za-z0-9_]), but must not be "None", "", "N/A", or + "-". In such cases, a BaseError is thrown. + + Args: + string: string which to make the xsd:ID from + + Returns: + an `xsd:ID` based on string + """ + + if not isinstance(string, str) or not check_notna(string): + raise BaseError(f"The string {string} cannot be made an xsd:ID") + + # if start of string is neither letter nor underscore, add an underscore + res = re.sub(r"^(?=[^A-Za-z_])", "_", string) + + # add uuid + _uuid = uuid.uuid4() + res = f"{res}_{_uuid}" + + # replace all illegal characters by underscore + res = re.sub(r"[^\d\w_\-.]", "_", res) + + return res + + +def find_date_in_string(string: str, calling_resource: str = "") -> Optional[str]: + """ + Checks if a string contains a date value (single date, or date range), and returns the first found date as + DSP-formatted string. Returns None if no date was found. + + Notes: + - Assumes Christian era (no BC dates) and Gregorian calendar. + - The years 0000-2999 are supported, in 4-digit form. + - Dates written with slashes are always interpreted in a European manner: 5/11/2021 is the 5th of November. + + Currently supported date formats: + - 0476-09-04 -> GREGORIAN:CE:0476-09-04:CE:0476-09-04 + - 0476_09_04 -> GREGORIAN:CE:0476-09-04:CE:0476-09-04 + - 30.4.2021 -> GREGORIAN:CE:2021-04-30:CE:2021-04-30 + - 5/11/2021 -> GREGORIAN:CE:2021-11-05:CE:2021-11-05 + - Jan 26, 1993 -> GREGORIAN:CE:1993-01-26:CE:1993-01-26 + - February26,2051 -> GREGORIAN:CE:2051-02-26:CE:2051-02-26 + - 28.2.-1.12.1515 --> GREGORIAN:CE:1515-02-28:CE:1515-12-01 + - 25.-26.2.0800 --> GREGORIAN:CE:0800-02-25:CE:0800-02-26 + - 1.9.2022-3.1.2024 --> GREGORIAN:CE:2022-09-01:CE:2024-01-03 + - 1848 -> GREGORIAN:CE:1848:CE:1848 + - 1849/1850 -> GREGORIAN:CE:1849:CE:1850 + - 1849/50 -> GREGORIAN:CE:1849:CE:1850 + - 1845-50 -> GREGORIAN:CE:1845:CE:1850 + + Args: + string: string to check + calling_resource: the name of the parent resource (for better error messages) + + Returns: + DSP-formatted date string, or None + + Examples: + >>> if find_date_in_string(row["Epoch"]): + >>> resource.append(make_date_prop(":hasDate", find_date_in_string(row["Epoch"])) + + See https://docs.dasch.swiss/latest/DSP-TOOLS/dsp-tools-xmlupload/#date-prop + """ + + monthes_dict = { + "January": 1, + "Jan": 1, + "February": 2, + "Feb": 2, + "March": 3, + "Mar": 3, + "April": 4, + "Apr": 4, + "May": 5, + "June": 6, + "Jun": 6, + "July": 7, + "Jul": 7, + "August": 8, + "Aug": 8, + "September": 9, + "Sept": 9, + "October": 10, + "Oct": 10, + "November": 11, + "Nov": 11, + "December": 12, + "Dec": 12, + } + + startdate: Optional[datetime.date] = None + enddate: Optional[datetime.date] = None + startyear: Optional[int] = None + endyear: Optional[int] = None + + year_regex = r"([0-2][0-9][0-9][0-9])" + month_regex = r"([0-1]?[0-9])" + day_regex = r"([0-3]?[0-9])" + sep_regex = r"[\./]" + lookbehind = r"(? bool: + """ + Check a value if it is usable in the context of data archiving. A value is considered usable if it is + - a number (integer or float, but not np.nan) + - a boolean + - a string with at least one Unicode letter, underscore, or number, but not "None", "", "N/A", or "-" + - a PropertyElement whose "value" fulfills the above criteria + + Args: + value: any object encountered when analysing data + + Returns: + True if the value is usable, False if it is N/A or otherwise unusable + """ + + if isinstance(value, PropertyElement): + value = value.value + + if any([ + isinstance(value, int), + isinstance(value, float) and pd.notna(value), # necessary because isinstance(np.nan, float) + isinstance(value, bool) + ]): + return True + elif isinstance(value, str): + return all([ + regex.search(r"\p{L}|\d|_", value, flags=re.UNICODE), + not bool(re.search(r"^(none||-|n/a)$", value, flags=re.IGNORECASE)) + ]) + else: + return False + + +def _check_and_prepare_values( + value: Optional[Union[PropertyElement, str, int, float, bool]], + values: Optional[Iterable[Union[PropertyElement, str, int, float, bool]]], + name: str, + calling_resource: str = "" +) -> list[PropertyElement]: + """ + There is a variety of possibilities how to call a make_*_prop() method. Before such a method can do its job, the + parameters need to be checked and prepared, which is done by this helper method. The parameters "value" and "values" + are passed to it as they were received. This method will then perform the following checks, and throw a BaseError in + case of failure: + - check that exactly one of them contains data, but not both. + - check that the values are usable, and not N/A + + Then, all values are transformed to PropertyElements and returned as a list. In case of a single "value", the + resulting list contains the PropertyElement of this value. + + Args: + value: "value" as received from the caller + values: "values" as received from the caller + name: name of the property (for better error messages) + calling_resource: name of the resource (for better error messages) + + Returns: + a list of PropertyElements + """ + + # reduce 'value' to None if it is not usable + if not check_notna(value): + value = None + + # reduce 'values' to None if it is not usable + if values and not any([check_notna(val) for val in values]): + values = None + + # assert that either "value" or "values" is usable, but not both at the same time + if not value and not values: + raise BaseError(f"ERROR in resource '{calling_resource}', property '{name}': 'value' and 'values' cannot both " + f"be empty") + if value and values: + raise BaseError(f"ERROR in resource '{calling_resource}', property '{name}': You cannot provide a 'value' and " + f"a 'values' at the same time!") + + # construct the resulting list + result: list[PropertyElement] = list() + + if value: + # make a PropertyElement out of it, if necessary + if isinstance(value, PropertyElement): + result.append(value) + else: + result.append(PropertyElement(value)) + elif values: + # if "values" contains unusable elements, remove them + multiple_values = [val for val in values if check_notna(val)] + # make a PropertyElement out of them, if necessary + for elem in multiple_values: + if isinstance(elem, PropertyElement): + result.append(elem) + else: + result.append(PropertyElement(elem)) + + return result + + +def make_root(shortcode: str, default_ontology: str) -> etree.Element: + """ + Start building your XML document by creating the root element . + + Args: + shortcode: The shortcode of this project as defined in the JSON project file + default ontology: one of the ontologies of the JSON project file + + Returns: + The root element . + + Examples: + >>> root = make_root(shortcode=shortcode, default_ontology=default_ontology) + >>> root = append_permissions(root) + + See https://docs.dasch.swiss/latest/DSP-TOOLS/dsp-tools-xmlupload/#the-root-element-knora + """ + + root = etree.Element( + _tag="{%s}knora" % (xml_namespace_map[None]), + attrib={ + str(etree.QName("http://www.w3.org/2001/XMLSchema-instance", "schemaLocation")): + "https://dasch.swiss/schema " + \ + "https://raw.githubusercontent.com/dasch-swiss/dsp-tools/main/knora/dsplib/schemas/data.xsd", + "shortcode": shortcode, + "default-ontology": default_ontology + }, + nsmap=xml_namespace_map + ) + return root + + +def append_permissions(root_element: etree.Element) -> etree.Element: + """ + After having created a root element, call this method to append the four permissions "res-default", + "res-restricted", "prop-default", and "prop-restricted" to it. These four permissions are a good basis to + start with, but remember that they can be adapted, and that other permissions can be defined instead of these. + + Args: + root_element: The XML root element created by make_root() + + Returns: + The root element with the four permission blocks appended + + Examples: + >>> root = make_root(shortcode=shortcode, default_ontology=default_ontology) + >>> root = append_permissions(root) + + See https://docs.dasch.swiss/latest/DSP-TOOLS/dsp-tools-xmlupload/#describing-permissions-with-permissions-elements + """ + + PERMISSIONS = E.permissions + ALLOW = E.allow + # lxml.builder.E is a more sophisticated element factory than etree.Element. + # E.tag is equivalent to E("tag") and results in + + res_default = etree.Element("{%s}permissions" % (xml_namespace_map[None]), id="res-default") + res_default.append(ALLOW("V", group="UnknownUser")) + res_default.append(ALLOW("V", group="KnownUser")) + res_default.append(ALLOW("CR", group="Creator")) + res_default.append(ALLOW("CR", group="ProjectAdmin")) + root_element.append(res_default) + + res_restricted = PERMISSIONS(id="res-restricted") + res_restricted.append(ALLOW("RV", group="UnknownUser")) + res_restricted.append(ALLOW("V", group="KnownUser")) + res_restricted.append(ALLOW("CR", group="Creator")) + res_restricted.append(ALLOW("CR", group="ProjectAdmin")) + root_element.append(res_restricted) + + prop_default = PERMISSIONS(id="prop-default") + prop_default.append(ALLOW("V", group="UnknownUser")) + prop_default.append(ALLOW("V", group="KnownUser")) + prop_default.append(ALLOW("CR", group="Creator")) + prop_default.append(ALLOW("CR", group="ProjectAdmin")) + root_element.append(prop_default) + + prop_restricted = PERMISSIONS(id="prop-restricted") + prop_restricted.append(ALLOW("RV", group="UnknownUser")) + prop_restricted.append(ALLOW("V", group="KnownUser")) + prop_restricted.append(ALLOW("CR", group="Creator")) + prop_restricted.append(ALLOW("CR", group="ProjectAdmin")) + root_element.append(prop_restricted) + + return root_element + + +def make_resource( + label: str, + restype: str, + id: str, + permissions: str = "res-default", + ark: Optional[str] = None, + iri: Optional[str] = None +) -> etree.Element: + """ + Creates an empty resource element, with the attributes as specified by the arguments + + Args: + The arguments correspond to the attributes of the element. + + Returns: + The resource element, without any children, but with the attributes + ```` + + Examples: + >>> resource = make_resource(...) + >>> resource.append(make_text_prop(...)) + >>> root.append(resource) + + See https://docs.dasch.swiss/latest/DSP-TOOLS/dsp-tools-xmlupload/#describing-resources-with-the-resource-element + """ + + kwargs = { + "label": label, + "restype": restype, + "id": id, + "permissions": permissions, + "nsmap": xml_namespace_map + } + if ark: + kwargs["ark"] = ark + if iri: + kwargs["iri"] = iri + if ark and iri: + warnings.warn(f"Both ARK and IRI were provided for resource '{label}' ({id}). The ARK will override the IRI.", + stacklevel=2) + + resource_ = etree.Element( + "{%s}resource" % (xml_namespace_map[None]), + **kwargs + ) + return resource_ + + +def make_bitstream_prop( + path: str, + permissions: str = "prop-default", + calling_resource: str = "" +) -> etree.Element: + """ + Creates a bitstream element that points to path. + + Args: + path: path to a valid file that will be uploaded + permissions: permissions string + calling_resource: the name of the parent resource (for better error messages) + + Returns: + an etree.Element that can be appended to the parent resource with resource.append(make_*_prop(...)) + + Examples: + >>> resource = make_resource(...) + >>> resource.append(make_bitstream_prop("data/images/tree.jpg")) + >>> root.append(resource) + + See https://docs.dasch.swiss/latest/DSP-TOOLS/dsp-tools-xmlupload/#bitstream + """ + + if not os.path.isfile(path): + warnings.warn(f"The following is not a valid path: {path} (resource '{calling_resource}')", + stacklevel=2) + prop_ = etree.Element("{%s}bitstream" % (xml_namespace_map[None]), permissions=permissions, + nsmap=xml_namespace_map) + prop_.text = path + return prop_ + + +def _format_bool(unformatted: Union[bool, str, int], name: str, calling_resource: str) -> str: + if isinstance(unformatted, str): + unformatted = unformatted.lower() + if unformatted in (False, "false", "0", 0, "no"): + return "false" + elif unformatted in (True, "true", "1", 1, "yes"): + return "true" + else: + raise BaseError(f"Invalid boolean format for prop '{name}' in resource '{calling_resource}': '{unformatted}'") + + +def make_boolean_prop( + name: str, + value: Union[PropertyElement, str, int, bool], + calling_resource: str = "" +) -> etree.Element: + """ + Make a from a boolean value. The value can be provided directly or inside a PropertyElement. The + following formats are supported: + - true: (True, "true", "True", "1", 1, "yes", "Yes") + - false: (False, "false", "False", "0", 0, "no", "No") + + Unless provided as PropertyElement, the permission for every value is "prop-default". + + Args: + name: the name of this property as defined in the onto + value: a str/bool/int itself or inside a PropertyElement + calling_resource: the name of the parent resource (for better error messages) + + Returns: + an etree.Element that can be appended to the parent resource with resource.append(make_*_prop(...)) + + Examples: + >>> make_boolean_prop(":testproperty", "no") + + false + + >>> make_boolean_prop(":testproperty", PropertyElement("1", permissions="prop-restricted", comment="example")) + + true + + + See https://docs.dasch.swiss/latest/DSP-TOOLS/dsp-tools-xmlupload/#boolean-prop + """ + + # validate input + if isinstance(value, PropertyElement): + value_new = dataclasses.replace(value, value=_format_bool(value.value, name, calling_resource)) + elif isinstance(value, str) or isinstance(value, bool) or isinstance(value, int): + value_new = PropertyElement(_format_bool(value, name, calling_resource)) + else: + raise BaseError(f"Invalid boolean format for prop '{name}' in resource '{calling_resource}': '{value}'") + + # make xml structure of the value + prop_ = etree.Element( + "{%s}boolean-prop" % (xml_namespace_map[None]), + name=name, + nsmap=xml_namespace_map + ) + kwargs = {"permissions": value_new.permissions} + if check_notna(value_new.comment): + kwargs["comment"] = value_new.comment + value_ = etree.Element( + "{%s}boolean" % (xml_namespace_map[None]), + **kwargs, + nsmap=xml_namespace_map + ) + value_.text = value_new.value + prop_.append(value_) + + return prop_ + + +def make_color_prop( + name: str, + value: Optional[Union[PropertyElement, str]] = None, + values: Optional[Iterable[Union[PropertyElement, str]]] = None, + calling_resource: str = "" +) -> etree.Element: + """ + Make a from one or more colors. The color(s) can be provided as string or as PropertyElement with a + string inside. If provided as string, the permission for every value is "prop-default". + + To create one ```` child, use the param ``value``, to create more than one ```` children, use + ``values``. + + Args: + name: the name of this property as defined in the onto + value: a string/PropertyElement + values: an iterable of (usually distinct) strings/PropertyElements + calling_resource: the name of the parent resource (for better error messages) + + Returns: + an etree.Element that can be appended to the parent resource with resource.append(make_*_prop(...)) + + Examples: + >>> make_color_prop(":testproperty", "#00ff66") + + #00ff66 + + >>> make_color_prop(":testproperty", PropertyElement("#00ff66", permissions="prop-restricted", comment="example")) + + #00ff66 + + >>> make_color_prop(":testproperty", values=["#00ff66", "#000000"]) + + #00ff66 + #000000 + + + See https://docs.dasch.swiss/latest/DSP-TOOLS/dsp-tools-xmlupload/#color-prop + """ + + # check the input: prepare a list with valid values + values_new = _check_and_prepare_values( + value=value, + values=values, + name=name, + calling_resource=calling_resource + ) + + # check value type + for val in values_new: + if not re.search(r"^#[0-9a-f]{6}$", str(val.value).strip(), flags=re.IGNORECASE): + raise BaseError(f"Invalid color format for prop '{name}' in resource '{calling_resource}': '{val.value}'") + + # make xml structure of the value + prop_ = etree.Element( + "{%s}color-prop" % (xml_namespace_map[None]), + name=name, + nsmap=xml_namespace_map + ) + for val in values_new: + kwargs = {"permissions": val.permissions} + if check_notna(val.comment): + kwargs["comment"] = val.comment + value_ = etree.Element( + "{%s}color" % (xml_namespace_map[None]), + **kwargs, + nsmap=xml_namespace_map + ) + value_.text = str(val.value).strip() + prop_.append(value_) + + return prop_ + + +def make_date_prop( + name: str, + value: Optional[Union[PropertyElement, str]] = None, + values: Optional[Iterable[Union[PropertyElement, str]]] = None, + calling_resource: str = "" +) -> etree.Element: + """ + Make a from one or more dates/date ranges. The date(s) can be provided as string or as PropertyElement + with a string inside. If provided as string, the permission for every value is "prop-default". + + To create one ```` child, use the param ``value``, to create more than one ```` children, use ``values``. + + Args: + name: the name of this property as defined in the onto + value: a string/PropertyElement + values: an iterable of (usually distinct) strings/PropertyElements + calling_resource: the name of the parent resource (for better error messages) + + Returns: + an etree.Element that can be appended to the parent resource with resource.append(make_*_prop(...)) + + Examples: + >>> make_date_prop(":testproperty", "GREGORIAN:CE:2014-01-31") + + GREGORIAN:CE:2014-01-31 + + >>> make_date_prop(":testproperty", PropertyElement("GREGORIAN:CE:2014-01-31", permissions="prop-restricted", comment="example")) + + + GREGORIAN:CE:2014-01-31 + + + >>> make_date_prop(":testproperty", values=["GREGORIAN:CE:1930-09-02:CE:1930-09-03", "GREGORIAN:CE:1930-09-02:CE:1930-09-03"]) + + + GREGORIAN:CE:1930-09-02:CE:1930-09-03 + + + GREGORIAN:CE:1930-09-02:CE:1930-09-03 + + + + See https://docs.dasch.swiss/latest/DSP-TOOLS/dsp-tools-xmlupload/#date-prop + """ + + # check the input: prepare a list with valid values + values_new = _check_and_prepare_values( + value=value, + values=values, + name=name, + calling_resource=calling_resource + ) + + # check value type + for val in values_new: + if not re.search(r"^(GREGORIAN:|JULIAN:)?(CE:|BCE:)?(\d{4})(-\d{1,2})?(-\d{1,2})?" + r"((:CE|:BCE)?(:\d{4})(-\d{1,2})?(-\d{1,2})?)?$", str(val.value).strip()): + raise BaseError(f"Invalid date format for prop '{name}' in resource '{calling_resource}': '{val.value}'") + + # make xml structure of the value + prop_ = etree.Element( + "{%s}date-prop" % (xml_namespace_map[None]), + name=name, + nsmap=xml_namespace_map + ) + for val in values_new: + kwargs = {"permissions": val.permissions} + if check_notna(val.comment): + kwargs["comment"] = val.comment + value_ = etree.Element( + "{%s}date" % (xml_namespace_map[None]), + **kwargs, + nsmap=xml_namespace_map + ) + value_.text = str(val.value).strip() + prop_.append(value_) + + return prop_ + + +def make_decimal_prop( + name: str, + value: Optional[Union[PropertyElement, str]] = None, + values: Optional[Iterable[Union[PropertyElement, str]]] = None, + calling_resource: str = "" +) -> etree.Element: + """ + Make a from one or more decimal numbers. The decimal(s) can be provided as string, float, or as + PropertyElement with a string/float inside. If provided as string/float, the permission for every value is + "prop-default". + + To create one ```` child, use the param ``value``, to create more than one ```` children, use + ``values``. + + Args: + name: the name of this property as defined in the onto + value: a string/float/PropertyElement + values: an iterable of distinct strings/PropertyElements + calling_resource: the name of the parent resource (for better error messages) + + Returns: + an etree.Element that can be appended to the parent resource with resource.append(make_*_prop(...)) + + Examples: + >>> make_decimal_prop(":testproperty", "3.14159") + + 3.14159 + + >>> make_decimal_prop(":testproperty", PropertyElement("3.14159", permissions="prop-restricted", comment="example")) + + 3.14159 + + >>> make_decimal_prop(":testproperty", values=["3.14159", "2.718"]) + + 3.14159 + 2.718 + + + See https://docs.dasch.swiss/latest/DSP-TOOLS/dsp-tools-xmlupload/#decimal-prop + """ + + # check the input: prepare a list with valid values + values_new = _check_and_prepare_values( + value=value, + values=values, + name=name, + calling_resource=calling_resource + ) + + # check value type + for val in values_new: + if not re.search(r"^\d+\.\d+$", str(val.value).strip()): + raise BaseError(f"Invalid decimal format for prop '{name}' in resource '{calling_resource}': '{val.value}'") + + # make xml structure of the value + prop_ = etree.Element( + "{%s}decimal-prop" % (xml_namespace_map[None]), + name=name, + nsmap=xml_namespace_map + ) + for val in values_new: + kwargs = {"permissions": val.permissions} + if check_notna(val.comment): + kwargs["comment"] = val.comment + value_ = etree.Element( + "{%s}decimal" % (xml_namespace_map[None]), + **kwargs, + nsmap=xml_namespace_map + ) + value_.text = str(val.value) + prop_.append(value_) + + return prop_ + + +def make_geometry_prop( + name: str, + value: Optional[Union[PropertyElement, str]] = None, + values: Optional[Iterable[Union[PropertyElement, str]]] = None, + calling_resource: str = "" +) -> etree.Element: + """ + Make a from one or more areas of an image. The area(s) can be provided as JSON-string or as + PropertyElement with the JSON-string inside. If provided as string, the permission for every value is "prop-default". + + To create one ```` child, use the param ``value``, to create more than one ```` children, use + ``values``. + + Args: + name: the name of this property as defined in the onto + value: a string/PropertyElement + values: an iterable of (usually distinct) strings/PropertyElements + calling_resource: the name of the parent resource (for better error messages) + + Returns: + an etree.Element that can be appended to the parent resource with resource.append(make_*_prop(...)) + + Examples: + >>> make_geometry_prop(":testproperty", json_string) + + {JSON} + + >>> make_geometry_prop(":testproperty", PropertyElement(json_string, permissions="prop-restricted", comment="example")) + + {JSON} + + >>> make_geometry_prop(":testproperty", values=[json_string1, json_string2]) + + {JSON} + {JSON} + + + See https://docs.dasch.swiss/latest/DSP-TOOLS/dsp-tools-xmlupload/#geometry-prop + """ + + # check the input: prepare a list with valid values + values_new = _check_and_prepare_values( + value=value, + values=values, + name=name, + calling_resource=calling_resource + ) + + # check value type + for val in values_new: + try: + value_as_dict = json.loads(val.value) + assert value_as_dict["type"] in ["rectangle", "circle"] + assert isinstance(value_as_dict["points"], list) + except (json.JSONDecodeError, TypeError, IndexError, KeyError, AssertionError): + raise BaseError(f"Invalid geometry format for prop '{name}' in resource '{calling_resource}': " + f"'{val.value}'") + + # make xml structure of the value + prop_ = etree.Element( + "{%s}geometry-prop" % (xml_namespace_map[None]), + name=name, + nsmap=xml_namespace_map + ) + for val in values_new: + kwargs = {"permissions": val.permissions} + if check_notna(val.comment): + kwargs["comment"] = val.comment + value_ = etree.Element( + "{%s}geometry" % (xml_namespace_map[None]), + **kwargs, + nsmap=xml_namespace_map + ) + value_.text = str(val.value) + prop_.append(value_) + return prop_ + + +def make_geoname_prop( + name: str, + value: Optional[Union[PropertyElement, str, int]] = None, + values: Optional[Iterable[Union[PropertyElement, str, int]]] = None, + calling_resource: str = "" +) -> etree.Element: + """ + Make a from one or more geonames.org IDs. The ID(s) can be provided as string, integer, or as + PropertyElement with a string/integer inside. If provided as string/integer, the permission for every value is + "prop-default". + + To create one ```` child, use the param ``value``, to create more than one ```` children, use + ``values``. + + Args: + name: the name of this property as defined in the onto + value: a string/int/PropertyElement + values: an iterable of (usually distinct) strings/ints/PropertyElements + calling_resource: the name of the parent resource (for better error messages) + + Returns: + an etree.Element that can be appended to the parent resource with resource.append(make_*_prop(...)) + + Examples: + >>> make_geoname_prop(":testproperty", "2761369") + + 2761369 + + >>> make_geoname_prop(":testproperty", PropertyElement("2761369", permissions="prop-restricted", comment="example")) + + 2761369 + + >>> make_geoname_prop(":testproperty", values=["2761369", "1010101"]) + + 2761369 + 1010101 + + + See https://docs.dasch.swiss/latest/DSP-TOOLS/dsp-tools-xmlupload/#geoname-prop + """ + + # check the input: prepare a list with valid values + values_new = _check_and_prepare_values( + value=value, + values=values, + name=name, + calling_resource=calling_resource + ) + + # check value type + for val in values_new: + if not re.search(r"^[0-9]+$", str(val.value)): + raise BaseError(f"Invalid geoname format for prop '{name}' in resource '{calling_resource}': '{val.value}'") + + prop_ = etree.Element( + "{%s}geoname-prop" % (xml_namespace_map[None]), + name=name, + nsmap=xml_namespace_map + ) + for val in values_new: + kwargs = {"permissions": val.permissions} + if check_notna(val.comment): + kwargs["comment"] = val.comment + value_ = etree.Element( + "{%s}geoname" % (xml_namespace_map[None]), + **kwargs, + nsmap=xml_namespace_map + ) + value_.text = str(val.value) + prop_.append(value_) + + return prop_ + + +def make_integer_prop( + name: str, + value: Optional[Union[PropertyElement, str, int]] = None, + values: Optional[Iterable[Union[PropertyElement, str, int]]] = None, + calling_resource: str = "" +) -> etree.Element: + """ + Make a from one or more integers. The integers can be provided as string, integer, or as + PropertyElement with a string/integer inside. If provided as string/integer, the permission for every value is + "prop-default". + + To create one ```` child, use the param ``value``, to create more than one ```` children, use + ``values``. + + Args: + name: the name of this property as defined in the onto + value: a string/int/PropertyElement + values: an iterable of (usually distinct) strings/ints/PropertyElements + calling_resource: the name of the parent resource (for better error messages) + + Returns: + an etree.Element that can be appended to the parent resource with resource.append(make_*_prop(...)) + + Examples: + >>> make_integer_prop(":testproperty", "2761369") + + 2761369 + + >>> make_integer_prop(":testproperty", PropertyElement("2761369", permissions="prop-restricted", comment="example")) + + 2761369 + + >>> make_integer_prop(":testproperty", values=["2761369", "1010101"]) + + 2761369 + 1010101 + + + See https://docs.dasch.swiss/latest/DSP-TOOLS/dsp-tools-xmlupload/#integer-prop + """ + + # check the input: prepare a list with valid values + values_new = _check_and_prepare_values( + value=value, + values=values, + name=name, + calling_resource=calling_resource + ) + + # check value type + for val in values_new: + if not re.search(r"^\d+$", str(val.value).strip()): + raise BaseError(f"Invalid integer format for prop '{name}' in resource '{calling_resource}': '{val.value}'") + + prop_ = etree.Element( + "{%s}integer-prop" % (xml_namespace_map[None]), + name=name, + nsmap=xml_namespace_map + ) + for val in values_new: + kwargs = {"permissions": val.permissions} + if check_notna(val.comment): + kwargs["comment"] = val.comment + value_ = etree.Element( + "{%s}integer" % (xml_namespace_map[None]), + **kwargs, + nsmap=xml_namespace_map + ) + value_.text = str(val.value) + prop_.append(value_) + + return prop_ + + +def make_interval_prop( + name: str, + value: Optional[Union[PropertyElement, str]] = None, + values: Optional[Iterable[Union[PropertyElement, str]]] = None, + calling_resource: str = "" +) -> etree.Element: + """ + Make a from one or more intervals. The interval(s) can be provided as string or as PropertyElement + with a string inside. If provided as string, the permission for every value is "prop-default". + + To create one ```` child, use the param ``value``, to create more than one ```` children, use + ``values``. + + Args: + name: the name of this property as defined in the onto + value: a string/PropertyElement + values: an iterable of (usually distinct) strings/PropertyElements + calling_resource: the name of the parent resource (for better error messages) + + Returns: + an etree.Element that can be appended to the parent resource with resource.append(make_*_prop(...)) + + Examples: + >>> make_interval_prop(":testproperty", "61:3600") + + 61:3600 + + >>> make_interval_prop(":testproperty", PropertyElement("61:3600", permissions="prop-restricted", comment="example")) + + 61:3600 + + >>> make_interval_prop(":testproperty", values=["61:3600", "60.5:120.5"]) + + 61:3600 + 60.5:120.5 + + + See https://docs.dasch.swiss/latest/DSP-TOOLS/dsp-tools-xmlupload/#interval-prop + """ + + # check the input: prepare a list with valid values + values_new = _check_and_prepare_values( + value=value, + values=values, + name=name, + calling_resource=calling_resource + ) + + # check value type + for val in values_new: + if not re.match(r"([+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)):([+-]?([0-9]+([.][0-9]*)?|[.][0-9]+))", str(val.value)): + raise BaseError(f"Invalid integer format for prop '{name}' in resource '{calling_resource}': '{val.value}'") + + + prop_ = etree.Element( + "{%s}interval-prop" % (xml_namespace_map[None]), + name=name, + nsmap=xml_namespace_map + ) + for val in values_new: + kwargs = {"permissions": val.permissions} + if check_notna(val.comment): + kwargs["comment"] = val.comment + value_ = etree.Element( + "{%s}interval" % (xml_namespace_map[None]), + **kwargs, + nsmap=xml_namespace_map + ) + value_.text = val.value + prop_.append(value_) + + return prop_ + + +def make_list_prop( + list_name: str, + name: str, + value: Optional[Union[PropertyElement, str]] = None, + values: Optional[Iterable[Union[PropertyElement, str]]] = None, + calling_resource: str = "" +) -> etree.Element: + """ + Make a from one or more list items. The list item(s) can be provided as string or as PropertyElement + with a string inside. If provided as string, the permission for every value is "prop-default". + + To create one ```` child, use the param ``value``, to create more than one ```` children, use ``values``. + + Args: + list_name: the name of the list as defined in the onto + name: the name of this property as defined in the onto + value: a string/PropertyElement + values: an iterable of (usually distinct) strings/PropertyElements + calling_resource: the name of the parent resource (for better error messages) + + Returns: + an etree.Element that can be appended to the parent resource with resource.append(make_*_prop(...)) + + Examples: + >>> make_list_prop("mylist", ":testproperty", "first_node") + + first_node + + >>> make_list_prop("mylist", ":testproperty", PropertyElement("first_node", permissions="prop-restricted", comment="example")) + + first_node + + >>> make_list_prop("mylist", ":testproperty", values=["first_node", "second_node"]) + + first_node + second_node + + + See https://docs.dasch.swiss/latest/DSP-TOOLS/dsp-tools-xmlupload/#list-prop + """ + + # check the input: prepare a list with valid values + values_new = _check_and_prepare_values( + value=value, + values=values, + name=name, + calling_resource=calling_resource + ) + + # check value type + for val in values_new: + if not isinstance(val.value, str) or not check_notna(val.value): + raise BaseError(f"Invalid list format for prop '{name}' in resource '{calling_resource}': '{val.value}'") + + # make xml structure of the valid values + prop_ = etree.Element( + "{%s}list-prop" % (xml_namespace_map[None]), + list=list_name, + name=name, + nsmap=xml_namespace_map + ) + for val in values_new: + kwargs = {"permissions": val.permissions} + if check_notna(val.comment): + kwargs["comment"] = val.comment + value_ = etree.Element( + "{%s}list" % (xml_namespace_map[None]), + **kwargs, + nsmap=xml_namespace_map + ) + value_.text = val.value + prop_.append(value_) + + return prop_ + + +def make_resptr_prop( + name: str, + value: Optional[Union[PropertyElement, str]] = None, + values: Optional[Iterable[Union[PropertyElement, str]]] = None, + calling_resource: str = "" +) -> etree.Element: + """ + Make a from one or more links to other resources. The links(s) can be provided as string or as + PropertyElement with a string inside. If provided as string, the permission for every value is "prop-default". + + To create one ```` child, use the param ``value``, to create more than one ```` children, use + ``values``. + + Args: + name: the name of this property as defined in the onto + value: a string/PropertyElement + values: an iterable of (usually distinct) strings/PropertyElements + calling_resource: the name of the parent resource (for better error messages) + + Returns: + an etree.Element that can be appended to the parent resource with resource.append(make_*_prop(...)) + + Examples: + >>> make_resptr_prop(":testproperty", "resource_1") + + resource_1 + + >>> make_resptr_prop(":testproperty", PropertyElement("resource_1", permissions="prop-restricted", comment="example")) + + resource_1 + + >>> make_resptr_prop(":testproperty", values=["resource_1", "resource_2"]) + + resource_1 + resource_2 + + + See https://docs.dasch.swiss/latest/DSP-TOOLS/dsp-tools-xmlupload/#resptr-prop + """ + + # check the input: prepare a list with valid values + values_new = _check_and_prepare_values( + value=value, + values=values, + name=name, + calling_resource=calling_resource + ) + + # check value type + for val in values_new: + if not isinstance(val.value, str) or not check_notna(val.value): + raise BaseError(f"Invalid resptr format for prop '{name}' in resource '{calling_resource}': '{val.value}'") + + # make xml structure of the valid values + prop_ = etree.Element( + "{%s}resptr-prop" % (xml_namespace_map[None]), + name=name, + nsmap=xml_namespace_map + ) + for val in values_new: + kwargs = {"permissions": val.permissions} + if check_notna(val.comment): + kwargs["comment"] = val.comment + value_ = etree.Element( + "{%s}resptr" % (xml_namespace_map[None]), + **kwargs, + nsmap=xml_namespace_map + ) + value_.text = val.value + prop_.append(value_) + + return prop_ + + +def make_text_prop( + name: str, + value: Optional[Union[PropertyElement, str]] = None, + values: Optional[Iterable[Union[PropertyElement, str]]] = None, + calling_resource: str = "" +) -> etree.Element: + """ + Make a from one or more texts. The text(s) can be provided as string or as PropertyElement with a string + inside. The default encoding is utf8. The default permission for every value is "prop-default". + + To create one ```` child, use the param ``value``, to create more than one ```` + children, use ``values``. + + Args: + name: the name of this property as defined in the onto + value: a string/PropertyElement + values: an iterable of (usually distinct) strings/PropertyElements + calling_resource: the name of the parent resource (for better error messages) + + Returns: + an etree.Element that can be appended to the parent resource with resource.append(make_*_prop(...)) + + Examples: + >>> make_text_prop(":testproperty", "first text") + + first text + + >>> make_text_prop(":testproperty", PropertyElement("first text", permissions="prop-restricted", encoding="xml")) + + first text + + >>> make_text_prop(":testproperty", values=["first text", "second text"]) + + first text + second text + + + See https://docs.dasch.swiss/latest/DSP-TOOLS/dsp-tools-xmlupload/#text-prop + """ + + # check the input: prepare a list with valid values + values_new = _check_and_prepare_values( + value=value, + values=values, + name=name, + calling_resource=calling_resource + ) + + # check value type + for val in values_new: + if not isinstance(val.value, str) or not check_notna(val.value): + raise BaseError(f"Invalid text format for prop '{name}' in resource '{calling_resource}': '{val.value}'") + + # make xml structure of the valid values + prop_ = etree.Element( + "{%s}text-prop" % (xml_namespace_map[None]), + name=name, + nsmap=xml_namespace_map + ) + for val in values_new: + kwargs = {"permissions": val.permissions} + if check_notna(val.comment): + kwargs["comment"] = val.comment + if check_notna(val.encoding): + kwargs["encoding"] = val.encoding + else: + kwargs["encoding"] = "utf8" + value_ = etree.Element( + "{%s}text" % (xml_namespace_map[None]), + **kwargs, + nsmap=xml_namespace_map + ) + value_.text = val.value + prop_.append(value_) + + return prop_ + + +def make_time_prop( + name: str, + value: Optional[Union[PropertyElement, str]] = None, + values: Optional[Iterable[Union[PropertyElement, str]]] = None, + calling_resource: str = "" +) -> etree.Element: + """ + Make a from one or more datetime values of the form "2009-10-10T12:00:00-05:00". The time(s) can be + provided as string or as PropertyElement with a string inside. If provided as string, the permission for every + value is "prop-default". + + To create one ``Link' + xml_returned_1 = excel2xml.make_text_prop(":test", excel2xml.PropertyElement( + value='A text with formatting and a Link', encoding="xml")) + xml_returned_1 = etree.tostring(xml_returned_1, encoding="unicode") + xml_returned_1 = re.sub(r" xmlns(:.+?)?=\".+?\"", "", xml_returned_1) + xml_returned_1 = xml_returned_1.replace("<", "<") + xml_returned_1 = xml_returned_1.replace(">", ">") + self.assertEqual(xml_expected_1, xml_returned_1) + + # encoding="unicode" must raise an error + self.assertRaises(BaseError, lambda: excel2xml.make_text_prop(":test", excel2xml.PropertyElement(value="a", encoding="unicode"))) + + + def test_make_time_prop(self) -> None: + prop = "time" + method = excel2xml.make_time_prop + different_values = [ + "2019-10-23T13:45:12.01-14:00", + "2019-10-23T13:45:12-14:00", + "2019-10-23T13:45:12Z", + "2019-10-23T13:45:12-13:30", + "2019-10-23T13:45:12+01:00", + "2019-10-23T13:45:12.1111111+01:00", + "2019-10-23T13:45:12.123456789012Z", + ] + invalid_values = [True, 10.0, 5, "2019-10-2", "CE:1849:CE:1850", "2019-10-23T13:45:12.1234567890123Z", "2022", + "GREGORIAN:CE:2014-01-31"] + run_test(self, prop, method, different_values, invalid_values) + + + def test_make_uri_prop(self) -> None: + prop = "uri" + method = excel2xml.make_uri_prop + different_values = [ + "https://www.test-case.ch/", + "https://reg-exr.com:3000", + "https://reg-exr.com:3000/path/to/file", + "https://reg-exr.com:3000/path/to/file#fragment", + "https://reg-exr.com:3000/path/to/file?query=test", + "https://reg-exr.com:3000/path/to/file?query=test#fragment", + "https://reg-exr.com/path/to/file?query=test#fragment", + "http://www.168.1.1.0/path", + "http://www.168.1.1.0:4200/path", + "http://[2001:0db8:0000:0000:0000:8a2e:0370:7334]:4200/path", + "https://en.wikipedia.org/wiki/Haiku#/media/File:Basho_Horohoroto.jpg" + ] + invalid_values = ["https:", 10.0, 5, "www.test.com"] + run_test(self, prop, method, different_values, invalid_values) + + + def test_make_annotation_link_region(self) -> None: + """ + This method tests 3 methods at the same time: make_annotation(), make_link(), and make_region(). + """ + for method, tagname in [ + (excel2xml.make_annotation, "annotation"), + (excel2xml.make_link, "link"), + (excel2xml.make_region, "region"), + ]: + xml_returned_1 = method("label", "id") + xml_returned_1 = etree.tostring(xml_returned_1, encoding="unicode") + xml_returned_1 = re.sub(r" xmlns(:.+?)?=\".+?\"", "", xml_returned_1) + self.assertEqual(f'<{tagname} label="label" id="id" permissions="res-default"/>', xml_returned_1) + + xml_returned_2 = method("label", "id", "res-restricted") + xml_returned_2 = etree.tostring(xml_returned_2, encoding="unicode") + xml_returned_2 = re.sub(r" xmlns(:.+?)?=\".+?\"", "", xml_returned_2) + self.assertEqual(f'<{tagname} label="label" id="id" permissions="res-restricted"/>', xml_returned_2) + + xml_returned_3 = method("label", "id", ark="ark") + xml_returned_3 = etree.tostring(xml_returned_3, encoding="unicode") + xml_returned_3 = re.sub(r" xmlns(:.+?)?=\".+?\"", "", xml_returned_3) + self.assertEqual(f'<{tagname} label="label" id="id" permissions="res-default" ark="ark"/>', xml_returned_3) + + xml_returned_4 = method("label", "id", iri="iri") + xml_returned_4 = etree.tostring(xml_returned_4, encoding="unicode") + xml_returned_4 = re.sub(r" xmlns(:.+?)?=\".+?\"", "", xml_returned_4) + self.assertEqual(f'<{tagname} label="label" id="id" permissions="res-default" iri="iri"/>', xml_returned_4) + + self.assertWarns(UserWarning, lambda: method("label", "id", ark="ark", iri="iri")) + + + def test_make_resource(self) -> None: + xml_returned_1 = excel2xml.make_resource("label", "restype", "id") + xml_returned_1 = etree.tostring(xml_returned_1, encoding="unicode") + xml_returned_1 = re.sub(r" xmlns(:.+?)?=\".+?\"", "", xml_returned_1) + self.assertEqual('', xml_returned_1) + + xml_returned_2 = excel2xml.make_resource("label", "restype", "id", "res-restricted") + xml_returned_2 = etree.tostring(xml_returned_2, encoding="unicode") + xml_returned_2 = re.sub(r" xmlns(:.+?)?=\".+?\"", "", xml_returned_2) + self.assertEqual('', xml_returned_2) + + xml_returned_3 = excel2xml.make_resource("label", "restype", "id", ark="ark") + xml_returned_3 = etree.tostring(xml_returned_3, encoding="unicode") + xml_returned_3 = re.sub(r" xmlns(:.+?)?=\".+?\"", "", xml_returned_3) + self.assertEqual('', xml_returned_3) + + xml_returned_4 = excel2xml.make_resource("label", "restype", "id", iri="iri") + xml_returned_4 = etree.tostring(xml_returned_4, encoding="unicode") + xml_returned_4 = re.sub(r" xmlns(:.+?)?=\".+?\"", "", xml_returned_4) + self.assertEqual('', xml_returned_4) + + self.assertWarns(UserWarning, lambda: excel2xml.make_resource("label", "restype", "id", ark="ark", iri="iri")) + + + def test_create_json_excel_list_mapping(self) -> None: + # We start with an Excel column that contains list nodes, but with spelling errors + excel_column = [ + " first noude ov testlist ", + "sekond noude ov testlist", + " fierst sobnode , sekond sobnode , Third Node Ov Testliest", # multiple entries per cell are possible + "completely wrong spelling variant of 'first subnode' that needs manual correction" + ] + corrections = {"completely wrong spelling variant of 'first subnode' that needs manual correction": "first subnode"} + testlist_mapping_returned = excel2xml.create_json_excel_list_mapping( + path_to_json="testdata/test-project-systematic.json", + list_name="testlist", + excel_values=excel_column, + sep=",", + corrections=corrections + ) + testlist_mapping_expected = { + "first noude ov testlist": "first node of testlist", + "sekond noude ov testlist": "second node of testlist", + "fierst sobnode": "first subnode", + "sekond sobnode": "second subnode", + "Third Node Ov Testliest": "third node of testlist", + "third node ov testliest": "third node of testlist", + "completely wrong spelling variant of 'first subnode' that needs manual correction": "first subnode" + } + self.assertDictEqual(testlist_mapping_returned, testlist_mapping_expected) + + + def test_create_json_list_mapping(self) -> None: + testlist_mapping_returned = excel2xml.create_json_list_mapping( + path_to_json="testdata/test-project-systematic.json", + list_name="testlist", + language_label="en" + ) + testlist_mapping_expected = { + "First node of the Test-List": "first node of testlist", + "first node of the test-list": "first node of testlist", + "First Sub-Node": "first subnode", + "first sub-node": "first subnode", + "Second Sub-Node": "second subnode", + "second sub-node": "second subnode", + "Second node of the Test-List": "second node of testlist", + "second node of the test-list": "second node of testlist", + "Third node of the Test-List": "third node of testlist", + "third node of the test-list": "third node of testlist" + } + self.assertDictEqual(testlist_mapping_returned, testlist_mapping_expected) + + + def test_excel2xml(self) -> None: + with open("testdata/excel2xml-expected-output.xml") as f: + expected = f.read() + for ext in ["xlsx", "xls", "csv"]: + excel2xml.excel2xml(f"testdata/excel2xml-testdata.{ext}", "1234", "excel2xml-output") + with open("excel2xml-output-data.xml") as f: + returned = f.read() + self.assertEqual(returned, expected, msg=f"Failed with extension {ext}") + if os.path.isfile("excel2xml-output-data.xml"): + os.remove("excel2xml-output-data.xml") + + @pytest.mark.filterwarnings("ignore") + def test_excel2xml_sample_script(self) -> None: + old_working_directory = os.getcwd() + os.chdir("docs/assets/templates") + with open("excel2xml_sample_script.py") as f: + template_script = f.read() + exec(template_script, {}) + with open("../../../testdata/excel2xml-template-expected-output.xml") as f: + template_expected = f.read() + # remove the resource ids, because they contain a random component + template_expected = re.sub(r'(? None: - """Is executed before each test""" + @classmethod + def setUpClass(cls) -> None: + """Is executed before the methods of this class are run""" os.makedirs('testdata/tmp', exist_ok=True) + @classmethod + def tearDownClass(cls) -> None: + """Is executed after the methods of this class have all run through""" + for file in os.listdir('testdata/tmp'): + os.remove('testdata/tmp/' + file) + os.rmdir('testdata/tmp') + def test_excel2jsonlist(self) -> None: # check that the output file was created excelfolder = "testdata/lists" diff --git a/test/unittests/test_excel_to_properties.py b/test/unittests/test_excel_to_properties.py index 9a3a6727e..ebe6e7b00 100644 --- a/test/unittests/test_excel_to_properties.py +++ b/test/unittests/test_excel_to_properties.py @@ -10,9 +10,17 @@ class TestExcelToProperties(unittest.TestCase): - def setUp(self) -> None: - """Is executed before each test""" - os.makedirs("testdata/tmp", exist_ok=True) + @classmethod + def setUpClass(cls) -> None: + """Is executed before the methods of this class are run""" + os.makedirs('testdata/tmp', exist_ok=True) + + @classmethod + def tearDownClass(cls) -> None: + """Is executed after the methods of this class have all run through""" + for file in os.listdir('testdata/tmp'): + os.remove('testdata/tmp/' + file) + os.rmdir('testdata/tmp') def test_excel2json(self) -> None: excelfile = "testdata/Properties.xlsx" diff --git a/test/unittests/test_excel_to_resource.py b/test/unittests/test_excel_to_resource.py index 16e583dfe..8a74d4d9a 100644 --- a/test/unittests/test_excel_to_resource.py +++ b/test/unittests/test_excel_to_resource.py @@ -12,9 +12,17 @@ class TestExcelToResource(unittest.TestCase): - def setUp(self) -> None: - """Is executed before each test""" - os.makedirs("testdata/tmp", exist_ok=True) + @classmethod + def setUpClass(cls) -> None: + """Is executed before the methods of this class are run""" + os.makedirs('testdata/tmp', exist_ok=True) + + @classmethod + def tearDownClass(cls) -> None: + """Is executed after the methods of this class have all run through""" + for file in os.listdir('testdata/tmp'): + os.remove('testdata/tmp/' + file) + os.rmdir('testdata/tmp') def test_prepare_dataframe(self) -> None: original_df = pd.DataFrame({ diff --git a/test/unittests/test_id_to_iri.py b/test/unittests/test_id_to_iri.py index 7e506f78f..8adab429d 100644 --- a/test/unittests/test_id_to_iri.py +++ b/test/unittests/test_id_to_iri.py @@ -10,10 +10,18 @@ class TestIdToIri(unittest.TestCase): out_file = 'testdata/tmp/_test-id2iri-replaced.xml' - def setUp(self) -> None: - """Is executed before each test""" + @classmethod + def setUpClass(cls) -> None: + """Is executed before the methods of this class are run""" os.makedirs('testdata/tmp', exist_ok=True) + @classmethod + def tearDownClass(cls) -> None: + """Is executed after the methods of this class have all run through""" + for file in os.listdir('testdata/tmp'): + os.remove('testdata/tmp/' + file) + os.rmdir('testdata/tmp') + def test_invalid_xml_file_name(self) -> None: with self.assertRaises(SystemExit) as cm: id_to_iri(xml_file='test.xml', diff --git a/testdata/excel2xml-expected-output.xml b/testdata/excel2xml-expected-output.xml new file mode 100644 index 000000000..08e4a7068 --- /dev/null +++ b/testdata/excel2xml-expected-output.xml @@ -0,0 +1,117 @@ + + + + V + V + CR + CR + + + RV + V + CR + CR + + + V + V + CR + CR + + + RV + V + CR + CR + + + + Homer + Ὅμηρος + + + http://d-nb.info/gnd/11855333X + + + https://en.wikipedia.org/wiki/Homer + + + + + This is an annotation to the resource Homer + + + person_0 + + + + testdata/bitstreams/test.jpg + + Iliad Prooem + + + This page shows the prooem of the Iliad in Greek manuscript 1397 in St. Catherine's Monastery on Mount Sinai. + + + © Library of Congress Collection of Manuscripts in St. Catherine's Monastery, Mt. Sinai. + + + artwork + + + + + Dies ist ein TestThing + + + test_thing_1 + + + + + This is an annotation to Testthing. + Second comment + + + test_thing_0 + + + + testdata/bitstreams/test.jpg + + Dies ist ein einfacher Text ohne Markup + Nochmals ein einfacher Text + + + This is bold and strong text! It contains links to all resources: + test_thing_0 + test_thing_1 + image_thing_0 + compound_thing_0 + partof_thing_1 + partof_thing_2 + partof_thing_3 + document_thing_1 + text_thing_1 + zip_thing_1 + audio_thing_1 + test_thing_2 + Another text without salsah-links + Another text without salsah-links + + + https://dasch.swiss + + + true + + + JULIAN:BCE:0700:BCE:0600 + CE:1849:CE:1850 + 2022 + + + 4711 + + + diff --git a/testdata/excel2xml-template-expected-output.xml b/testdata/excel2xml-template-expected-output.xml new file mode 100644 index 000000000..ec03ff107 --- /dev/null +++ b/testdata/excel2xml-template-expected-output.xml @@ -0,0 +1,264 @@ + + + + V + V + CR + CR + + + RV + V + CR + CR + + + V + V + CR + CR + + + RV + V + CR + CR + + + testdata/bitstreams/test.jpg + + First Resource + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + + + mammals + insects + + + true + + + #00ff66 + + + + + + 2761369 + + + 0 + + + res_1 + + + http://test.org + + + + + Second Resource + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + + + birds + + + false + + + #ff00ff + + + GREGORIAN:CE:2015-01-01:CE:2015-01-01 + + + + + + 45.8 + + + 2761369 + + + 12 + + + res_8 + + + + testdata/bitstreams/test.jpg + + Third Resource + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + + + reptiles + + + 3 + + + + + Fourth Resource + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + + + plants + + + false + + + GREGORIAN:CE:1973-12-01:CE:1974-01-06 + + + 2 + + + + + Fifth Resource + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + + + physics + + + true + + + GREGORIAN:CE:1908-03-05:CE:1908-03-05 + + + 1 + + + + + Sixth Resource + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + + + humans + animals + + + false + + + GREGORIAN:CE:1886:CE:1886 + + + + + + 200.382 + + + 3 + + + + + Seventh Resource + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + + + amphibians + + + GREGORIAN:CE:1849:CE:1850 + + + 99 + + + + testdata/bitstreams/test.jpg + + Eighth Resource + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + + + physics + + + #ff00ff + + + 6 + + + + + Ninth Resource + + + Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. + + + plants + + + true + + + GREGORIAN:CE:1948-02-26:CE:1948-03-24 + + + 7 + + + + + This is a comment + + + res_0 + + + + + This is a comment + + + #5d1f1e + + + image_0 + + + {"type": "rectangle", "lineColor": "#ff3333", "lineWidth": 2, "points": [{"x": 0.08, "y": 0.16}, {"x": 0.73, "y": 0.72}], "original_index": 0} + + + + + This is a comment + + + res_0 + res_1 + + + diff --git a/testdata/excel2xml-testdata.csv b/testdata/excel2xml-testdata.csv new file mode 100644 index 000000000..7f58f560c --- /dev/null +++ b/testdata/excel2xml-testdata.csv @@ -0,0 +1,56 @@ +id,restype,label,ark,iri,permissions,file,file permissions,prop name,prop type,prop list,1_value,1_encoding,1_permissions,1_comment,2_value,2_encoding,2_permissions,2_comment,3_value,3_encoding,3_permissions,3_comment,4_value,4_encoding,4_permissions,4_comment,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +person_0,:Person,Homer,,,res-default,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,:hasName,text-prop,,Homer,utf8,prop-default,,Ὅμηρος,utf8,prop-default,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,:hasIdentifier,uri-prop,,http://d-nb.info/gnd/11855333X,,prop-default,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,:hasExternalLink,uri-prop,,https://en.wikipedia.org/wiki/Homer,,prop-default,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +annotation_0,Annotation,Annotation to Homer,,,res-default,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,hasComment,text-prop,,This is an annotation to the resource Homer,utf8,prop-default,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,isAnnotationOf,resptr-prop,,person_0,,prop-default,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +img_obj_6,:Image2D,Iliad Prooem,,,res-default,testdata/bitstreams/test.jpg,prop-default,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,:hasTitle,text-prop,,Iliad Prooem,utf8,prop-default,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,:hasDescription,text-prop,,This page shows the prooem of the Iliad in Greek manuscript 1397 in St. Catherine's Monastery on Mount Sinai.,xml,prop-default,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,:hasCopyright,text-prop,,"© Library of Congress Collection of Manuscripts in St. Catherine's Monastery, Mt. Sinai.",xml,prop-default,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,:hasCategory,list-prop,category,artwork,,prop-default,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +test_thing_0,:TestThing2,Testthing Zero,ark:/72163/4123-31ec6eab334-a.2022829,,res-default,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,:hasText,text-prop,,Dies ist ein TestThing,utf8,prop-default,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,:hasTestThing,resptr-prop,,test_thing_1,,prop-default,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +annotation_1,Annotation,Annotation to Testthing,,,res-default,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,hasComment,text-prop,,This is an annotation to Testthing.,utf8,prop-default,,Second comment,utf8,prop-default,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,isAnnotationOf,resptr-prop,,test_thing_0,,prop-default,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +test_thing_1,:TestThing,First Testthing,,http://rdfh.ch/4123/54SYvWF0QUW6d7ClGg6ZIw,res-default,testdata/bitstreams/test.jpg,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,:hasText,text-prop,,Dies ist ein einfacher Text ohne Markup,utf8,prop-default,,Nochmals ein einfacher Text,utf8,prop-restricted,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,:hasRichtext,text-prop,,"This is bold and strong text! It contains links to all resources: + test_thing_0 + test_thing_1 + image_thing_0 + compound_thing_0 + partof_thing_1 + partof_thing_2 + partof_thing_3 + document_thing_1 + text_thing_1 + zip_thing_1 + audio_thing_1 + test_thing_2",xml,prop-default,,Another text without salsah-links,xml,prop-default,,Another text without salsah-links,xml,prop-default,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,:hasUri,uri-prop,,https://dasch.swiss,,prop-default,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,:hasBoolean,boolean-prop,,true,,prop-default,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,:hasDate,date-prop,,JULIAN:BCE:0700:BCE:0600,,prop-default,,CE:1849:CE:1850,,prop-default,,2022,,prop-default,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,:hasInteger,integer-prop,,4711,,prop-default,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, \ No newline at end of file diff --git a/testdata/excel2xml-testdata.xls b/testdata/excel2xml-testdata.xls new file mode 100644 index 0000000000000000000000000000000000000000..cfa8219c199f949697bc06bc3b17ccecf3ed17e7 GIT binary patch literal 54784 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 literal 0 HcmV?d00001 diff --git a/testdata/excel2xml-testdata.xlsx b/testdata/excel2xml-testdata.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..9d92366a4efbffb6e5edec0259b13a956f5f5f1b GIT binary patch literal 43464 zcmeFa2|U!__dl+^M2fO6i6~_kLL^e6lu{xl*|RTY9aAclE&EPF5|WA`JK2(bZ!q>H z%h<;ZGxNKnK9yGE`+IyJ-~a#jecwl~&g;JJJ?GqWpXc0r&+9es&MR%$xSMP<*;X<# zGIlcLF!G|yIx@1jO=M&=WLwv3%2}A(>YLj_E;(50+i3CGo0;x?x^ex1CuHkE`~Us# z|6vQH1Xi6Z5!iVWeR@f*pZ8|R>*HIGjJuXo^IVWF^yc2_bP|I`4*Ea4OVB&yfQ_Sx;c(Ux6I*+^9{UZBhxi{1BBIyb5 zmt}{xRVpt`^6(UQYgQMeKzLKM5{6D{95B1^oH@R_bx-HY2ztFOO*CecdyQ0&LM zi(aq}FR2}I(X!z)Cwq4I949c@J8S#w)R&+*XhYF|mi&6{DST{4(Vf_qIymm!+p6r{ zto=@tm!k4EEHzDZFI>BqHgrR_wNM2Gh)~9 znHLo_aV#uJ>{^lc>)uLjdtDk)PAiGoOh!f^Y#=+o!fi9u1UO~^7L|clZU=4)vDP=W z;pZb>|JPmrANJ87hMwYoR;5IM!VPtL$-M&I-1&It2_-w((>d&yT<$1#P-S?>aWZul za5L|`#CUJRDOjG%(g#GRl=tTf_W53s6i@2ik{p?Kscs=yt6N)mxU7Q}tWtX3@4DSQ z*E}C~>WssI+j+h`$yv{1l`4@O3XN0p={sxrt}t$+Nnm!PJr;IXqwMU}t{V$4*EOHG zIG*bE3NH5Kb3knbtTp)A2&Ih2QRT6QusszvHw@cfI#-yo&tVwVE*c#*esv?-T4J9A zM9;KvMn0l|2ItIsF0w*(p9uAWQ0=Mm{ZHm#8gE6Ws-hg;i)|}Ou9&QJ^<;bA1y-f?vvVG7)I2P>+eFBD(Q=<>1x>kw zypS2K4r}Xu_T&whQOq}{jMj&Hhw#n3;-T=>8pC9>2hKurBh?C)!2ah~1kKYJFg^w7 z$pxDwDgX_DpdYc5qGo0hC%zN!(tz8tV=mbwf^1!r-k$ZtJhxQT;0TwLYnJ46g^_0A zmzwctrht%V5sf^EyaVaFPD_&?KMI$WCf>TJS$plpnYT0%(KLu&GrmVJpy6#03*p^u z!3`bx4#>5#0~GG}3g@f-X5r!ROUY$8hyZ%b&=x}W|*sY~D%pF~%2<%tS6WAzW* zDH4@eW6#vwx~S{ElT9q;<~ZdEPL-mao+pE5#AMB>XT)pa{2PbXt1~IQkthF8K&-chAv!KfA5VGH8abx7fIF!1J#FZ z?vyb#oWfspJqV?9owK4iCug1BUmH}}6CE@wQ}F?*i?O&KxFp25$(T~(;a+<-`I-3+ zW+%t==Qmy8elJVkP4KsbDmQ1U2`4l@ZO9F6(^Y@82L@+fv~$!Ak7_jj`43g(DAN^FdNi=Ui}IG z)a0pDGP)1A1km|RDmyBTsxg7=FB#<=E99)B#!-XgoT;i_Br84uU@wdX%O-R)cC z5GUV!dErh=oyOfR0nPFAJ0QwA33VmaQX4;I>gh$Z^KX~nw6>$Pg=FLk=x^M+C7E|O z?4-oV<_)jban9S$JeH|3FcgutL#HLtF@&)HQuS zN$hkOlLRpAT%3PGn6zBMqE__m!*3Hy&bI%*fT zT1eM=l?OcRy5z-Js~#*r*=CDkZY-3dIoeazwzL$eq1$+1Pw&yEu6=DCy$#L*OLIIt z&B49pd&)kLZPhLtJd-rnlv!o^fPn|~p3x@s;C;Q*vRC-Vt$77MDCIr8fUt?IGw`r{ zBOCg9jCrS9e{1!p4;wW(PSW0|&PYvHzNg=4r^LspasG)$w)6+D50nXpm#n$tS#h#E zIKp-(9B5+c{`B;~X*XEBWIDI#haIQyI5YS&wfWfD&Y#it=toe?(3nXJKO`?wGuFFN zqGpOceku7nHpsKU;YDI$@%Rf)o&ttik75tQPl|!tc;aIVZJ!*y;%oWd@=Y)6h1ZYe zsc1cRy)$zgg=S2$iU%%NUBt`cqT%w4zIghDRK{&(pOx?~{TMK@b zh|Qb*w=!$$R9+mWH(NB$*<50obfsMSIPF>Q>mUVF8-bm>|m0oFiPL!gXX$yx``JNhG z;aL-njKHoFH5*&Gk4jX8Ep}Jr(8OfNU8u!;GM>ej*NvE^5cp^E_^EET$Hrg#yZ~d2 z!Q&e_X0JJByLp-4V_vvvA#T+x`l@6xQ0ENWt-SioG|Tc1D5yK^z++TI1BWSsei)K=SMn|RU15zhhH*Udsl z%WgWp!4~E1bQ0N{d5$`5AeE*2FfKnquadz$FjD^UWl=5N{hXN1A6hX+n`g2VIg)$d zCf_Z#Flf4{-YtIRP@-6E3-oyGCoRV;s;tUtkp`yGp_GVrjv2Y*;_r0v{W}$Eg7H=* zwCd01xCdo5b4;Amxm4N@GuN@XGF^{pKs?TcP(7!4evINap|Epv$!+$1JGS826cXM< zr*GFSS8J1V{J?MSi$8;6NUC)aNIS!Xl@F87OR7r;3QSEKhr^H`z6`7iE!sAS(Rk(eI zItYj)8tn`FQ>wxzi?i`>$R@T#G3ZZKBHX=}ril4Dt~!iU>lBAWIe!CZf_wIpydD)lz)XCGI;CyN1cZIViD z?%`I`EWg_W6qXLY>jXiAaTgic?yo_^NXJ@V@1m`>vAN-L>_}0swnm8V%s1ph-I;!@ z&2xcMzx}eFKJ(FY&FP&YncQZ4j2;^mIXMg0Kha0GbS>_Mx@vzKQD!T2aLZSu^Pkft zYdYC-TGyH`2YDkMHrd^5cJ&Ft*VAZ>qiDqSBa)=-Ei*1$%lJ#AAJl7+AHpNl>NowrB{t3 znElT_$_b{0t?$T_qiXiTUkj*rwQiep+o0&a;YfUC;gyoRX-L}LvhnYdJrwyS19x-n z@~q`b+V?bG6{Q}Y5=a~T>C7a#;I(%Qd~$=-n}R-EVpBG^yrB8WwX$I-vyfKzp5n(U ze}U~>wk7wy1$y+hE4OW=$<}$Q5NgDv+_bUrR*5Z>&O5~QcV`85UP`?mpT&0=HmL5? z5q-J;a>lGx#Fi_`vxSc&)mt~N8xEpAr}kLsa=e`B(boB*CYqF#tx9)V4p^QHVwnp( z=)Vho(q{KQUD-pMuIg#D7Pv0ueu%w)pG#?-;ZqTlhV=J^^M@uABk)hGJc|p@K+Cd< zKNUYLmDHY6PEw+_2|liG{fsuqq!M-Ul)m2`1B-E%OX`RV$Z7wrt+)A^n-wIXk;gL| z13#%|JgJ$1KCf2vJc|fS=%`ii-BMK4rM>&4%)3{}4xWAVd^HE&@Fccy#WvBjMAz58 z-M6iDW_=pcdFb{ecIRPuiq^y3r_Nbz<#^cb$>1DPo^&NSa_?zIZO_oGlZ3@Z^kTsT zVQG34Cj)UY@wpISb7s?9QI_Hv@gwcrfkyXs?+YA(Jc=FPrVdNqS<4_hRA+TFnf2PF zIKLG;%Z6owQjRA*t7@mTGEp>BTRw1>)hHXw| z4ocgd4$yHPn=V6@4ClPknfY^ed6`53kK;@ZnvK*!EYZ3zYsK|5$o-! zueHN=Adj}G>5b{5ZEj5GHEX(W_IyrGJIcQ`(=B1db*$-OUe3Jr zM-A1BA6vVN-=oWteX8_9z$~Xjy0Xh=re=NQ`rgr~ zsedNuNZl-4eVS5Y3kO_%-HcsXX_T=}$8mR8slNH@pm%XpTta^F4w5xE^PLKKZS-_v z1XW@b+e?{Qf82#B(NQRKt+Hw8MJ(Qhe)ehhr9naPj=FpyGIE~ZKXQP9;IO! zR)`Pe31!~Omyl*WuA*k}vN^=~?ZA!f@btP<`xaVcO>V`#3ZQ>&FWomI;zCkU+6BnN}2uf_~Rol z8B7y@!y8xS*y3akls-$`c2z<}^l0jK znlcf$a}FYQW2epDm?jqcuN$Dd7|BH0)6u{8b9-Vx!L&8=UTmCj?L*V0U9qexGsQRQo&F z6Bx?;T?F=TiO>~D9<`q_UbL6QBlGZ5nyCh!$!OG|b;h+LG7w(6vo=T0q9!I#gN^Mc zE)zg*VkWElN?XlMThkcxa#TZg8q6Yms(+k`dsqfBUzJo5H-qho8GqRsiO3BRVP$wi z)5%W_^k#^x0(dF~TIAc5f+~k18_G1p&~-hOj* zY&sNE5Ql3qFHr8pjt&gxu-n)~s3gwluq_UY=9oU__qTWqDX?XVH&F|_>EBKT|Ja8O z#$+H=2PCQ^MwV1WKdNEeFh@jlqswnojgL4NWCY-QYGz_F^q9FtK^Mz+*@{TMf?nJ5 zxVw^CwoF5<=DJsRjPj`5%||RT=0s6`zS(RR3ehboeWk^ChHkgDhgnA3?QTWxz4~_Y z?jDyQuXaDV>Ykq9hysK-_iYR{pG<3cJUfoZV^^NU7^e$IMzj)a7llxy^*$ zG>>kpvsz!!G!ydT=2r5HcXeLc4&(2YUMdvN-Mo#J*Y?O^9=@j=-D&B`H(t~dY^9We zZX@Sys%4?3(71k1X7Ag|APHRId2)(F=VXQhT=O3#vyCa%YBx7iEyUkR+w4NQbhxQ@ z9pmiP=LJ~Cv8#Ju=J)C-ox97paCLac^{ZBB<8H&d&-uGAelAL07qjVE{u33?u(U_f zg(^-x0otC+*Pt*-jDf_I}=uWqg#+HTq5bGw$E+0YkRV&CsSZ@w%20gL3 z?1B?>o_S)?(s)I9?jw+z<#;5KZMNDGd-HR|7AS2mnOmzE754D^!LazdXmUqoSU9!0X>@O zW>dwn4I+~>muey5o9{$_KBU}uypE>mdidu1(V>iuA_BAWmZ8tqJ>`B@aa{21LE1)< z>$CEE7{s3)tkezP{5U#vpA+|uhX-CiW^alNb);0ae;f|CNrJN{>`*)+@m{3jB;}qH z@q@ftLNpED$E&?7ubmbnI~A|^*qqGVMcF|1-7fWg)%R`X9@}uHoaTA!p%oOXx>b(CLt+!srZ1w-_wU=RL^>LgpkA}EXTMbA!5f=mNKl9Q+;=EV9Kf zICP&Z=g2kx?BMO^Qf?{M@kNJfGWG7a8r(0NXTR^!eP_jZsA3&|^nrcs*Qt%K`-g>8 zHTN?syWXFA4@^nd&%A9{fI3aU$3oj9U^4WSdrg&HeWv7hZ(|QQ%@OcKPwImFi4U9& zA2^K|RNh}iIDZ__ur}b5t+v0BZ=C*~+4vzCvcC(LY#mY9Q^m+Q#m6E59kIsLqsG+o z-rR#ax^9E(cMsj9*7N4>Z4p&!WLbCg)QL9EmNrh|Bb~CAHhS6@n!US|qEFv_Gj$0P zU)XNH&*P)g70Vs{cCd{#Wi+zHsvD2Ij;e|qa#)H+Ki{Y8v%95zYos^#v(w^CJ(1qa zUGBfx)v#PSsPmszfz~WllNK7wEh|Df64Z-TXFAz{nD=`aY~pv1s^83C3ti~^@e>9voZi_yG-&>*C?D`)^+Q?>ZWUVdLzBBCwX!YCYe2ZCb@Asim31n zR#8jQ;-gGNW;+41_&2-6nN)WBS&E*J_)=;7Y|wSXkm<%Mh?Jmwc+$v@yZfn?u74GA z0EqZvj!>csx0dB*6IJ*@n`<=7me6FXQK+~jNK|q!=$4hSOtJ&AZSEWSh1u49>B?VK zKS1Pr?GTZ}RwAj%PbBGyq?YhE@{6>+4a(I6VM<>VCz_@o%kZCpbucm#ElwQcn8Y`- zLxV^zkG$V;ncVeo|B?R;9kh*Hwg&~UNAWV-Wd>g=HxiBXGX0yqUcR{iOgAUb6X~6Z z-M42MGQ~v5Tmsb_{m8+%4+?de92zp+4W6m#<+fgKX+Q84J_C{7pzS`7`)?}QbWf#9 z6Z;Tn;SAuK@B;JVF|Z;R>^1w$y=O!*MDy|r zo-A%ZF#1)Ny8OUbSrzt%as3iZ4*<{3Pk_G!)40rIFB{QMFir+IU_z#Yrmd%u)0;}y z>sQe*A{6Pxzp2N^Rc751U1t57U@v4HMO5}FQ1-$Xavu>zA5xbMK(y6|FShu|yv;DK z|{7~HZ3UnN_f003Y8uyA7tbz68(hi1zhD zxRO>h3wgU~Ew^WF>R4~S4t{49w^p86Cp-3;?}B@1zUS?w{paoe4r=pl@cWv@ygk8| zN?HHDWq^BWmhtulTUBN0cLhMS6+KBE4v3{h^>bT^wK$ z(+g(?@MV1ihk{@W{dD}0Bo_ zZ;W(xOVwhq3T!Uaxdk1BlqSbzzmlQLcjj|myk?J{NU%os>TY#HR&kuN-iH*M8O}xm zH%FoUm>l*a;giD*{fb=`XclQK)wE)Mg;*itdic^24Ab=ZjA$qqOacrUta0>tMTO(j=u_DUg7u%%QX}$1j$9xat*~wlH`(O`G?Y#0~H9(hZzy20$EK0iP^Sj5eGl@u`f__rnsY9{ zbCCdkCb2b=*iZ|DJzdqv1CfSc5JSZ|cKksDVic8?evuVn&N9_-_M;e=`@UFQ3Tt)% zYG~@Y$~5oNb7Y`}ob`0|+;b$Kuk(gn%F0SeKmj{anplhOXX~1_9?b)lcmLzzst&y7 z+kYrkd5f$l2KXjW1`c7ZG%Z2Nk7EP3cQmgTtbW}Fi~I`rUClDS!hKh>jIVItRV?GA zt=quYSGezLmhlztyNYET@rd-Y(tTI4jFYx*17Ba^zN=ZrnaoKqlIEnBE#{;bZgbKL zo%!!es}r2ctskw(03$qJN<1me>|c;3zzKsaTtAJsU0;Fq3r?gtj-7ek2kl{FQIlcW z_BxMmab#xWl0!Rx%yy(SU00q{(%~4vP%f}O8(OnpvE~4-dCz#-Y!WQMNHILPvh@cz z`&rz97l~j@rp5XZLuLYJ@zF~qn%d@hpz`SBFHT8xOk$a!oXz4huZs6UyuMg&R(yFqb2B?EkW9lX$mls&iQYMgb&j(j!Fx?)GZ3TIuhBPVswZMhm}U9lryg|n{Q zk?(<%URL3(D|Y0h?zt^j5 z&5Z;)?hL})S!k|g;{M!A#Y+40FEuFbFSztUDFJ`YVUgkHVs>wAbBqj&MxJzwgCzUF z%+Sa})rUUaLozIOLXfVWc|VgpuaA(Iw{lZ#@-Mv%`4GZC>O9vlGp95V5%K{JZqPOZ zGv)8miy>EVld2Br3H?~RDg-rZ0y;%+i5b3N6b2f&A?2>?AYIi>rW7aUkFMI_F4F^I|LD102Xlu&SzR zbOqRg|5Tzzdow$77N=Y+HE)0Z`Qh?CNsBqL@d}q;&6#|K%dh54zUo8x*Oh)y|%LRz2Tsj715L1{|u8-zZX zDUR8`Xwy?QR1B|uV29G?LGwzxy+Y?nS#xqvLxp{#PrU3`(5{SKl{&C9IW@>w&Kq!f9snooEc&T`O*kk?~8B+NwZhT)uzvub8V|})fLIL)wt@4 zL;B2_)xZ=MRB=vR)EE` zQ@DBie(&6X|76L=zGcaBzGunC{x?guss#A|X318S0AE9n%#!pR8NzA}IWkMqb7YcM zf5?$p<`A=FIr#+jK0;cfs*47;q)}Cyrze3L!ZVWK58)X|P=&p;)cCOYsBfk=x_l^& zx4;4G8Hs~dA$D3h>$NBPEzWx5Rln%`~L+0Qg5D3*Ve4ddp# zs3nFeb7~@Hy?6RXBf8B*&*fVzT|icD!Ei$QnTYrJ@FhyjyhDU{Kmj*lGB`vNMQjA` zkMmhhYg=37EuWpgSUPCGmEbgWnlBkLNn(Z!9IfY9@h0ns*Ip|Y+f}^STHTwh zAJS*5dz1A;`fPP?vVO>ZuUKqX@nUOrZ?b+!pQUDzUREr&t9h}tx;I%rWWQG|wyW^# z%$_xrzD$zK?d~;{-rJ;?p4-1Kd3KA}MO_HDDhddNe#DJ*p&wG3O?(<;#I{*4fHXGM zs~ND3zRQ8qbb%6{Vbcl-{pBk7$s8D=gifWZ34JV6s`dp!H>`kCx;5ksA$wZ%JhRM| zE+3yz{F7`tRk7lR3&^W&Sg}3dK{ds zhdGeJzRrj6y!>KFj=szew!l*?%TAwA=ZmmLO_fU!_vC0Fmsw(>tz!{gfA8zNaBEau zxycVnI+9Xv#K!;s*{_eo8gE-j`s=wnJb;vTZ%`i#V?RqCeUU zdeJdB`cq?bdclVS8{W}t;nVloytv$?mewA|lxsGbS%lbZv1+0eL2IFzYc`chG9j5e zKABm9*kM6BQI24>aLOH@$|RZClFKrAyAZL?f^_0NVwc6KT$ZWZBoo}ZQIofe5!);=O)|l6?^J}?-laV`?+T~O&FdcD zWbwUfx)Koq%Ogx8Abq-5T^vH-F;&x!#(4g2ghS(!JMs4md^YH=vycuK@v6^xRB2bF zXHlh9GM@-=M6IC%I?qBTcAzuDTqaI`Zo{=fs!0rlBhBO>|ELn%6^prQY-+w%!9>nn z0t(!fU{hVL6-*?~C8QN-=e4%lX=&htFkMsK_O?<-qgj~B7aG;O9GA8o)* z(nhngaU@j5DX<;EKCeIJEq!zLLYyxnv}u@xs`z?tZ)xLM#W)p4Xv1)TLW~HOgesHS zt8ulA(3ash3Nc5pDfNcl(yOmx@-`QFS3j%}M>^3wd^t|j+x*Bok_k!g36Xb=!{_2i zCt8QKG!LuAUGdgF;z%;V&j_s__Edm~V4v30dFLC=o{almHGOl| zOd2zp+#fK-Cx+>7+&@!kO@rxw<~1grYttwZ(~*zvnbWtJsXRL?o{#Q=xGXdxaVQw^ zx640SLBkA=sT-f`m&HY8KN_g>5KrRcdS$8--(UPb z%~asd3imFiCFBD)yg&!je-CqiYOllB2-PBp-&n!zH5FJ1E={!7#Q5}h>U6LmYnf1) zu6mjM`YQT~5Wy6F-39Hk^LbwE4h(WARb{tUeWSO|>+6j$8=uxsZ`VTB|{bunkcBz)kTl)P5i77g+yWLlAKWXv`ab0^h&UuYE*Ax<~XUef}m6^B}jH z8?wZM(aWtmD~++Obbj6OzFeZy*{o=yL{cnurgGegO&c=L6|>ay%!?W6gt{f|fchzT z5VC7Sv|B=?^RLeLTb|%e+(copUdY)T{Iv(F&%iffdGC3nk+jhaaI-;AH(vp z^jlsscN30{vGg6}*ORK&k7PD1w-=}~>+?ITE9Ir{RsPD$g1H2D!fj5U0$D}>B1Djr zUw2sRQ6+JQ^Ve{J68SVjG-4u%DdV$XwxWnZrICBtj6YWF^uCP+K6nnq9nixA{J?&F zJ=pnFad$lX2}l3-8)zMn2YleGtZ%HpctIlwalgv?uAwgoZk7>0g@J#^A+XS!D-8YyYjj1Fb&9ylw9c+ z429AmFQ@W*ef3lQ^sqxpmh`awRc#Vw$f6TvGumbLFRSQlEg1SLdHcrbYYo`@l0ew; z)%W5C$193p!7+Yadq|l;zE_VUL&>y|8?v^~+(XMvLFo_}?Pai6Nd>1{&f%NfgdjzUFtCaFR+Z=?D3`PbpD)SM`W zH&wZzayM|TUk$KA?ytiMQap1Ag!I2z_4j({`!|rwEnEHf&wrSf?+uhyHFSQRw(rls z&MVcttI^w^E?^?r#!Bmq*v2;1|KsvI!+%lt4>R#^vxUV69HjUiZdS_r8IVec0s=3C z&GVlC`^(6`G5ElluxAyBU&$aW{w=`WuTcJr%Y0$5I;^im&)+m0pqmy=b)GYEPQ|!S z@tq7rHXh2)Jz8Uh#&8v!U8;&m+n)_XJ(KBThB(-RzdM}e+xl`xW(ux=fb*`o^=TRe z?vU4iFrpP=ul*?%0w*{>L>+@5Z(V~(r1D3#qHbM9-qM0NUWH(@T+%FB?4UXMvz^!m z^e7su18wykRgy{NS2NSjIH#i)k!J9S9>%H^LJ!+qMepmx&?n2^*DlC9a3a%ZYKc}T zxQOIpdpQ5#jJ~%{h$%R#o z$xR_ybf@=av%QR|p8#qzK&ZL7q28!@^V9kll=5+EOdh{!AgtKz7W3j8KDpEbXVFjR5{hQXi+iv;LGc>!+8m(Y@d#JBRh z%wSo#ATWAVgV_wns|js>s9-_fq6>6H2RdQ}9WjQEI6#eO94j-YN6N8J*an< zSw-(C6wcpZ}IYz;08p z(5+6uj|{3gH-TXK-=QJV23!Z*KrsEc3I59(5*Y|97J+d8dmH~KV8tY?MMENk2t{%b z?n#*CHET%3k+SM5oqxgT=OM01Ln2TO{MP{FwP?8PDr*7q-!z=r?!aXFNSF|NeMTh? z;YA}vn5~r|RCPiKggrxc!hK%o3ALG_Y^E(%T`tetaoC0uY(pQmp%L3KgKZd`NpP4q zTEwUtN*I)98}sI1F3&vUx42R3f*W+VLoN-zY4I72nVIbxZD}7=LoVs_jl$o3jW>ib zWC{v<8#+RK^VNz1Z@yl#wT6I{(2I@~m&%4}l%X#@Oi(Ej9MGzW=QDa38~DQj{?LFw zlwcbKW#}8>)0Jw~_ZotGP4x$ZH(`b#(|ifM0V@|O3jmpBu%((05zOV+rH7PR=6UHj zFqEK%F1!QSumv+W0zym!0A~U)tdbZwyqN>+bb=W44q)Tg1_6QQAcXw+`R^DQ_&0zc zw@Lv&G6;*e1wrn=K|`VqsyP!tkXwZb{>vH?8E_r!1VQfiHvUn-kCU(_4T%hd6-Pjj zBVm@;sv!|a5sGKTz-jgGF#36jYtoPiRAp7tfxIRSiLSCHApcE67j}!rvquK6h?@v^ z4b&E3;&}T9H6arU=n+;K`SIikerPrfLTWHf8D=RZQO2LyF+WBxvj|R6QMyYfs?4^O*IQY8 zK^z>aORa9@io5Mr-O#65Y3CE`-Bh@6VVWkcNx&kf(I{!%(|XknU@A9*kU$BhiV;}) z2nY!`?-;#pBC7`troH9jptZ-~#&1#C?ndAe6W>%=XN0t&s$azg5Eho3cAi-yFRc2hh<37q!ViT+u@k3(FOhC~L+syDCy2T6Yuune`Y4c4R~k%5lC z>ISg-{29x?(_k$cE=yPokpHIPy;2 z+&H=&<`{JB25y%o9OZ036h*MVDB+Wap8YhSS3#Fd)rOs|v6#rloC%v5UHpg~ovx9D z&E>!gTIXaYAutD2bOZrg-VDPnHU~A7r#@-e0|ExHG;Rajte$dVc`=5pjl%_=U~e*&^`j z1PY=C7NB`{9SENrz$&{%ZzZW1WWHlii5}9;JutQfCW@p6$@bTy(z#Y6^KA= z0Qt+$zw;sn5H=7%8n%EozKTSNKR5X*?$11oMs)ofwg6=OLB<~#tX|2V7z6}LgAn(t zLH@66NHkMu{A>}}IW1fKZvs{{Sc`^42IZUyR3HfdW}g0{!J0HAB7y556a?Xwp!(Mk z*Q6m4wt|Wy_rT7H2(#70WGx!58OVRraN$AjS+*b#LOfN4Q8GMIl6y@4#^!6MG5>e}))=O-X zkGn-)A-2Q2sjlgGmx5&XO?x-975}V|{ zvRK*VCviXX2#r4nJm|YRKR*ZPSg9}RN`7Zh%6W_2?UzA(f4*#xpEX#MhC~~1C0zs_ zl=#~b{K-aZ(vT=YP%+9i_Un}XJc2)Juoex0jR?LOIS8wMDY`?>hurw~BEtRlm$%VA z8I)-^^-qOGG5D<8vhKEPTifXg>Cr~pE61wC^^`n(yx^D5`sKZFe)=FpEin|RD@!U~ zJ_4*fA$5#Iu&$}2fokG)l95TA*EMw%24MLe)QJpTxGW@2I&_OI9C;p-*6^rI61L;> zgkdDB_PLuXdR_htOB2oX$-dFHXn*fH=Y~;OewUYA<3uSNio9%oZZXdx2iwV5I-$#k zE6?He!Br`mMjtNtXyq_G>u6+t+PCUWO_iKVm0ZX`#>=WV=2ddFRdNviw7Kf(6pIk| zd`(uYi$f|~upur>YPNB@?h1sLf4W4xCig_35==e*~hr)I5WD98q)_S>MTH6 zSl!x;zkRjqJ_UO=vYru2PZ$dI`1T>M*AVR89R;>BVVPauzUrk{nX_H@?T*Oc3Zs^> zZ9;v}8i>^T424!kEfZUk2{Fb_Bip!oZw1ncpd0t8S{)tC(;=6ZqB}_PgWqEcvc^wV8NJYJWDe1*N@dP zrNf9jGb0w%mSt*=L)b_k)-I^{dzhFOFI8%YyJ(dhVj#A2x(+|^*lb`xa#m*MP%Fy6 zerg~l52KUH--VZNF}4~hZ>>c)#2^bSak?S-1g$jRD4T+k*9!}yr)2OGhNb2O-F-1K z)-^+R{8urKQ&Y&p!nxp(R7_3#4Jq@BCG=FE zM7&O!GM1Gbq3lZ!cbhtXwxmc$N|=U$s$9ft=mulW4wW5r?pDHyFky4hG)I`SPgJ=i zYeS*<}lmC~-|4KgJiA5H0(~<|Ig5Q%3{U9-X>XOw%t`T~kNl zq)D(tSO&~548MTtJ1|x>SWCy~uphxp!Wh@ou~wN!ielL>{xF0;VfBw4`$5ntZoFJg zAZ$mJo7X^lFqXq-d#*BT6fDWrd^${J7UF)pN^UVkjy5`k$~i!1Le$2GpD;$yvX{Q* z!M4{4=c$FoESU=rFZn4I6ljV$V%y63$!l@t5C~yO4A!m{@($Bg~x`6D#QN zE5HG|lYe0WMaM=cnI0r8Zw6S=R#{&1p>)Z<10)>317svZ$$ z<}w>mzkrgs5_^MZjb$Z*lyvS>WXgIS!^ZS>wv1(|5pGtk&vZJXjOpPzjBY4n`WJ~h zr=#6s(YKzH3mU!U*;sF+wiSH8uu~rV$SIZFDqJD?1hCeFz2HYsMrt07A{u+Z2V}%% zPBJ^aGtnU4s2OpM;{cTM*gO;viy&OBo(NM^^N@`j$AQlUyMU9CM5x7vy+s4>oOjvF zXodm^T|HsQrN+Mbi}K$_v0CEt1A~Sx5b#JGDbXx3VV54zuVJJ_vuNxIS9Fr0_~%IP zf%yW<1abb<*ujTV%adoIy5|O%y|457y&kwZx$N?cf<4r{i}=}EOVguhev|5C@;<>~l_=f9a}rNmWq%)1G^;9TQ(Cn3UXmCynEr58KFTK=Oi0=m*B);$DbjepZk~ zosI?&Zp<0W&PKR-mShRiFu?T~%c{ZYjR}OJyTKVep-b z;qN+rRQ|2J|0^BMMLvDl0j4vE#X~`}9gK!ZcnS*}>w}F<>KF)pA9l$9fq2(!WaS28 z?uyZ@LfMJ`ipe<6r&w{jZ3^!>GdFTVn^EUeZgKwS8xHJu>{L^OuK1|qA$fzPyo z{IoJ3=6g8%KGNAa9Z4)rV1xKLV+R{N+4MD+ZmIASCaPWQ_SHBq7?y>VJ}_5Wx12)C zUl6Mb$k$@UnmY_HmL|BUN6+?6*MUq^Y1m@HEAg53Ys_F%#1N+O-d+L(AvzF*JdEjU zi@~PIXGdTDxkPVG2}(nEt41zst1JmGO`#}J^D+@HgaBpV4&@R{8Ojo@2nDiH;F3zo zV<}2G)aZGlk$yAKKSZ8y0RE$e{?<|WU=7$qt$@-uhQAA0#8|i7V3i%KL1P3lj(j1) z6s_E3WnA^B(G*TBAtkVl01 zxc5=u_hrl;3QoEprsfG6ehCLZ)Br!|3I&k2_z1KW8wA5=viaI^W#l3*IR?w!f35$; zCw`ouRfN3Xn{JgIzYgvDK)y^zp5XVIUxxOhO@3)coKTC80P4T7$xj_UK_~~i%T)%1 z|5ms^ri-)a`^dJc@PoEP1s%;_61J&a%&{wrqz!R##x03i4Nf!+4TLyjD-wCd45gjT z`aYh*Kjxpv?>EC3*MfijF-R88hMw#7T{OTOLhz#nFw6uRHSwYmUge9O2yt#gd-`H^ z91^~6k0v`bc_x>1UeX?NpNc$tOjk+=1f*1u;cf<*AY;ZIJNo>>D(po;q@ryUFeiw3 zG0A|#T5?I&!$#x2`LiWiXF<}t4&;agDf^_AOD2y|%FT&TR5l9C=I_`9@;kI3eZvb< zKN{U&x(fkZ?}C|K4^B~=Kze99NXPuzz>sSV9fbqdgX7Nc8U9t`nmUFl-UZvDKM&!L z64%u6V|*dlX8he~zm>SAjs~jTz{~zUO}|K7Q%B*0#Qe_cfU!d2S~^CD?ItEGR}aPi zNL*z{M*wekuo3b(MVLz?jQ14yj)1!~6`92< z2w@h2M`JAr3!_MUs%`;61=<}#z_ucBwKA~j<{Yc>nuvnplF?Yq7R2Q$xzT~x>gl@M z4n}aPx3FU(7VIqAR2l0VuHa80(a3_O8&j-NRFM4f1WY{I&f@)1UVgkd?15E#ZT`Zn zdB9Nal8*|%dzW*I&|+>|B;oPsv?_ccc4#0rEd;q3F>n>hM+JpT&ElptWDeJ2!E3w3 zf!G(-y}pML&eCv?skh2HH>9{hsCo?cPGEbKc!RNQ25gZOL*3#^vfj`zpg|f52hvDG z&w6?VCaUGjw8Gr1WWYi0p)o!BI-?uTn7%7fhaQ~g92wmPjOkqZ6F-5hK^y1?OF8-f4eV8(lAf_Wru~*@?&`ybvkfp>DP`W}?VlTx$?bwk|sfYl6a>k&Pp`i8AyWWR#`PK++ zx+Dm%@OkVBNnq|fnZ0{gmtGB8CPZ&$ms3qxMGpV68XL&YD{a`gn`|@LR&dWwRz$Zm z|J)Wbva-EoWHg}0-jv_gNZ(BV%T1LdXe=V}T9FH&Euy`flTVJHl4aUERi!I=~rW zdPd_mrROPpUSc9T+Qo6kcB&yS)bUj4_G15FNdEchW078Mp`4AuFca|uBF-wO1HE?X zZ!^biKfI)bh;gyrWmdVhyln$U-ZVCtP4Os#hqiR9n_pI;f%sh{msk1eVvZQva%Pr$ zU7@sx5>!*^Is6y2U2mLsf(o&7j8O)ifjK4Rs`Lo@ouKqOfV}b|eSd6vU&-qe9uJ09 zCs^1&P3uGpO0xDkHJ}erQ@a>p_Tz@=;QX}pm$~EzHeZ!9e?kz-R=)16UT-r!QgpLn z@78-7P6?0=&#!_1vkFe5AhRKHoso!Z(6h`oiP0eQ$2yefvCXkLS5RtIv|V)&tvA^$ zFMJ=gKDh*Y<;F(wuKVQnG}(jF*Gk-@3=$1TTc7NPXqL1pjccEW&zhWC z(=Pkd9=+mWADS+Td$M`{2Wu}prc54Fy{N3dPi#QLYeGnx?9O^sPQ~8JQVNy2t*^5@ z4;<}&kJ#V1mD<9Mn(KPjrGu1b)E3gD@8X|vJjxUhiVxDd{!pDsb>9BQ`%2}^r9)4e z#rEevom;<%(v{ncyXfH7ec_{=(->U_{q;xVk1r5Q^oHK zmM~Qz1%xSyxmUd%d-(IA)6*jXbV%sjkcuzMCj>J~=p_5Y^vRb!x+pGBox3|0r`E|t zy%#v~-v9Wn;oHwAsw&oNYwrDAeCySCF@jmWY*VgOfl2#2wyA7RpRhEA^i3VQUbpG( zB)VUOS4~b@Mk{v)%w#{zd06LXGQoEyg$z9;{NzfnYPi2WPOlhwm#5O1RH4$`+7Q z^zB$=)p{@LuVg*kknKq??04*ToA-Gsn@`IA74KcRcCerHv%9BA=j_`qDSu3k`TT3a z4*0grVw}OgtWEtl!vncJu=+JD>A7BEd2DpHuCF}xKS^b_WmG>DmXw%D3i^~of6>;f zjNS<@ZKGbNBV09%QO@||b2#0MfTIY#;HINN<>{Qz6PXXC-vO~V5IIR%QN-dnF4hB(qrIBQuR zagY!*>z4P$->YW!%T%~l}wvtoOb-H@&!C#Y)O4cx)2#jcp5T)9% zTj6pZO+deQY3;+;+)H*JSnbx?Y~JLnIkgxl5ph6&NdE4dZD_W`CXPnY9fg+uDK;$* zYBt)<3xvfN294U%Y5FxL|EFCm|A(?` z0N?Wnodgw|cvL4uJ}&9cgcdjk9-PRo)FYJPA#*8&>$KH$iiV76sy7!=wVkBU1jF4J z<2O!2iIU%&7I^)9);b}acXUYTMV)9&Y#mtT(@AKig#|)}>GrLVs4$j{SD_pRw zMKwp3lo*ecD#|jm8BtEGdH9}HR{vHYai;yjYb#$3*?Ij3Ya5-Twf)Xa{$#bQUct&w zO=i$q`1>%)jow4;2`B8LM&m7Gm?vsaNx-**q}-Xt^atSyg7ytu7sE`_q2;;Ze*EUI zwu)@#e5E-j?c$7WIQr%{=xpq}b^UOKF9+lnrm65Z;n1ikL(FTwviBQP^)^F+-iy$~ z4}O$&KC<-ia}9ScQP{7xnOa=!H<`_-|c9wD`_buf00;eJo_{7IHFp9*Vc z*JIeA$ClYliTbM96^FJSZ5@_zw4+L0bE*fey4I9$(}6(S+qB!SDrI}B5O|Uf27$Oh z%ydq8N3;*#i-55A^4i&Z4`(pYNj?GrJ^r^X&-4PGg5;`$eG?5`v~@8sOHZ6;@z7L9 zTNID1s42(Cn7u6jj4ORr`0^Gm_EwVKsN~6E`n-iGXp@x*0{q4)8dPw}@GkaEq?%2nA;|6JjE` zGI|{N>(MMlJL29N&N7Xl#x*QB&wU2^{B`BrY*}WRX;?j}Ms?$;MP%&wq5P2EdrMZi zlNoN=m5?X!s=EbbB8_YX6PB-7TycM5DL;{U z#VR2>sWk~C;aWJy{FLgT`p*bxSX-@I%GPN31M#qL)+J<(V0kLhvr`goeb>yBiFL`U z0iN`_J>Q=c(v?cQ!6z5fCLxb#E42O8Bx33qS%2kEQT%3?E&cmx4W|SDE^H zo`zQHud+EGwyX$yunhb7w(ToF{(EfvtM9t&^@fFR=G>dJ_zn!9(tnHmy3~l15g=g# zJqQHdFLFn`5BC2m-&@(yca062kv!oOjN95Bf2NjVpNE%&6V-2WH{F`DHe4uoh`({P zWTfGwM}^gpNT6X?6{f4|XzC)CH!!RioA1MOj)CzJ39et((KGJ~MH$-(n}(W|g$fBb zW(0U&OZ2`F;4wKw5sa2;f}~~2QdQ1be^P24T!No>hApBm+YFpl_!R3Lc6=J+4pu($ z0b<}~hUL%bo}Z|mT8b^p%n8ycssB!=H z_>rOh1y@9!`H}~uCG}-fEA4khB}iJ-I&)lHHG{jn z16l%prh`}35n@)AeabDpbDAXIi*-x)a}P$9jt1gme5VHE2RfeQy3AGuNfaa!4|+`V z9c}aIlRU7fmW8PdD@g&1RW0?$o_m(neuW-~Z>w70y7T$8%X*NoM2)Y=rGWbA2DuWo z`oblpt@1z5m-KIm9u!FL5H-}|R4;1BKwLX=!`kRtO}7KbnOlQ-mMAK!DI-ceE`zLy zk3H0P%^x`BGGdxEOG`Qx^xC5RrXgmhywqU;6p zzK?=8QcBn3%fed}hvq-uybj7= zeaex}>1OevkhSOgzr0zhV;uQ1tEs`m)k)3%Htxi)`Yt5XGm|~LJEt^}64Co^P=K|R z$R2W^J~rOz_Ax5Ug9FJ<+vM}E6IR@caQ5ZRM#j~#My)ufp#t;a6>I8@Q^#iL!o(%7 zj^uvSr63+Vi%YyNUS%-7;&9mF_gU}A&X612iDyBPN#-m`oNsIerda)&B;5iePq%Sv ziL{T2q2dFeZ%_1zUCwM(Lbd7YpNN`uH+g;9SjW1sLi;>ejD&X7S*3J>2MMt2hZYpt zSBLKfw_E3LuDV+|CSA)K4>P;8arUc~-rDlCRxtGmUW7dGh7+zyT4wz3OwiH8L3@>l ziJ1x0AYd*Y1J8FV55JnVHr|tn^(10026$l!wmZucZSX5WnHduHH-It(CKH(LeO3Wv zQy3qtI{_iLbEO5I<*k>X2i~j=bQ9bM1A#^%yK&(ktUunz%>j>h+j;kIc~-cc1v3DS zTA-A&6La=VIP{MGU||4(0)TMDzJ~G9~)pJY9avu-VG}vjG4q0bYRZCjiL715eYYkN^Mx literal 0 HcmV?d00001 diff --git a/testdata/test-data-systematic.xml b/testdata/test-data-systematic.xml index d20aa2ce1..03b4218cf 100644 --- a/testdata/test-data-systematic.xml +++ b/testdata/test-data-systematic.xml @@ -92,6 +92,11 @@ JULIAN:BCE:0700:BCE:0600 + CE:1849:CE:1850 + GREGORIAN:1848-01:1849-02 + 2022 + GREGORIAN:CE:0476-09-04:CE:0476-09-04 + GREGORIAN:CE:2014-01-31 4711 @@ -106,7 +111,7 @@ #00ff00 - b2 + first subnode test_thing_0 @@ -181,11 +186,32 @@ false - https://dasch.swiss + https://en.wiktionary.org/wiki/Ῥόδος + https://www.test-case.ch/ + https://reg-exr.com:3000 + https://reg-exr.com:3000/path/to/file_(%C3%89).htm + https://reg-exr.com:3000/path/to/file#fragment + https://reg-exr.com:3000/path/to/file?query=test + https://reg-exr.com:3000/path/to/file?query=test#fragment + https://reg-exr.com/path/to/file?query=test#fragment + http://www.168.1.1.0/path + http://www.168.1.1.0:4200/path + http://[2001:0db8:0000:0000:0000:8a2e:0370:7334]:4200/path + https://en.wikipedia.org/wiki/Haiku#/media/File:Basho_Horohoroto.jpg + http://datypic.com/prod.html#shirt JULIAN:BCE:0700:BCE:0600 + + + + + + + + + 4711 @@ -202,7 +228,7 @@ test_thing_0 - b2 + second subnode @@ -654,7 +680,7 @@ video_thing_1 - 2.0:10.0 + +.1:+.9 @@ -667,7 +693,7 @@ audio_thing_1 - 8.0:12.0 + -.1:5 @@ -680,7 +706,7 @@ video_thing_1 - 8.0:12.0 + -10.0:-5.1 diff --git a/testdata/test-project-systematic.json b/testdata/test-project-systematic.json index bf6f6155c..fc5bba24d 100644 --- a/testdata/test-project-systematic.json +++ b/testdata/test-project-systematic.json @@ -32,36 +32,36 @@ }, "nodes": [ { - "name": "a", + "name": "first node of testlist", "labels": { - "en": "a_label with a\"post'rophes", + "en": "First node of the Test-List", "rm": "Rumantsch" } }, { - "name": "b", + "name": "second node of testlist", "labels": { - "en": "b_label with no comment" + "en": "Second node of the Test-List" }, "nodes": [ { - "name": "b1", + "name": "first subnode", "labels": { - "en": "b1_label" + "en": "First Sub-Node" } }, { - "name": "b2", + "name": "second subnode", "labels": { - "en": "b2_label" + "en": "Second Sub-Node" } } ] }, { - "name": "c", + "name": "third node of testlist", "labels": { - "en": "c_label" + "en": "Third node of the Test-List" } } ] @@ -93,7 +93,11 @@ { "name": "notUsedNode_1", "labels": { - "en": "nodeLabel_1" + "en": "nodeLabel_1\"'" + }, + "comments": { + "en": "Nodes can have comments, too!", + "rm": "Even in Rumantsch!" } } ] @@ -301,6 +305,17 @@ }, "gui_element": "Date" }, + { + "name": "hasTime", + "super": [ + "hasValue" + ], + "object": "TimeValue", + "labels": { + "en": "Time" + }, + "gui_element": "TimeStamp" + }, { "name": "hasInteger", "super": [ @@ -487,77 +502,66 @@ "cardinalities": [ { "propname": ":hasText", - "gui_order": 1, "cardinality": "1-n" }, { "propname": ":hasRichtext", - "gui_order": 2, "cardinality": "0-n" }, { "propname": ":hasUri", - "gui_order": 3, "cardinality": "0-n" }, { "propname": ":hasBoolean", - "gui_order": 4, "cardinality": "1" }, { "propname": ":hasDate", - "gui_order": 5, + "cardinality": "0-n" + }, + { + "propname": ":hasTime", "cardinality": "0-n" }, { "propname": ":hasInteger", - "gui_order": 6, "cardinality": "0-n" }, { "propname": ":hasDecimal", - "gui_order": 7, "cardinality": "0-n" }, { "propname": ":hasGeoname", - "gui_order": 9, "cardinality": "0-n" }, { "propname": ":hasColor", - "gui_order": 11, "cardinality": "0-n" }, { "propname": ":hasListItem", - "gui_order": 12, "cardinality": "0-n" }, { "propname": ":hasTestThing2", - "gui_order": 13, "cardinality": "0-n" }, { "propname": ":hasResource", - "gui_order": 14, "cardinality": "0-n" }, { "propname": ":hasRegion", - "gui_order": 15, "cardinality": "0-n" }, { "propname": ":hasRepresentation", - "gui_order": 16, "cardinality": "0-n" }, { "propname": ":hasMovingImageRepresentation", - "gui_order": 17, "cardinality": "0-n" } ]