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 zcmb@u2UJtp*Eg)=D5D6ds30vWgV-Qq0SP543JOX
ln&AZq>M6vNQr<-2Lb6N
zf&$VKl@bskA|><=fdmKvl8}TX-^H2#GxNO9yT0|UXMM0jl6%hC<+t~__nf`=A=1Ry
zKx~)Ht}R=(h~2n;_4bx6B7Ix7Y`y;54&ci6#sepTFLo|^dL}pY^bVW&cs+7)f4F7K
z@i_k%Fr!~g_cxPX+GF0{zxr#~x0An}cHHOUrhoUNbjdrPy;I#SpP<;}NN2LL^MN1A
zFLW=BAIr{0T{h_|QtvwZ+m39t=@YW?K1H
*w7srx{Fs0~