diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 0a2c913e..f18d7e13 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,2 +1,2 @@ -- [ ] Model changes documented in the [business entity model](https://ltwiki.adr.admin.ch:8443/display/PB/Business+Entity+Model) +- [ ] Model changes documented in the [business entity model](https://swissgeoplatform.atlassian.net/wiki/spaces/PB/pages/16155637/Business+Entity+Model) diff --git a/Pipfile b/Pipfile index 389c1fa3..9177b52b 100644 --- a/Pipfile +++ b/Pipfile @@ -4,7 +4,7 @@ verify_ssl = true name = "pypi" [packages] -django = "~=5.0.13" +django = "~=5.0.14" django-ninja = "~=1.1" psycopg2-binary = "~=2.9.5" django-environ = "~=0.11" @@ -17,6 +17,8 @@ nanoid = "~=2.0.0" whitenoise = "~=6.8.2" pystac-client = "~=0.8.5" ecs-logging = "*" +beautifulsoup4 = "*" +requests = "*" [dev-packages] yapf = "*" @@ -40,6 +42,7 @@ bandit = "*" pytest-xdist = "*" types-nanoid = "*" pytest-cov = "*" +types-requests = "*" [requires] python_version = "3.12" diff --git a/Pipfile.lock b/Pipfile.lock index 79232230..1d8dd198 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "5b6063085b3762ce95b4667831171130562a0f40498032d3f0ca703a609c503c" + "sha256": "8acf594b53fb388195ffd9caefae00a082c70e1867622436ace666a33fba0bf1" }, "pipfile-spec": 6, "requires": { @@ -34,11 +34,20 @@ }, "attrs": { "hashes": [ - "sha256:1c97078a80c814273a76b2a298a932eb681c87415c11dee0a6921de7f1b02c3e", - "sha256:c75a69e28a550a7e93789579c22aa26b0f5b83b75dc4e08fe092980051e1090a" + "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", + "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b" ], "markers": "python_version >= '3.8'", - "version": "==25.1.0" + "version": "==25.3.0" + }, + "beautifulsoup4": { + "hashes": [ + "sha256:1bd32405dacc920b42b83ba01644747ed77456a65760e285fbc47633ceddaf8b", + "sha256:99045d7d3f08f91f0d656bc9b7efbae189426cd913d830294a15eefa0ea4df16" + ], + "index": "pypi", + "markers": "python_full_version >= '3.7.0'", + "version": "==4.13.3" }, "boto3": { "hashes": [ @@ -165,12 +174,12 @@ }, "django": { "hashes": [ - "sha256:b983238dfa2eb2e6b27ebb815e14f0741cf186606eb7bcd857e740174017c50e", - "sha256:f9d4b7b87a9dae248d5f20cec940cf7290e07d508d6d8432e3c2cabf09b3b0ff" + "sha256:29019a5763dbd48da1720d687c3522ef40d1c61be6fb2fad27ed79e9f655bc11", + "sha256:e762bef8629ee704de215ebbd32062b84f4e56327eed412e5544f6f6eb1dfd74" ], "index": "pypi", "markers": "python_version >= '3.10'", - "version": "==5.0.13" + "version": "==5.0.14" }, "django-environ": { "hashes": [ @@ -183,12 +192,12 @@ }, "django-ninja": { "hashes": [ - "sha256:5b320e2dc0f41a6032bfa7e1ebc33559ae1e911a426f0c6be6674a50b20819be", - "sha256:f58096b6c767d1403dfd6c49743f82d780d7b9688d9302ecab316ac1fa6131bb" + "sha256:590bb1c8fc32f07bac1d5b5d404419205c41b4945b86d4237b0676174c1b6df7", + "sha256:b64bb7f269d0c0a890b429802dffbe3689298eb1ed7dc324224f3b325e8f8871" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==1.3.0" + "version": "==1.4.0" }, "ecs-logging": { "hashes": [ @@ -466,117 +475,116 @@ }, "pydantic": { "hashes": [ - "sha256:427d664bf0b8a2b34ff5dd0f5a18df00591adcee7198fbd71981054cef37b584", - "sha256:ca5daa827cce33de7a42be142548b0096bf05a7e7b365aebfa5f8eeec7128236" + "sha256:2138628e050bd7a1e70b91d4bf4a91167f4ad76fdb83209b107c8d84b854917e", + "sha256:7f17d25846bcdf89b670a86cdfe7b29a9f1c9ca23dee154221c9aa81845cfca7" ], - "markers": "python_version >= '3.8'", - "version": "==2.10.6" + "markers": "python_version >= '3.9'", + "version": "==2.11.2" }, "pydantic-core": { "hashes": [ - "sha256:00bad2484fa6bda1e216e7345a798bd37c68fb2d97558edd584942aa41b7d278", - "sha256:0296abcb83a797db256b773f45773da397da75a08f5fcaef41f2044adec05f50", - "sha256:03d0f86ea3184a12f41a2d23f7ccb79cdb5a18e06993f8a45baa8dfec746f0e9", - "sha256:044a50963a614ecfae59bb1eaf7ea7efc4bc62f49ed594e18fa1e5d953c40e9f", - "sha256:05e3a55d124407fffba0dd6b0c0cd056d10e983ceb4e5dbd10dda135c31071d6", - "sha256:08e125dbdc505fa69ca7d9c499639ab6407cfa909214d500897d02afb816e7cc", - "sha256:097830ed52fd9e427942ff3b9bc17fab52913b2f50f2880dc4a5611446606a54", - "sha256:0d1e85068e818c73e048fe28cfc769040bb1f475524f4745a5dc621f75ac7630", - "sha256:0d75070718e369e452075a6017fbf187f788e17ed67a3abd47fa934d001863d9", - "sha256:14d4a5c49d2f009d62a2a7140d3064f686d17a5d1a268bc641954ba181880236", - "sha256:172fce187655fece0c90d90a678424b013f8fbb0ca8b036ac266749c09438cb7", - "sha256:18a101c168e4e092ab40dbc2503bdc0f62010e95d292b27827871dc85450d7ee", - "sha256:1a4207639fb02ec2dbb76227d7c751a20b1a6b4bc52850568e52260cae64ca3b", - "sha256:1c1fd185014191700554795c99b347d64f2bb637966c4cfc16998a0ca700d048", - "sha256:1e2cb691ed9834cd6a8be61228471d0a503731abfb42f82458ff27be7b2186fc", - "sha256:1ebaf1d0481914d004a573394f4be3a7616334be70261007e47c2a6fe7e50130", - "sha256:220f892729375e2d736b97d0e51466252ad84c51857d4d15f5e9692f9ef12be4", - "sha256:251136cdad0cb722e93732cb45ca5299fb56e1344a833640bf93b2803f8d1bfd", - "sha256:26f0d68d4b235a2bae0c3fc585c585b4ecc51382db0e3ba402a22cbc440915e4", - "sha256:26f32e0adf166a84d0cb63be85c562ca8a6fa8de28e5f0d92250c6b7e9e2aff7", - "sha256:280d219beebb0752699480fe8f1dc61ab6615c2046d76b7ab7ee38858de0a4e7", - "sha256:28ccb213807e037460326424ceb8b5245acb88f32f3d2777427476e1b32c48c4", - "sha256:2bf14caea37e91198329b828eae1618c068dfb8ef17bb33287a7ad4b61ac314e", - "sha256:2d367ca20b2f14095a8f4fa1210f5a7b78b8a20009ecced6b12818f455b1e9fa", - "sha256:30c5f68ded0c36466acede341551106821043e9afaad516adfb6e8fa80a4e6a6", - "sha256:337b443af21d488716f8d0b6164de833e788aa6bd7e3a39c005febc1284f4962", - "sha256:3911ac9284cd8a1792d3cb26a2da18f3ca26c6908cc434a18f730dc0db7bfa3b", - "sha256:3d591580c34f4d731592f0e9fe40f9cc1b430d297eecc70b962e93c5c668f15f", - "sha256:3de3ce3c9ddc8bbd88f6e0e304dea0e66d843ec9de1b0042b0911c1663ffd474", - "sha256:3de9961f2a346257caf0aa508a4da705467f53778e9ef6fe744c038119737ef5", - "sha256:40d02e7d45c9f8af700f3452f329ead92da4c5f4317ca9b896de7ce7199ea459", - "sha256:42c5f762659e47fdb7b16956c71598292f60a03aa92f8b6351504359dbdba6cf", - "sha256:47956ae78b6422cbd46f772f1746799cbb862de838fd8d1fbd34a82e05b0983a", - "sha256:491a2b73db93fab69731eaee494f320faa4e093dbed776be1a829c2eb222c34c", - "sha256:4c9775e339e42e79ec99c441d9730fccf07414af63eac2f0e48e08fd38a64d76", - "sha256:4e0b4220ba5b40d727c7f879eac379b822eee5d8fff418e9d3381ee45b3b0362", - "sha256:50a68f3e3819077be2c98110c1f9dcb3817e93f267ba80a2c05bb4f8799e2ff4", - "sha256:519f29f5213271eeeeb3093f662ba2fd512b91c5f188f3bb7b27bc5973816934", - "sha256:521eb9b7f036c9b6187f0b47318ab0d7ca14bd87f776240b90b21c1f4f149320", - "sha256:57762139821c31847cfb2df63c12f725788bd9f04bc2fb392790959b8f70f118", - "sha256:5e4f4bb20d75e9325cc9696c6802657b58bc1dbbe3022f32cc2b2b632c3fbb96", - "sha256:5e68c4446fe0810e959cdff46ab0a41ce2f2c86d227d96dc3847af0ba7def306", - "sha256:669e193c1c576a58f132e3158f9dfa9662969edb1a250c54d8fa52590045f046", - "sha256:688d3fd9fcb71f41c4c015c023d12a79d1c4c0732ec9eb35d96e3388a120dcf3", - "sha256:6fb4aadc0b9a0c063206846d603b92030eb6f03069151a625667f982887153e2", - "sha256:7041c36f5680c6e0f08d922aed302e98b3745d97fe1589db0a3eebf6624523af", - "sha256:71b24c7d61131bb83df10cc7e687433609963a944ccf45190cfc21e0887b08c9", - "sha256:77d1bca19b0f7021b3a982e6f903dcd5b2b06076def36a652e3907f596e29f67", - "sha256:7969e133a6f183be60e9f6f56bfae753585680f3b7307a8e555a948d443cc05a", - "sha256:7a66efda2387de898c8f38c0cf7f14fca0b51a8ef0b24bfea5849f1b3c95af27", - "sha256:7d0c8399fcc1848491f00e0314bd59fb34a9c008761bcb422a057670c3f65e35", - "sha256:7d14bd329640e63852364c306f4d23eb744e0f8193148d4044dd3dacdaacbd8b", - "sha256:7e17b560be3c98a8e3aa66ce828bdebb9e9ac6ad5466fba92eb74c4c95cb1151", - "sha256:8083d4e875ebe0b864ffef72a4304827015cff328a1be6e22cc850753bfb122b", - "sha256:82f91663004eb8ed30ff478d77c4d1179b3563df6cdb15c0817cd1cdaf34d154", - "sha256:82f986faf4e644ffc189a7f1aafc86e46ef70372bb153e7001e8afccc6e54133", - "sha256:83097677b8e3bd7eaa6775720ec8e0405f1575015a463285a92bfdfe254529ef", - "sha256:85210c4d99a0114f5a9481b44560d7d1e35e32cc5634c656bc48e590b669b145", - "sha256:8c19d1ea0673cd13cc2f872f6c9ab42acc4e4f492a7ca9d3795ce2b112dd7e15", - "sha256:8d9b3388db186ba0c099a6d20f0604a44eabdeef1777ddd94786cdae158729e4", - "sha256:8e10c99ef58cfdf2a66fc15d66b16c4a04f62bca39db589ae8cba08bc55331bc", - "sha256:953101387ecf2f5652883208769a79e48db18c6df442568a0b5ccd8c2723abee", - "sha256:9c3ed807c7b91de05e63930188f19e921d1fe90de6b4f5cd43ee7fcc3525cb8c", - "sha256:9e0c8cfefa0ef83b4da9588448b6d8d2a2bf1a53c3f1ae5fca39eb3061e2f0b0", - "sha256:9fdbe7629b996647b99c01b37f11170a57ae675375b14b8c13b8518b8320ced5", - "sha256:a0fcd29cd6b4e74fe8ddd2c90330fd8edf2e30cb52acda47f06dd615ae72da57", - "sha256:ac4dbfd1691affb8f48c2c13241a2e3b60ff23247cbcf981759c768b6633cf8b", - "sha256:b0cb791f5b45307caae8810c2023a184c74605ec3bcbb67d13846c28ff731ff8", - "sha256:ba5dd002f88b78a4215ed2f8ddbdf85e8513382820ba15ad5ad8955ce0ca19a1", - "sha256:bca101c00bff0adb45a833f8451b9105d9df18accb8743b08107d7ada14bd7da", - "sha256:bd8086fa684c4775c27f03f062cbb9eaa6e17f064307e86b21b9e0abc9c0f02e", - "sha256:bec317a27290e2537f922639cafd54990551725fc844249e64c523301d0822fc", - "sha256:c10eb4f1659290b523af58fa7cffb452a61ad6ae5613404519aee4bfbf1df993", - "sha256:c33939a82924da9ed65dab5a65d427205a73181d8098e79b6b426bdf8ad4e656", - "sha256:c61709a844acc6bf0b7dce7daae75195a10aac96a596ea1b776996414791ede4", - "sha256:c70c26d2c99f78b125a3459f8afe1aed4d9687c24fd677c6a4436bc042e50d6c", - "sha256:c817e2b40aba42bac6f457498dacabc568c3b7a986fc9ba7c8d9d260b71485fb", - "sha256:cabb9bcb7e0d97f74df8646f34fc76fbf793b7f6dc2438517d7a9e50eee4f14d", - "sha256:cc3f1a99a4f4f9dd1de4fe0312c114e740b5ddead65bb4102884b384c15d8bc9", - "sha256:cca63613e90d001b9f2f9a9ceb276c308bfa2a43fafb75c8031c4f66039e8c6e", - "sha256:ce8918cbebc8da707ba805b7fd0b382816858728ae7fe19a942080c24e5b7cd1", - "sha256:d2088237af596f0a524d3afc39ab3b036e8adb054ee57cbb1dcf8e09da5b29cc", - "sha256:d262606bf386a5ba0b0af3b97f37c83d7011439e3dc1a9298f21efb292e42f1a", - "sha256:d2d63f1215638d28221f664596b1ccb3944f6e25dd18cd3b86b0a4c408d5ebb9", - "sha256:d3e8d504bdd3f10835468f29008d72fc8359d95c9c415ce6e767203db6127506", - "sha256:d4041c0b966a84b4ae7a09832eb691a35aec90910cd2dbe7a208de59be77965b", - "sha256:d716e2e30c6f140d7560ef1538953a5cd1a87264c737643d481f2779fc247fe1", - "sha256:d81d2068e1c1228a565af076598f9e7451712700b673de8f502f0334f281387d", - "sha256:d9640b0059ff4f14d1f37321b94061c6db164fbe49b334b31643e0528d100d99", - "sha256:de3cd1899e2c279b140adde9357c4495ed9d47131b4a4eaff9052f23398076b3", - "sha256:e0fd26b16394ead34a424eecf8a31a1f5137094cabe84a1bcb10fa6ba39d3d31", - "sha256:e2bb4d3e5873c37bb3dd58714d4cd0b0e6238cebc4177ac8fe878f8b3aa8e74c", - "sha256:eb026e5a4c1fee05726072337ff51d1efb6f59090b7da90d30ea58625b1ffb39", - "sha256:eda3f5c2a021bbc5d976107bb302e0131351c2ba54343f8a496dc8783d3d3a6a", - "sha256:ef592d4bad47296fb11f96cd7dc898b92e795032b4894dfb4076cfccd43a9308", - "sha256:f141ee28a0ad2123b6611b6ceff018039df17f32ada8b534e6aa039545a3efb2", - "sha256:f66d89ba397d92f840f8654756196d93804278457b5fbede59598a1f9f90b228", - "sha256:f6f8e111843bbb0dee4cb6594cdc73e79b3329b526037ec242a3e49012495b3b", - "sha256:fa8e459d4954f608fa26116118bb67f56b93b209c39b008277ace29937453dc9", - "sha256:fd1aea04935a508f62e0d0ef1f5ae968774a32afc306fb8545e06f5ff5cdf3ad" + "sha256:0483847fa9ad5e3412265c1bd72aad35235512d9ce9d27d81a56d935ef489672", + "sha256:048831bd363490be79acdd3232f74a0e9951b11b2b4cc058aeb72b22fdc3abe1", + "sha256:048c01eee07d37cbd066fc512b9d8b5ea88ceeb4e629ab94b3e56965ad655add", + "sha256:049e0de24cf23766f12cc5cc71d8abc07d4a9deb9061b334b62093dedc7cb068", + "sha256:08530b8ac922003033f399128505f513e30ca770527cc8bbacf75a84fcc2c74b", + "sha256:0fb935c5591573ae3201640579f30128ccc10739b45663f93c06796854405505", + "sha256:1293d7febb995e9d3ec3ea09caf1a26214eec45b0f29f6074abb004723fc1de8", + "sha256:177d50460bc976a0369920b6c744d927b0ecb8606fb56858ff542560251b19e5", + "sha256:1a28239037b3d6f16916a4c831a5a0eadf856bdd6d2e92c10a0da3a59eadcf3e", + "sha256:1b30d92c9412beb5ac6b10a3eb7ef92ccb14e3f2a8d7732e2d739f58b3aa7544", + "sha256:1c607801d85e2e123357b3893f82c97a42856192997b95b4d8325deb1cd0c5f4", + "sha256:1d20eb4861329bb2484c021b9d9a977566ab16d84000a57e28061151c62b349a", + "sha256:1dfae24cf9921875ca0ca6a8ecb4bb2f13c855794ed0d468d6abbec6e6dcd44a", + "sha256:25626fb37b3c543818c14821afe0fd3830bc327a43953bc88db924b68c5723f1", + "sha256:282b3fe1bbbe5ae35224a0dbd05aed9ccabccd241e8e6b60370484234b456266", + "sha256:2ea62419ba8c397e7da28a9170a16219d310d2cf4970dbc65c32faf20d828c83", + "sha256:2f593494876eae852dc98c43c6f260f45abdbfeec9e4324e31a481d948214764", + "sha256:2f9284e11c751b003fd4215ad92d325d92c9cb19ee6729ebd87e3250072cdcde", + "sha256:3077cfdb6125cc8dab61b155fdd714663e401f0e6883f9632118ec12cf42df26", + "sha256:32cd11c5914d1179df70406427097c7dcde19fddf1418c787540f4b730289896", + "sha256:338ea9b73e6e109f15ab439e62cb3b78aa752c7fd9536794112e14bee02c8d18", + "sha256:35a5ec3fa8c2fe6c53e1b2ccc2454398f95d5393ab398478f53e1afbbeb4d939", + "sha256:398a38d323f37714023be1e0285765f0a27243a8b1506b7b7de87b647b517e48", + "sha256:3a371dc00282c4b84246509a5ddc808e61b9864aa1eae9ecc92bb1268b82db4a", + "sha256:3a64e81e8cba118e108d7126362ea30e021291b7805d47e4896e52c791be2761", + "sha256:3ab2d36e20fbfcce8f02d73c33a8a7362980cff717926bbae030b93ae46b56c7", + "sha256:3f1fdb790440a34f6ecf7679e1863b825cb5ffde858a9197f851168ed08371e5", + "sha256:3f2648b9262607a7fb41d782cc263b48032ff7a03a835581abbf7a3bec62bcf5", + "sha256:401d7b76e1000d0dd5538e6381d28febdcacb097c8d340dde7d7fc6e13e9f95d", + "sha256:495bc156026efafd9ef2d82372bd38afce78ddd82bf28ef5276c469e57c0c83e", + "sha256:4b315e596282bbb5822d0c7ee9d255595bd7506d1cb20c2911a4da0b970187d3", + "sha256:5183e4f6a2d468787243ebcd70cf4098c247e60d73fb7d68d5bc1e1beaa0c4db", + "sha256:5277aec8d879f8d05168fdd17ae811dd313b8ff894aeeaf7cd34ad28b4d77e33", + "sha256:52928d8c1b6bda03cc6d811e8923dffc87a2d3c8b3bfd2ce16471c7147a24850", + "sha256:549150be302428b56fdad0c23c2741dcdb5572413776826c965619a25d9c6bde", + "sha256:5773da0ee2d17136b1f1c6fbde543398d452a6ad2a7b54ea1033e2daa739b8d2", + "sha256:5ab77f45d33d264de66e1884fca158bc920cb5e27fd0764a72f72f5756ae8bdb", + "sha256:5c834f54f8f4640fd7e4b193f80eb25a0602bba9e19b3cd2fc7ffe8199f5ae02", + "sha256:5ccd429694cf26af7997595d627dd2637e7932214486f55b8a357edaac9dae8c", + "sha256:681d65e9011f7392db5aa002b7423cc442d6a673c635668c227c6c8d0e5a4f77", + "sha256:694ad99a7f6718c1a498dc170ca430687a39894a60327f548e02a9c7ee4b6504", + "sha256:6dd8ecfde08d8bfadaea669e83c63939af76f4cf5538a72597016edfa3fad516", + "sha256:6e966fc3caaf9f1d96b349b0341c70c8d6573bf1bac7261f7b0ba88f96c56c24", + "sha256:70af6a21237b53d1fe7b9325b20e65cbf2f0a848cf77bed492b029139701e66a", + "sha256:723c5630c4259400818b4ad096735a829074601805d07f8cafc366d95786d331", + "sha256:7965c13b3967909a09ecc91f21d09cfc4576bf78140b988904e94f130f188396", + "sha256:7aeb055a42d734c0255c9e489ac67e75397d59c6fbe60d155851e9782f276a9c", + "sha256:7edbc454a29fc6aeae1e1eecba4f07b63b8d76e76a748532233c4c167b4cb9ea", + "sha256:7fb66263e9ba8fea2aa85e1e5578980d127fb37d7f2e292773e7bc3a38fb0c7b", + "sha256:87d3776f0001b43acebfa86f8c64019c043b55cc5a6a2e313d728b5c95b46969", + "sha256:8ab581d3530611897d863d1a649fb0644b860286b4718db919bfd51ece41f10b", + "sha256:8d13f0276806ee722e70a1c93da19748594f19ac4299c7e41237fc791d1861ea", + "sha256:8ffab8b2908d152e74862d276cf5017c81a2f3719f14e8e3e8d6b83fda863927", + "sha256:902dbc832141aa0ec374f4310f1e4e7febeebc3256f00dc359a9ac3f264a45dc", + "sha256:9097b9f17f91eea659b9ec58148c0747ec354a42f7389b9d50701610d86f812e", + "sha256:91815221101ad3c6b507804178a7bb5cb7b2ead9ecd600041669c8d805ebd595", + "sha256:948b73114f47fd7016088e5186d13faf5e1b2fe83f5e320e371f035557fd264d", + "sha256:99b56acd433386c8f20be5c4000786d1e7ca0523c8eefc995d14d79c7a081498", + "sha256:9d3da303ab5f378a268fa7d45f37d7d85c3ec19769f28d2cc0c61826a8de21fe", + "sha256:9f466e8bf0a62dc43e068c12166281c2eca72121dd2adc1040f3aa1e21ef8599", + "sha256:9fea9c1869bb4742d174a57b4700c6dadea951df8b06de40c2fedb4f02931c2e", + "sha256:a0d5f3acc81452c56895e90643a625302bd6be351e7010664151cc55b7b97f89", + "sha256:a3edde68d1a1f9af1273b2fe798997b33f90308fb6d44d8550c89fc6a3647cf6", + "sha256:a62c3c3ef6a7e2c45f7853b10b5bc4ddefd6ee3cd31024754a1a5842da7d598d", + "sha256:aa687a23d4b7871a00e03ca96a09cad0f28f443690d300500603bd0adba4b523", + "sha256:ab0277cedb698749caada82e5d099dc9fed3f906a30d4c382d1a21725777a1e5", + "sha256:ad05b683963f69a1d5d2c2bdab1274a31221ca737dbbceaa32bcb67359453cdd", + "sha256:b172f7b9d2f3abc0efd12e3386f7e48b576ef309544ac3a63e5e9cdd2e24585d", + "sha256:b1caa0bc2741b043db7823843e1bde8aaa58a55a58fda06083b0569f8b45693a", + "sha256:bae370459da6a5466978c0eacf90690cb57ec9d533f8e63e564ef3822bfa04fe", + "sha256:bcc9c6fdb0ced789245b02b7d6603e17d1563064ddcfc36f046b61c0c05dd9df", + "sha256:bdc84017d28459c00db6f918a7272a5190bec3090058334e43a76afb279eac7c", + "sha256:bfd0adeee563d59c598ceabddf2c92eec77abcb3f4a391b19aa7366170bd9e30", + "sha256:c566dd9c5f63d22226409553531f89de0cac55397f2ab8d97d6f06cfce6d947e", + "sha256:c91dbb0ab683fa0cd64a6e81907c8ff41d6497c346890e26b23de7ee55353f96", + "sha256:c964fd24e6166420d18fb53996d8c9fd6eac9bf5ae3ec3d03015be4414ce497f", + "sha256:cc77ec5b7e2118b152b0d886c7514a4653bcb58c6b1d760134a9fab915f777b3", + "sha256:d100e3ae783d2167782391e0c1c7a20a31f55f8015f3293647544df3f9c67824", + "sha256:d3a07fadec2a13274a8d861d3d37c61e97a816beae717efccaa4b36dfcaadcde", + "sha256:d5e3d15245b08fa4a84cefc6c9222e6f37c98111c8679fbd94aa145f9a0ae23d", + "sha256:de9e06abe3cc5ec6a2d5f75bc99b0bdca4f5c719a5b34026f8c57efbdecd2ee3", + "sha256:df6a94bf9452c6da9b5d76ed229a5683d0306ccb91cca8e1eea883189780d568", + "sha256:e100c52f7355a48413e2999bfb4e139d2977a904495441b374f3d4fb4a170961", + "sha256:e11f3864eb516af21b01e25fac915a82e9ddad3bb0fb9e95a246067398b435a4", + "sha256:e14f369c98a7c15772b9da98987f58e2b509a93235582838bd0d1d8c08b68fda", + "sha256:e3de2777e3b9f4d603112f78006f4ae0acb936e95f06da6cb1a45fbad6bdb4b5", + "sha256:e7aaba1b4b03aaea7bb59e1b5856d734be011d3e6d98f5bcaa98cb30f375f2ad", + "sha256:ec259f62538e8bf364903a7d0d0239447059f9434b284f5536e8402b7dd198db", + "sha256:ec79de2a8680b1a67a07490bddf9636d5c2fab609ba8c57597e855fa5fa4dacd", + "sha256:ed3eb16d51257c763539bde21e011092f127a2202692afaeaccb50db55a31383", + "sha256:ede9b407e39949d2afc46385ce6bd6e11588660c26f80576c11c958e6647bc40", + "sha256:ee12a7be1742f81b8a65b36c6921022301d466b82d80315d215c4c691724986f", + "sha256:ef99779001d7ac2e2461d8ab55d3373fe7315caefdbecd8ced75304ae5a6fc6b", + "sha256:f59295ecc75a1788af8ba92f2e8c6eeaa5a94c22fc4d151e8d9638814f85c8fc", + "sha256:f995719707e0e29f0f41a8aa3bcea6e761a36c9136104d3189eafb83f5cec5e5", + "sha256:f99aeda58dce827f76963ee87a0ebe75e648c72ff9ba1174a253f6744f518f65", + "sha256:fc6bf8869e193855e8d91d91f6bf59699a5cdfaa47a404e278e776dd7f168b39", + "sha256:fc903512177361e868bc1f5b80ac8c8a6e05fcdd574a5fb5ffeac5a9982b9e89", + "sha256:fe44d56aa0b00d66640aa84a3cbe80b7a3ccdc6f0b1ca71090696a6d4777c091" ], - "markers": "python_version >= '3.8'", - "version": "==2.27.2" + "markers": "python_version >= '3.9'", + "version": "==2.33.1" }, "pystac": { "extras": [ @@ -679,117 +687,129 @@ "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6" ], + "index": "pypi", "markers": "python_version >= '3.8'", "version": "==2.32.3" }, "rpds-py": { "hashes": [ - "sha256:09cd7dbcb673eb60518231e02874df66ec1296c01a4fcd733875755c02014b19", - "sha256:0f3288930b947cbebe767f84cf618d2cbe0b13be476e749da0e6a009f986248c", - "sha256:0fced9fd4a07a1ded1bac7e961ddd9753dd5d8b755ba8e05acba54a21f5f1522", - "sha256:112b8774b0b4ee22368fec42749b94366bd9b536f8f74c3d4175d4395f5cbd31", - "sha256:11dd60b2ffddba85715d8a66bb39b95ddbe389ad2cfcf42c833f1bcde0878eaf", - "sha256:178f8a60fc24511c0eb756af741c476b87b610dba83270fce1e5a430204566a4", - "sha256:1b08027489ba8fedde72ddd233a5ea411b85a6ed78175f40285bd401bde7466d", - "sha256:1bf5be5ba34e19be579ae873da515a2836a2166d8d7ee43be6ff909eda42b72b", - "sha256:1ed7de3c86721b4e83ac440751329ec6a1102229aa18163f84c75b06b525ad7e", - "sha256:1eedaaccc9bb66581d4ae7c50e15856e335e57ef2734dbc5fd8ba3e2a4ab3cb6", - "sha256:243241c95174b5fb7204c04595852fe3943cc41f47aa14c3828bc18cd9d3b2d6", - "sha256:26bb3e8de93443d55e2e748e9fd87deb5f8075ca7bc0502cfc8be8687d69a2ec", - "sha256:271fa2184cf28bdded86bb6217c8e08d3a169fe0bbe9be5e8d96e8476b707122", - "sha256:28358c54fffadf0ae893f6c1050e8f8853e45df22483b7fff2f6ab6152f5d8bf", - "sha256:285019078537949cecd0190f3690a0b0125ff743d6a53dfeb7a4e6787af154f5", - "sha256:2893d778d4671ee627bac4037a075168b2673c57186fb1a57e993465dbd79a93", - "sha256:2a54027554ce9b129fc3d633c92fa33b30de9f08bc61b32c053dc9b537266fed", - "sha256:2c6ae11e6e93728d86aafc51ced98b1658a0080a7dd9417d24bfb955bb09c3c2", - "sha256:2cfa07c346a7ad07019c33fb9a63cf3acb1f5363c33bc73014e20d9fe8b01cdd", - "sha256:35d5631ce0af26318dba0ae0ac941c534453e42f569011585cb323b7774502a5", - "sha256:3614d280bf7aab0d3721b5ce0e73434acb90a2c993121b6e81a1c15c665298ac", - "sha256:3902df19540e9af4cc0c3ae75974c65d2c156b9257e91f5101a51f99136d834c", - "sha256:3aaf141d39f45322e44fc2c742e4b8b4098ead5317e5f884770c8df0c332da70", - "sha256:3d8abf7896a91fb97e7977d1aadfcc2c80415d6dc2f1d0fca5b8d0df247248f3", - "sha256:3e77febf227a1dc3220159355dba68faa13f8dca9335d97504abf428469fb18b", - "sha256:3e9212f52074fc9d72cf242a84063787ab8e21e0950d4d6709886fb62bcb91d5", - "sha256:3ee9d6f0b38efb22ad94c3b68ffebe4c47865cdf4b17f6806d6c674e1feb4246", - "sha256:4233df01a250b3984465faed12ad472f035b7cd5240ea3f7c76b7a7016084495", - "sha256:4263320ed887ed843f85beba67f8b2d1483b5947f2dc73a8b068924558bfeace", - "sha256:4ab923167cfd945abb9b51a407407cf19f5bee35001221f2911dc85ffd35ff4f", - "sha256:4caafd1a22e5eaa3732acb7672a497123354bef79a9d7ceed43387d25025e935", - "sha256:50fb62f8d8364978478b12d5f03bf028c6bc2af04082479299139dc26edf4c64", - "sha256:55ff4151cfd4bc635e51cfb1c59ac9f7196b256b12e3a57deb9e5742e65941ad", - "sha256:5b98b6c953e5c2bda51ab4d5b4f172617d462eebc7f4bfdc7c7e6b423f6da957", - "sha256:5c9ff044eb07c8468594d12602291c635da292308c8c619244e30698e7fc455a", - "sha256:5e9c206a1abc27e0588cf8b7c8246e51f1a16a103734f7750830a1ccb63f557a", - "sha256:5fb89edee2fa237584e532fbf78f0ddd1e49a47c7c8cfa153ab4849dc72a35e6", - "sha256:633462ef7e61d839171bf206551d5ab42b30b71cac8f10a64a662536e057fdef", - "sha256:66f8d2a17e5838dd6fb9be6baaba8e75ae2f5fa6b6b755d597184bfcd3cb0eba", - "sha256:6959bb9928c5c999aba4a3f5a6799d571ddc2c59ff49917ecf55be2bbb4e3722", - "sha256:698a79d295626ee292d1730bc2ef6e70a3ab135b1d79ada8fde3ed0047b65a10", - "sha256:721f9c4011b443b6e84505fc00cc7aadc9d1743f1c988e4c89353e19c4a968ee", - "sha256:72e680c1518733b73c994361e4b06441b92e973ef7d9449feec72e8ee4f713da", - "sha256:75307599f0d25bf6937248e5ac4e3bde5ea72ae6618623b86146ccc7845ed00b", - "sha256:754fba3084b70162a6b91efceee8a3f06b19e43dac3f71841662053c0584209a", - "sha256:759462b2d0aa5a04be5b3e37fb8183615f47014ae6b116e17036b131985cb731", - "sha256:7938c7b0599a05246d704b3f5e01be91a93b411d0d6cc62275f025293b8a11ce", - "sha256:7b77e07233925bd33fc0022b8537774423e4c6680b6436316c5075e79b6384f4", - "sha256:7e5413d2e2d86025e73f05510ad23dad5950ab8417b7fc6beaad99be8077138b", - "sha256:7f3240dcfa14d198dba24b8b9cb3b108c06b68d45b7babd9eefc1038fdf7e707", - "sha256:7f9682a8f71acdf59fd554b82b1c12f517118ee72c0f3944eda461606dfe7eb9", - "sha256:8d67beb6002441faef8251c45e24994de32c4c8686f7356a1f601ad7c466f7c3", - "sha256:9441af1d25aed96901f97ad83d5c3e35e6cd21a25ca5e4916c82d7dd0490a4fa", - "sha256:98b257ae1e83f81fb947a363a274c4eb66640212516becaff7bef09a5dceacaa", - "sha256:9e9f3a3ac919406bc0414bbbd76c6af99253c507150191ea79fab42fdb35982a", - "sha256:a1c66e71ecfd2a4acf0e4bd75e7a3605afa8f9b28a3b497e4ba962719df2be57", - "sha256:a1e17d8dc8e57d8e0fd21f8f0f0a5211b3fa258b2e444c2053471ef93fe25a00", - "sha256:a20cb698c4a59c534c6701b1c24a968ff2768b18ea2991f886bd8985ce17a89f", - "sha256:a970bfaf130c29a679b1d0a6e0f867483cea455ab1535fb427566a475078f27f", - "sha256:a98f510d86f689fcb486dc59e6e363af04151e5260ad1bdddb5625c10f1e95f8", - "sha256:a9d3b728f5a5873d84cba997b9d617c6090ca5721caaa691f3b1a78c60adc057", - "sha256:ad76f44f70aac3a54ceb1813ca630c53415da3a24fd93c570b2dfb4856591017", - "sha256:ae28144c1daa61366205d32abd8c90372790ff79fc60c1a8ad7fd3c8553a600e", - "sha256:b03a8d50b137ee758e4c73638b10747b7c39988eb8e6cd11abb7084266455165", - "sha256:b5a96fcac2f18e5a0a23a75cd27ce2656c66c11c127b0318e508aab436b77428", - "sha256:b5ef909a37e9738d146519657a1aab4584018746a18f71c692f2f22168ece40c", - "sha256:b79f5ced71efd70414a9a80bbbfaa7160da307723166f09b69773153bf17c590", - "sha256:b91cceb5add79ee563bd1f70b30896bd63bc5f78a11c1f00a1e931729ca4f1f4", - "sha256:b92f5654157de1379c509b15acec9d12ecf6e3bc1996571b6cb82a4302060447", - "sha256:c04ca91dda8a61584165825907f5c967ca09e9c65fe8966ee753a3f2b019fe1e", - "sha256:c1f8afa346ccd59e4e5630d5abb67aba6a9812fddf764fd7eb11f382a345f8cc", - "sha256:c5334a71f7dc1160382d45997e29f2637c02f8a26af41073189d79b95d3321f1", - "sha256:c617d7453a80e29d9973b926983b1e700a9377dbe021faa36041c78537d7b08c", - "sha256:c632419c3870507ca20a37c8f8f5352317aca097639e524ad129f58c125c61c6", - "sha256:c6760211eee3a76316cf328f5a8bd695b47b1626d21c8a27fb3b2473a884d597", - "sha256:c698d123ce5d8f2d0cd17f73336615f6a2e3bdcedac07a1291bb4d8e7d82a05a", - "sha256:c76b32eb2ab650a29e423525e84eb197c45504b1c1e6e17b6cc91fcfeb1a4b1d", - "sha256:c8f7e90b948dc9dcfff8003f1ea3af08b29c062f681c05fd798e36daa3f7e3e8", - "sha256:c9e799dac1ffbe7b10c1fd42fe4cd51371a549c6e108249bde9cd1200e8f59b4", - "sha256:cafa48f2133d4daa028473ede7d81cd1b9f9e6925e9e4003ebdf77010ee02f35", - "sha256:ce473a2351c018b06dd8d30d5da8ab5a0831056cc53b2006e2a8028172c37ce5", - "sha256:d31ed4987d72aabdf521eddfb6a72988703c091cfc0064330b9e5f8d6a042ff5", - "sha256:d550d7e9e7d8676b183b37d65b5cd8de13676a738973d330b59dc8312df9c5dc", - "sha256:d6adb81564af0cd428910f83fa7da46ce9ad47c56c0b22b50872bc4515d91966", - "sha256:d6f6512a90bd5cd9030a6237f5346f046c6f0e40af98657568fa45695d4de59d", - "sha256:d7031d493c4465dbc8d40bd6cafefef4bd472b17db0ab94c53e7909ee781b9ef", - "sha256:d9f75a06ecc68f159d5d7603b734e1ff6daa9497a929150f794013aa9f6e3f12", - "sha256:db7707dde9143a67b8812c7e66aeb2d843fe33cc8e374170f4d2c50bd8f2472d", - "sha256:e0397dd0b3955c61ef9b22838144aa4bef6f0796ba5cc8edfc64d468b93798b4", - "sha256:e0df046f2266e8586cf09d00588302a32923eb6386ced0ca5c9deade6af9a149", - "sha256:e14f86b871ea74c3fddc9a40e947d6a5d09def5adc2076ee61fb910a9014fb35", - "sha256:e5963ea87f88bddf7edd59644a35a0feecf75f8985430124c253612d4f7d27ae", - "sha256:e768267cbe051dd8d1c5305ba690bb153204a09bf2e3de3ae530de955f5b5580", - "sha256:e9cb79ecedfc156c0692257ac7ed415243b6c35dd969baa461a6888fc79f2f07", - "sha256:ed6f011bedca8585787e5082cce081bac3d30f54520097b2411351b3574e1219", - "sha256:f3429fb8e15b20961efca8c8b21432623d85db2228cc73fe22756c6637aa39e7", - "sha256:f35eff113ad430b5272bbfc18ba111c66ff525828f24898b4e146eb479a2cdda", - "sha256:f3a6cb95074777f1ecda2ca4fa7717caa9ee6e534f42b7575a8f0d4cb0c24013", - "sha256:f7356a6da0562190558c4fcc14f0281db191cdf4cb96e7604c06acfcee96df15", - "sha256:f88626e3f5e57432e6191cd0c5d6d6b319b635e70b40be2ffba713053e5147dd", - "sha256:fad784a31869747df4ac968a351e070c06ca377549e4ace94775aaa3ab33ee06", - "sha256:fc869af5cba24d45fb0399b0cfdbcefcf6910bf4dee5d74036a57cf5264b3ff4", - "sha256:fee513135b5a58f3bb6d89e48326cd5aa308e4bcdf2f7d59f67c861ada482bf8" + "sha256:0047638c3aa0dbcd0ab99ed1e549bbf0e142c9ecc173b6492868432d8989a046", + "sha256:006f4342fe729a368c6df36578d7a348c7c716be1da0a1a0f86e3021f8e98724", + "sha256:041f00419e1da7a03c46042453598479f45be3d787eb837af382bfc169c0db33", + "sha256:04ecf5c1ff4d589987b4d9882872f80ba13da7d42427234fce8f22efb43133bc", + "sha256:04f2b712a2206e13800a8136b07aaedc23af3facab84918e7aa89e4be0260032", + "sha256:0aeb3329c1721c43c58cae274d7d2ca85c1690d89485d9c63a006cb79a85771a", + "sha256:0e374c0ce0ca82e5b67cd61fb964077d40ec177dd2c4eda67dba130de09085c7", + "sha256:0f00c16e089282ad68a3820fd0c831c35d3194b7cdc31d6e469511d9bffc535c", + "sha256:174e46569968ddbbeb8a806d9922f17cd2b524aa753b468f35b97ff9c19cb718", + "sha256:1b221c2457d92a1fb3c97bee9095c874144d196f47c038462ae6e4a14436f7bc", + "sha256:208b3a70a98cf3710e97cabdc308a51cd4f28aa6e7bb11de3d56cd8b74bab98d", + "sha256:20f2712bd1cc26a3cc16c5a1bfee9ed1abc33d4cdf1aabd297fe0eb724df4272", + "sha256:24795c099453e3721fda5d8ddd45f5dfcc8e5a547ce7b8e9da06fecc3832e26f", + "sha256:2a0f156e9509cee987283abd2296ec816225145a13ed0391df8f71bf1d789e2d", + "sha256:2b2356688e5d958c4d5cb964af865bea84db29971d3e563fb78e46e20fe1848b", + "sha256:2c13777ecdbbba2077670285dd1fe50828c8742f6a4119dbef6f83ea13ad10fb", + "sha256:2d3ee4615df36ab8eb16c2507b11e764dcc11fd350bbf4da16d09cda11fcedef", + "sha256:2d53747da70a4e4b17f559569d5f9506420966083a31c5fbd84e764461c4444b", + "sha256:32bab0a56eac685828e00cc2f5d1200c548f8bc11f2e44abf311d6b548ce2e45", + "sha256:34d90ad8c045df9a4259c47d2e16a3f21fdb396665c94520dbfe8766e62187a4", + "sha256:369d9c6d4c714e36d4a03957b4783217a3ccd1e222cdd67d464a3a479fc17796", + "sha256:3a55fc10fdcbf1a4bd3c018eea422c52cf08700cf99c28b5cb10fe97ab77a0d3", + "sha256:3d2d8e4508e15fc05b31285c4b00ddf2e0eb94259c2dc896771966a163122a0c", + "sha256:3fab5f4a2c64a8fb64fc13b3d139848817a64d467dd6ed60dcdd6b479e7febc9", + "sha256:43dba99f00f1d37b2a0265a259592d05fcc8e7c19d140fe51c6e6f16faabeb1f", + "sha256:44d51febb7a114293ffd56c6cf4736cb31cd68c0fddd6aa303ed09ea5a48e029", + "sha256:493fe54318bed7d124ce272fc36adbf59d46729659b2c792e87c3b95649cdee9", + "sha256:4b28e5122829181de1898c2c97f81c0b3246d49f585f22743a1246420bb8d399", + "sha256:4cd031e63bc5f05bdcda120646a0d32f6d729486d0067f09d79c8db5368f4586", + "sha256:528927e63a70b4d5f3f5ccc1fa988a35456eb5d15f804d276709c33fc2f19bda", + "sha256:564c96b6076a98215af52f55efa90d8419cc2ef45d99e314fddefe816bc24f91", + "sha256:5db385bacd0c43f24be92b60c857cf760b7f10d8234f4bd4be67b5b20a7c0b6b", + "sha256:5ef877fa3bbfb40b388a5ae1cb00636a624690dcb9a29a65267054c9ea86d88a", + "sha256:5f6e3cec44ba05ee5cbdebe92d052f69b63ae792e7d05f1020ac5e964394080c", + "sha256:5fc13b44de6419d1e7a7e592a4885b323fbc2f46e1f22151e3a8ed3b8b920405", + "sha256:60748789e028d2a46fc1c70750454f83c6bdd0d05db50f5ae83e2db500b34da5", + "sha256:60d9b630c8025b9458a9d114e3af579a2c54bd32df601c4581bd054e85258143", + "sha256:619ca56a5468f933d940e1bf431c6f4e13bef8e688698b067ae68eb4f9b30e3a", + "sha256:630d3d8ea77eabd6cbcd2ea712e1c5cecb5b558d39547ac988351195db433f6c", + "sha256:63981feca3f110ed132fd217bf7768ee8ed738a55549883628ee3da75bb9cb78", + "sha256:66420986c9afff67ef0c5d1e4cdc2d0e5262f53ad11e4f90e5e22448df485bf0", + "sha256:675269d407a257b8c00a6b58205b72eec8231656506c56fd429d924ca00bb350", + "sha256:6a4a535013aeeef13c5532f802708cecae8d66c282babb5cd916379b72110cf7", + "sha256:6a727fd083009bc83eb83d6950f0c32b3c94c8b80a9b667c87f4bd1274ca30ba", + "sha256:6e1daf5bf6c2be39654beae83ee6b9a12347cb5aced9a29eecf12a2d25fff664", + "sha256:6eea559077d29486c68218178ea946263b87f1c41ae7f996b1f30a983c476a5a", + "sha256:75a810b7664c17f24bf2ffd7f92416c00ec84b49bb68e6a0d93e542406336b56", + "sha256:772cc1b2cd963e7e17e6cc55fe0371fb9c704d63e44cacec7b9b7f523b78919e", + "sha256:78884d155fd15d9f64f5d6124b486f3d3f7fd7cd71a78e9670a0f6f6ca06fb2d", + "sha256:79e8d804c2ccd618417e96720ad5cd076a86fa3f8cb310ea386a3e6229bae7d1", + "sha256:7e80d375134ddb04231a53800503752093dbb65dad8dabacce2c84cccc78e964", + "sha256:8097b3422d020ff1c44effc40ae58e67d93e60d540a65649d2cdaf9466030791", + "sha256:8205ee14463248d3349131bb8099efe15cd3ce83b8ef3ace63c7e976998e7124", + "sha256:8212ff58ac6dfde49946bea57474a386cca3f7706fc72c25b772b9ca4af6b79e", + "sha256:823e74ab6fbaa028ec89615ff6acb409e90ff45580c45920d4dfdddb069f2120", + "sha256:84e0566f15cf4d769dade9b366b7b87c959be472c92dffb70462dd0844d7cbad", + "sha256:896c41007931217a343eff197c34513c154267636c8056fb409eafd494c3dcdc", + "sha256:8aa362811ccdc1f8dadcc916c6d47e554169ab79559319ae9fae7d7752d0d60c", + "sha256:8b3b397eefecec8e8e39fa65c630ef70a24b09141a6f9fc17b3c3a50bed6b50e", + "sha256:8ebc7e65ca4b111d928b669713865f021b7773350eeac4a31d3e70144297baba", + "sha256:9168764133fd919f8dcca2ead66de0105f4ef5659cbb4fa044f7014bed9a1797", + "sha256:921ae54f9ecba3b6325df425cf72c074cd469dea843fb5743a26ca7fb2ccb149", + "sha256:92558d37d872e808944c3c96d0423b8604879a3d1c86fdad508d7ed91ea547d5", + "sha256:951cc481c0c395c4a08639a469d53b7d4afa252529a085418b82a6b43c45c240", + "sha256:998c01b8e71cf051c28f5d6f1187abbdf5cf45fc0efce5da6c06447cba997034", + "sha256:9abc80fe8c1f87218db116016de575a7998ab1629078c90840e8d11ab423ee25", + "sha256:9be4f99bee42ac107870c61dfdb294d912bf81c3c6d45538aad7aecab468b6b7", + "sha256:9c39438c55983d48f4bb3487734d040e22dad200dab22c41e331cee145e7a50d", + "sha256:9d7e8ce990ae17dda686f7e82fd41a055c668e13ddcf058e7fb5e9da20b57793", + "sha256:9ea7f4174d2e4194289cb0c4e172d83e79a6404297ff95f2875cf9ac9bced8ba", + "sha256:a18fc371e900a21d7392517c6f60fe859e802547309e94313cd8181ad9db004d", + "sha256:a36b452abbf29f68527cf52e181fced56685731c86b52e852053e38d8b60bc8d", + "sha256:a5b66d1b201cc71bc3081bc2f1fc36b0c1f268b773e03bbc39066651b9e18391", + "sha256:a824d2c7a703ba6daaca848f9c3d5cb93af0505be505de70e7e66829affd676e", + "sha256:a88c0d17d039333a41d9bf4616bd062f0bd7aa0edeb6cafe00a2fc2a804e944f", + "sha256:aa6800adc8204ce898c8a424303969b7aa6a5e4ad2789c13f8648739830323b7", + "sha256:aad911555286884be1e427ef0dc0ba3929e6821cbeca2194b13dc415a462c7fd", + "sha256:afc6e35f344490faa8276b5f2f7cbf71f88bc2cda4328e00553bd451728c571f", + "sha256:b9a4df06c35465ef4d81799999bba810c68d29972bf1c31db61bfdb81dd9d5bb", + "sha256:bb2954155bb8f63bb19d56d80e5e5320b61d71084617ed89efedb861a684baea", + "sha256:bbc4362e06f950c62cad3d4abf1191021b2ffaf0b31ac230fbf0526453eee75e", + "sha256:c0145295ca415668420ad142ee42189f78d27af806fcf1f32a18e51d47dd2052", + "sha256:c30ff468163a48535ee7e9bf21bd14c7a81147c0e58a36c1078289a8ca7af0bd", + "sha256:c347a20d79cedc0a7bd51c4d4b7dbc613ca4e65a756b5c3e57ec84bd43505b47", + "sha256:c43583ea8517ed2e780a345dd9960896afc1327e8cf3ac8239c167530397440d", + "sha256:c61a2cb0085c8783906b2f8b1f16a7e65777823c7f4d0a6aaffe26dc0d358dd9", + "sha256:c9ca89938dff18828a328af41ffdf3902405a19f4131c88e22e776a8e228c5a8", + "sha256:cc31e13ce212e14a539d430428cd365e74f8b2d534f8bc22dd4c9c55b277b875", + "sha256:cdabcd3beb2a6dca7027007473d8ef1c3b053347c76f685f5f060a00327b8b65", + "sha256:cf86f72d705fc2ef776bb7dd9e5fbba79d7e1f3e258bf9377f8204ad0fc1c51e", + "sha256:d09dc82af2d3c17e7dd17120b202a79b578d79f2b5424bda209d9966efeed114", + "sha256:d3aa13bdf38630da298f2e0d77aca967b200b8cc1473ea05248f6c5e9c9bdb44", + "sha256:d69d003296df4840bd445a5d15fa5b6ff6ac40496f956a221c4d1f6f7b4bc4d9", + "sha256:d6e109a454412ab82979c5b1b3aee0604eca4bbf9a02693bb9df027af2bfa91a", + "sha256:d8551e733626afec514b5d15befabea0dd70a343a9f23322860c4f16a9430205", + "sha256:d8754d872a5dfc3c5bf9c0e059e8107451364a30d9fd50f1f1a85c4fb9481164", + "sha256:d8f9a6e7fd5434817526815f09ea27f2746c4a51ee11bb3439065f5fc754db58", + "sha256:dbcbb6db5582ea33ce46a5d20a5793134b5365110d84df4e30b9d37c6fd40ad3", + "sha256:e0f3ef95795efcd3b2ec3fe0a5bcfb5dadf5e3996ea2117427e524d4fbf309c6", + "sha256:e13ae74a8a3a0c2f22f450f773e35f893484fcfacb00bb4344a7e0f4f48e1f97", + "sha256:e274f62cbd274359eff63e5c7e7274c913e8e09620f6a57aae66744b3df046d6", + "sha256:e838bf2bb0b91ee67bf2b889a1a841e5ecac06dd7a2b1ef4e6151e2ce155c7ae", + "sha256:e8acd55bd5b071156bae57b555f5d33697998752673b9de554dd82f5b5352727", + "sha256:e8e5ab32cf9eb3647450bc74eb201b27c185d3857276162c101c0f8c6374e098", + "sha256:ebcb786b9ff30b994d5969213a8430cbb984cdd7ea9fd6df06663194bd3c450c", + "sha256:ebea2821cdb5f9fef44933617be76185b80150632736f3d76e54829ab4a3b4d1", + "sha256:ed0ef550042a8dbcd657dfb284a8ee00f0ba269d3f2286b0493b15a5694f9fe8", + "sha256:eda5c1e2a715a4cbbca2d6d304988460942551e4e5e3b7457b50943cd741626d", + "sha256:f5c0ed12926dec1dfe7d645333ea59cf93f4d07750986a586f511c0bc61fe103", + "sha256:f6016bd950be4dcd047b7475fdf55fb1e1f59fc7403f387be0e8123e4a576d30", + "sha256:f9e0057a509e096e47c87f753136c9b10d7a91842d8042c2ee6866899a717c0d", + "sha256:fc1c892b1ec1f8cbd5da8de287577b455e388d9c328ad592eabbdcb6fc93bee5", + "sha256:fc2c1e1b00f88317d9de6b2c2b39b012ebbfe35fe5e7bef980fd2a91f6100a07", + "sha256:fd822f019ccccd75c832deb7aa040bb02d70a92eb15a2f16c7987b7ad4ee8d83" ], "markers": "python_version >= '3.9'", - "version": "==0.23.1" + "version": "==0.24.0" }, "s3transfer": { "hashes": [ @@ -801,11 +821,11 @@ }, "setuptools": { "hashes": [ - "sha256:199466a166ff664970d0ee145839f5582cb9bca7a0a3a2e795b6a9cb2308e9c6", - "sha256:43b4ee60e10b0d0ee98ad11918e114c70701bc6051662a9a675a0496c1a158f4" + "sha256:18fd474d4a82a5f83dac888df697af65afa82dec7323d09c3e37d1f14288da54", + "sha256:3e386e96793c8702ae83d17b853fb93d3e09ef82ec62722e61da5cd22376dcd8" ], "markers": "python_version >= '3.9'", - "version": "==76.0.0" + "version": "==78.1.0" }, "six": { "hashes": [ @@ -815,6 +835,14 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", "version": "==1.17.0" }, + "soupsieve": { + "hashes": [ + "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb", + "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9" + ], + "markers": "python_version >= '3.8'", + "version": "==2.6" + }, "sqlparse": { "hashes": [ "sha256:09f67787f56a0b16ecdbde1bfc7f5d9c3371ca683cfeaa8e6ff60b4807ec9272", @@ -825,11 +853,19 @@ }, "typing-extensions": { "hashes": [ - "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", - "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8" + "sha256:4b6cf02909eb5495cfbc3f6e8fd49217e6cc7944e145cdda8caa3734777f9e69", + "sha256:98795af00fb9640edec5b8e31fc647597b4691f099ad75f469a2616be1a76dff" ], "markers": "python_version >= '3.8'", - "version": "==4.12.2" + "version": "==4.13.1" + }, + "typing-inspection": { + "hashes": [ + "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", + "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122" + ], + "markers": "python_version >= '3.9'", + "version": "==0.4.0" }, "urllib3": { "hashes": [ @@ -939,91 +975,91 @@ "cognito-idp" ], "hashes": [ - "sha256:7636997894d217cd4ffe62367ad09df10e5f250e6dc5fc2ceb70ff1584be4100", - "sha256:da592a38fb66ddae0cae0a5842ce6ae25b4f206d268247350c15580febf32b26" + "sha256:36444606a7c1c10c9700dde590f7afb134546065553f761f36207c1feb847e0b", + "sha256:a3471040c098c4e82a87fafeb38deb66eb4966950a771c62eba0bf36834f69d6" ], "markers": "python_version >= '3.8'", - "version": "==1.37.10" + "version": "==1.37.29" }, "botocore-stubs": { "hashes": [ - "sha256:a7e9acc0085cb42332f55a5e99b7e06ee7b18b8be6ac9ef7ad8dbac2a5c44145", - "sha256:e6183ca37b7b3b5c36daae38d44040858cf7ce69d02797d4d3d4d800985c10b4" + "sha256:923127abb5fac0b8b0f11837a4641f2863bb4398b5bd6b11d1604134966c4bb6", + "sha256:c59898bf1d09bf6a9f491f4705c5696e74b83156b766aa1716867f11b8a04ea1" ], "markers": "python_version >= '3.8'", - "version": "==1.37.10" + "version": "==1.37.29" }, "coverage": { "extras": [ "toml" ], "hashes": [ - "sha256:00b2086892cf06c7c2d74983c9595dc511acca00665480b3ddff749ec4fb2a95", - "sha256:0533adc29adf6a69c1baa88c3d7dbcaadcffa21afbed3ca7a225a440e4744bf9", - "sha256:06097c7abfa611c91edb9e6920264e5be1d6ceb374efb4986f38b09eed4cb2fe", - "sha256:07e92ae5a289a4bc4c0aae710c0948d3c7892e20fd3588224ebe242039573bf0", - "sha256:0a9d8be07fb0832636a0f72b80d2a652fe665e80e720301fb22b191c3434d924", - "sha256:0e549f54ac5f301e8e04c569dfdb907f7be71b06b88b5063ce9d6953d2d58574", - "sha256:0ef01d70198431719af0b1f5dcbefc557d44a190e749004042927b2a3fed0702", - "sha256:0f16f44025c06792e0fb09571ae454bcc7a3ec75eeb3c36b025eccf501b1a4c3", - "sha256:14d47376a4f445e9743f6c83291e60adb1b127607a3618e3185bbc8091f0467b", - "sha256:1a936309a65cc5ca80fa9f20a442ff9e2d06927ec9a4f54bcba9c14c066323f2", - "sha256:1ceeb90c3eda1f2d8c4c578c14167dbd8c674ecd7d38e45647543f19839dd6ea", - "sha256:1f7ffa05da41754e20512202c866d0ebfc440bba3b0ed15133070e20bf5aeb5f", - "sha256:200e10beb6ddd7c3ded322a4186313d5ca9e63e33d8fab4faa67ef46d3460af3", - "sha256:220fa6c0ad7d9caef57f2c8771918324563ef0d8272c94974717c3909664e674", - "sha256:2251fabcfee0a55a8578a9d29cecfee5f2de02f11530e7d5c5a05859aa85aee9", - "sha256:2458f275944db8129f95d91aee32c828a408481ecde3b30af31d552c2ce284a0", - "sha256:299cf973a7abff87a30609879c10df0b3bfc33d021e1adabc29138a48888841e", - "sha256:2b996819ced9f7dbb812c701485d58f261bef08f9b85304d41219b1496b591ef", - "sha256:3688b99604a24492bcfe1c106278c45586eb819bf66a654d8a9a1433022fb2eb", - "sha256:3a1e465f398c713f1b212400b4e79a09829cd42aebd360362cd89c5bdc44eb87", - "sha256:488c27b3db0ebee97a830e6b5a3ea930c4a6e2c07f27a5e67e1b3532e76b9ef1", - "sha256:48cfc4641d95d34766ad41d9573cc0f22a48aa88d22657a1fe01dca0dbae4de2", - "sha256:4b467a8c56974bf06e543e69ad803c6865249d7a5ccf6980457ed2bc50312703", - "sha256:53c56358d470fa507a2b6e67a68fd002364d23c83741dbc4c2e0680d80ca227e", - "sha256:5d1095bbee1851269f79fd8e0c9b5544e4c00c0c24965e66d8cba2eb5bb535fd", - "sha256:641dfe0ab73deb7069fb972d4d9725bf11c239c309ce694dd50b1473c0f641c3", - "sha256:64cbb1a3027c79ca6310bf101014614f6e6e18c226474606cf725238cf5bc2d4", - "sha256:66fe626fd7aa5982cdebad23e49e78ef7dbb3e3c2a5960a2b53632f1f703ea45", - "sha256:676f92141e3c5492d2a1596d52287d0d963df21bf5e55c8b03075a60e1ddf8aa", - "sha256:69e62c5034291c845fc4df7f8155e8544178b6c774f97a99e2734b05eb5bed31", - "sha256:704c8c8c6ce6569286ae9622e534b4f5b9759b6f2cd643f1c1a61f666d534fe8", - "sha256:78f5243bb6b1060aed6213d5107744c19f9571ec76d54c99cc15938eb69e0e86", - "sha256:79cac3390bfa9836bb795be377395f28410811c9066bc4eefd8015258a7578c6", - "sha256:7ae6eabf519bc7871ce117fb18bf14e0e343eeb96c377667e3e5dd12095e0288", - "sha256:7e39e845c4d764208e7b8f6a21c541ade741e2c41afabdfa1caa28687a3c98cf", - "sha256:8161d9fbc7e9fe2326de89cd0abb9f3599bccc1287db0aba285cb68d204ce929", - "sha256:8bec2ac5da793c2685ce5319ca9bcf4eee683b8a1679051f8e6ec04c4f2fd7dc", - "sha256:959244a17184515f8c52dcb65fb662808767c0bd233c1d8a166e7cf74c9ea985", - "sha256:9b148068e881faa26d878ff63e79650e208e95cf1c22bd3f77c3ca7b1d9821a3", - "sha256:aa6f302a3a0b5f240ee201297fff0bbfe2fa0d415a94aeb257d8b461032389bd", - "sha256:ace9048de91293e467b44bce0f0381345078389814ff6e18dbac8fdbf896360e", - "sha256:ad7525bf0241e5502168ae9c643a2f6c219fa0a283001cee4cf23a9b7da75879", - "sha256:b01a840ecc25dce235ae4c1b6a0daefb2a203dba0e6e980637ee9c2f6ee0df57", - "sha256:b076e625396e787448d27a411aefff867db2bffac8ed04e8f7056b07024eed5a", - "sha256:b172f8e030e8ef247b3104902cc671e20df80163b60a203653150d2fc204d1ad", - "sha256:b1f097878d74fe51e1ddd1be62d8e3682748875b461232cf4b52ddc6e6db0bba", - "sha256:b95574d06aa9d2bd6e5cc35a5bbe35696342c96760b69dc4287dbd5abd4ad51d", - "sha256:bda1c5f347550c359f841d6614fb8ca42ae5cb0b74d39f8a1e204815ebe25750", - "sha256:cec6b9ce3bd2b7853d4a4563801292bfee40b030c05a3d29555fd2a8ee9bd68c", - "sha256:d1a987778b9c71da2fc8948e6f2656da6ef68f59298b7e9786849634c35d2c3c", - "sha256:d74c08e9aaef995f8c4ef6d202dbd219c318450fe2a76da624f2ebb9c8ec5d9f", - "sha256:e18aafdfb3e9ec0d261c942d35bd7c28d031c5855dadb491d2723ba54f4c3015", - "sha256:e216c5c45f89ef8971373fd1c5d8d1164b81f7f5f06bbf23c37e7908d19e8558", - "sha256:e695df2c58ce526eeab11a2e915448d3eb76f75dffe338ea613c1201b33bab2f", - "sha256:e7575ab65ca8399c8c4f9a7d61bbd2d204c8b8e447aab9d355682205c9dd948d", - "sha256:e995b3b76ccedc27fe4f477b349b7d64597e53a43fc2961db9d3fbace085d69d", - "sha256:ea31689f05043d520113e0552f039603c4dd71fa4c287b64cb3606140c66f425", - "sha256:eb5507795caabd9b2ae3f1adc95f67b1104971c22c624bb354232d65c4fc90b3", - "sha256:eb8668cfbc279a536c633137deeb9435d2962caec279c3f8cf8b91fff6ff8953", - "sha256:ecea0c38c9079570163d663c0433a9af4094a60aafdca491c6a3d248c7432827", - "sha256:f25d8b92a4e31ff1bd873654ec367ae811b3a943583e05432ea29264782dc32c", - "sha256:f60a297c3987c6c02ffb29effc70eadcbb412fe76947d394a1091a3615948e2f", - "sha256:f973643ef532d4f9be71dd88cf7588936685fdb576d93a79fe9f65bc337d9d73" + "sha256:042e7841a26498fff7a37d6fda770d17519982f5b7d8bf5278d140b67b61095f", + "sha256:04bfec25a8ef1c5f41f5e7e5c842f6b615599ca8ba8391ec33a9290d9d2db3a3", + "sha256:0915742f4c82208ebf47a2b154a5334155ed9ef9fe6190674b8a46c2fb89cb05", + "sha256:18c5ae6d061ad5b3e7eef4363fb27a0576012a7447af48be6c75b88494c6cf25", + "sha256:2931f66991175369859b5fd58529cd4b73582461877ecfd859b6549869287ffe", + "sha256:2e4b6b87bb0c846a9315e3ab4be2d52fac905100565f4b92f02c445c8799e257", + "sha256:3043ba1c88b2139126fc72cb48574b90e2e0546d4c78b5299317f61b7f718b78", + "sha256:379fe315e206b14e21db5240f89dc0774bdd3e25c3c58c2c733c99eca96f1ada", + "sha256:42421e04069fb2cbcbca5a696c4050b84a43b05392679d4068acbe65449b5c64", + "sha256:4dfd9a93db9e78666d178d4f08a5408aa3f2474ad4d0e0378ed5f2ef71640cb6", + "sha256:52a523153c568d2c0ef8826f6cc23031dc86cffb8c6aeab92c4ff776e7951b28", + "sha256:554fec1199d93ab30adaa751db68acec2b41c5602ac944bb19187cb9a41a8067", + "sha256:581a40c7b94921fffd6457ffe532259813fc68eb2bdda60fa8cc343414ce3733", + "sha256:5a26c0c795c3e0b63ec7da6efded5f0bc856d7c0b24b2ac84b4d1d7bc578d676", + "sha256:5a570cd9bd20b85d1a0d7b009aaf6c110b52b5755c17be6962f8ccd65d1dbd23", + "sha256:5aaeb00761f985007b38cf463b1d160a14a22c34eb3f6a39d9ad6fc27cb73008", + "sha256:5ac46d0c2dd5820ce93943a501ac5f6548ea81594777ca585bf002aa8854cacd", + "sha256:5c8a5c139aae4c35cbd7cadca1df02ea8cf28a911534fc1b0456acb0b14234f3", + "sha256:6b8af63b9afa1031c0ef05b217faa598f3069148eeee6bb24b79da9012423b82", + "sha256:769773614e676f9d8e8a0980dd7740f09a6ea386d0f383db6821df07d0f08545", + "sha256:771eb7587a0563ca5bb6f622b9ed7f9d07bd08900f7589b4febff05f469bea00", + "sha256:77af0f6447a582fdc7de5e06fa3757a3ef87769fbb0fdbdeba78c23049140a47", + "sha256:7a3d62b3b03b4b6fd41a085f3574874cf946cb4604d2b4d3e8dca8cd570ca501", + "sha256:821f7bcbaa84318287115d54becb1915eece6918136c6f91045bb84e2f88739d", + "sha256:89b1f4af0d4afe495cd4787a68e00f30f1d15939f550e869de90a86efa7e0814", + "sha256:8a1d96e780bdb2d0cbb297325711701f7c0b6f89199a57f2049e90064c29f6bd", + "sha256:8a40fcf208e021eb14b0fac6bdb045c0e0cab53105f93ba0d03fd934c956143a", + "sha256:8f99eb72bf27cbb167b636eb1726f590c00e1ad375002230607a844d9e9a2318", + "sha256:90e7fbc6216ecaffa5a880cdc9c77b7418c1dcb166166b78dbc630d07f278cc3", + "sha256:94ec0be97723ae72d63d3aa41961a0b9a6f5a53ff599813c324548d18e3b9e8c", + "sha256:95aa6ae391a22bbbce1b77ddac846c98c5473de0372ba5c463480043a07bff42", + "sha256:96121edfa4c2dfdda409877ea8608dd01de816a4dc4a0523356067b305e4e17a", + "sha256:a1f406a8e0995d654b2ad87c62caf6befa767885301f3b8f6f73e6f3c31ec3a6", + "sha256:a321c61477ff8ee705b8a5fed370b5710c56b3a52d17b983d9215861e37b642a", + "sha256:a5761c70c017c1b0d21b0815a920ffb94a670c8d5d409d9b38857874c21f70d7", + "sha256:a9abbccd778d98e9c7e85038e35e91e67f5b520776781d9a1e2ee9d400869487", + "sha256:ad80e6b4a0c3cb6f10f29ae4c60e991f424e6b14219d46f1e7d442b938ee68a4", + "sha256:b44674870709017e4b4036e3d0d6c17f06a0e6d4436422e0ad29b882c40697d2", + "sha256:b571bf5341ba8c6bc02e0baeaf3b061ab993bf372d982ae509807e7f112554e9", + "sha256:b8194fb8e50d556d5849753de991d390c5a1edeeba50f68e3a9253fbd8bf8ccd", + "sha256:b87eb6fc9e1bb8f98892a2458781348fa37e6925f35bb6ceb9d4afd54ba36c73", + "sha256:bbb5cc845a0292e0c520656d19d7ce40e18d0e19b22cb3e0409135a575bf79fc", + "sha256:be945402e03de47ba1872cd5236395e0f4ad635526185a930735f66710e1bd3f", + "sha256:bf13d564d310c156d1c8e53877baf2993fb3073b2fc9f69790ca6a732eb4bfea", + "sha256:cf60dd2696b457b710dd40bf17ad269d5f5457b96442f7f85722bdb16fa6c899", + "sha256:d1ba00ae33be84066cfbe7361d4e04dec78445b2b88bdb734d0d1cbab916025a", + "sha256:d39fc4817fd67b3915256af5dda75fd4ee10621a3d484524487e33416c6f3543", + "sha256:d766a4f0e5aa1ba056ec3496243150698dc0481902e2b8559314368717be82b1", + "sha256:dbf364b4c5e7bae9250528167dfe40219b62e2d573c854d74be213e1e52069f7", + "sha256:dd19608788b50eed889e13a5d71d832edc34fc9dfce606f66e8f9f917eef910d", + "sha256:e013b07ba1c748dacc2a80e69a46286ff145935f260eb8c72df7185bf048f502", + "sha256:e5d2b9be5b0693cf21eb4ce0ec8d211efb43966f6657807f6859aab3814f946b", + "sha256:e5ff52d790c7e1628241ffbcaeb33e07d14b007b6eb00a19320c7b8a7024c040", + "sha256:e75a2ad7b647fd8046d58c3132d7eaf31b12d8a53c0e4b21fa9c4d23d6ee6d3c", + "sha256:e7ac22a0bb2c7c49f441f7a6d46c9c80d96e56f5a8bc6972529ed43c8b694e27", + "sha256:ed2144b8a78f9d94d9515963ed273d620e07846acd5d4b0a642d4849e8d91a0c", + "sha256:f017a61399f13aa6d1039f75cd467be388d157cd81f1a119b9d9a68ba6f2830d", + "sha256:f1d8a2a57b47142b10374902777e798784abf400a004b14f1b0b9eaf1e528ba4", + "sha256:f2d32f95922927186c6dbc8bc60df0d186b6edb828d299ab10898ef3f40052fe", + "sha256:f319bae0321bc838e205bf9e5bc28f0a3165f30c203b610f17ab5552cff90323", + "sha256:f3c38e4e5ccbdc9198aecc766cedbb134b2d89bf64533973678dfcf07effd883", + "sha256:f9983d01d7705b2d1f7a95e10bbe4091fabc03a46881a256c2787637b087003f", + "sha256:fa260de59dfb143af06dcf30c2be0b200bed2a73737a8a59248fcb9fa601ef0f" ], "markers": "python_version >= '3.9'", - "version": "==7.6.12" + "version": "==7.8.0" }, "debugpy": { "hashes": [ @@ -1077,12 +1113,12 @@ }, "django": { "hashes": [ - "sha256:b983238dfa2eb2e6b27ebb815e14f0741cf186606eb7bcd857e740174017c50e", - "sha256:f9d4b7b87a9dae248d5f20cec940cf7290e07d508d6d8432e3c2cabf09b3b0ff" + "sha256:29019a5763dbd48da1720d687c3522ef40d1c61be6fb2fad27ed79e9f655bc11", + "sha256:e762bef8629ee704de215ebbd32062b84f4e56327eed412e5544f6f6eb1dfd74" ], "index": "pypi", "markers": "python_version >= '3.10'", - "version": "==5.0.13" + "version": "==5.0.14" }, "django-debug-toolbar": { "hashes": [ @@ -1146,20 +1182,20 @@ }, "faker": { "hashes": [ - "sha256:2598f78b76710a4ed05e197dda5235be409b4c291ba5c9c7514989cfbc7a5144", - "sha256:d2e4e2a30d459a8ec0ae52a552aa51c48973cb32cf51107dee90f58a8322a880" + "sha256:ad9dc66a3b84888b837ca729e85299a96b58fdaef0323ed0baace93c9614af06", + "sha256:dc2f730be71cb770e9c715b13374d80dbcee879675121ab51f9683d262ae9a1c" ], "index": "pypi", "markers": "python_version >= '3.9'", - "version": "==37.0.0" + "version": "==37.1.0" }, "iniconfig": { "hashes": [ - "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", - "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374" + "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", + "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760" ], - "markers": "python_version >= '3.7'", - "version": "==2.0.0" + "markers": "python_version >= '3.8'", + "version": "==2.1.0" }, "ipdb": { "hashes": [ @@ -1172,11 +1208,11 @@ }, "ipython": { "hashes": [ - "sha256:143ef3ea6fb1e1bffb4c74b114051de653ffb7737a3f7ab1670e657ca6ae8c44", - "sha256:ec7b479e3e5656bf4f58c652c120494df1820f4f28f522fb7ca09e213c2aab52" + "sha256:2df07257ec2f84a6b346b8d83100bcf8fa501c6e01ab75cd3799b0bb253b3d2a", + "sha256:a47e13a5e05e02f3b8e1e7a0f9db372199fe8c3763532fe7a1e0379e4e135f16" ], "markers": "python_version >= '3.11'", - "version": "==9.0.2" + "version": "==9.1.0" }, "ipython-pygments-lexers": { "hashes": [ @@ -1276,11 +1312,11 @@ }, "mypy-boto3-cognito-idp": { "hashes": [ - "sha256:140305ec710cf92b47eef9213e8988c61d8645222137141a099c74a3afc4a187", - "sha256:24ab9229e8e55562f6e6dcef3db724b4db82b0eca534c79618ec27486806ef0b" + "sha256:107cace7aca58673bac3058b28c00162b8dff4adc3d369596e9720850957c512", + "sha256:43cc95141d50869bdac62798d84691d5f55e0a5f10e6d2f9328ad340956ddfe6" ], "markers": "python_version >= '3.8'", - "version": "==1.37.5" + "version": "==1.37.13.post1" }, "mypy-extensions": { "hashes": [ @@ -1324,11 +1360,11 @@ }, "platformdirs": { "hashes": [ - "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", - "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb" + "sha256:a03875334331946f13c549dbd8f4bac7a13a50a895a0eb1e8c6a8ace80d40a94", + "sha256:eb437d586b6a0986388f0d6f74aa0cde27b48d0e3d66843640bfb6bdcdb6e351" ], - "markers": "python_version >= '3.8'", - "version": "==4.3.6" + "markers": "python_version >= '3.9'", + "version": "==4.3.7" }, "pluggy": { "hashes": [ @@ -1370,12 +1406,12 @@ }, "pylint": { "hashes": [ - "sha256:38d0f784644ed493d91f76b5333a0e370a1c1bc97c22068a77523b4bf1e82c31", - "sha256:7cb170929a371238530b2eeea09f5f28236d106b70308c3d46a9c0cf11634633" + "sha256:8b7c2d3e86ae3f94fb27703d521dd0b9b6b378775991f504d7c3a6275aa0a6a6", + "sha256:b634a041aac33706d56a0d217e6587228c66427e20ec21a019bc4cdee48c040a" ], "index": "pypi", "markers": "python_full_version >= '3.9.0'", - "version": "==3.3.5" + "version": "==3.3.6" }, "pylint-django": { "hashes": [ @@ -1405,21 +1441,21 @@ }, "pytest-cov": { "hashes": [ - "sha256:eee6f1b9e61008bd34975a4d5bab25801eb31898b032dd55addc93e96fcaaa35", - "sha256:fde0b595ca248bb8e2d76f020b465f3b107c9632e6a1d1705f17834c89dcadc0" + "sha256:46935f7aaefba760e716c2ebfbe1c216240b9592966e7da99ea8292d4d3e2a0a", + "sha256:bddf29ed2d0ab6f4df17b4c55b0a657287db8684af9c42ea546b21b1041b3dde" ], "index": "pypi", "markers": "python_version >= '3.9'", - "version": "==6.0.0" + "version": "==6.1.1" }, "pytest-django": { "hashes": [ - "sha256:1091b20ea1491fd04a310fc9aaff4c01b4e8450e3b157687625e16a6b5f3a366", - "sha256:57c74ef3aa9d89cae5a5d73fbb69a720a62673ade7ff13b9491872409a3f5918" + "sha256:1b63773f648aa3d8541000c26929c1ea63934be1cfa674c76436966d73fe6a10", + "sha256:a949141a1ee103cb0e7a20f1451d355f83f5e4a5d07bdd4dcfdd1fd0ff227991" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==4.10.0" + "version": "==4.11.1" }, "pytest-xdist": { "hashes": [ @@ -1492,19 +1528,19 @@ }, "rich": { "hashes": [ - "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", - "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90" + "sha256:1c9491e1951aac09caffd42f448ee3d04e58923ffe14993f6e83068dc395d7e0", + "sha256:82f1bc23a6a21ebca4ae0c45af9bdbc492ed20231dcb63f297d6d1021a9d5725" ], "markers": "python_full_version >= '3.8.0'", - "version": "==13.9.4" + "version": "==14.0.0" }, "setuptools": { "hashes": [ - "sha256:199466a166ff664970d0ee145839f5582cb9bca7a0a3a2e795b6a9cb2308e9c6", - "sha256:43b4ee60e10b0d0ee98ad11918e114c70701bc6051662a9a675a0496c1a158f4" + "sha256:18fd474d4a82a5f83dac888df697af65afa82dec7323d09c3e37d1f14288da54", + "sha256:3e386e96793c8702ae83d17b853fb93d3e09ef82ec62722e61da5cd22376dcd8" ], "markers": "python_version >= '3.9'", - "version": "==76.0.0" + "version": "==78.1.0" }, "sqlparse": { "hashes": [ @@ -1547,28 +1583,28 @@ }, "types-awscrt": { "hashes": [ - "sha256:f3f2578ff74a254a79882b95961fb493ba217cebc350b3eb239d1cd948d4d7fa", - "sha256:fc6eae56f8dc5a3f8cc93cc2c7c332fa82909f8284fbe25e014c575757af397d" + "sha256:7bcd649aedca3c41007ca5757096d3b3bdb454b73ca66970ddae6c2c2f541c8c", + "sha256:e11298750c99647f7f3b98d6d6d648790096cd32d445fd0d49a6041a63336c9a" ], "markers": "python_version >= '3.8'", - "version": "==0.24.1" + "version": "==0.25.7" }, "types-gevent": { "hashes": [ - "sha256:63eab6b2b823ccbb5ffc865c4fc97f45b1d0ab46af7b559eefbaa68b239b4bc3", - "sha256:dd19d524f1ebd1c8bb2136abd622d7200ca73bd14f6e8d6a56ab4051e5f8778f" + "sha256:1443f796a442062698e67d818fca50aa88067dee4021d457a7c0c6bedd6f46ca", + "sha256:6764faf861ea99250c38179c58076392c44019ac3393029f71b06c4a15e8c1d1" ], "index": "pypi", "markers": "python_version >= '3.9'", - "version": "==24.11.0.20250305" + "version": "==24.11.0.20250401" }, "types-greenlet": { "hashes": [ - "sha256:77515243c2f8b9c745b6924eeea202c1d2a18acecad3519da2afa677be6ee208", - "sha256:7bcf57f53e1036c9acb942ea87bf776c39995e63357a4502541eebe5db0b5897" + "sha256:77987f3249b0f21415dc0254057e1ae4125a696a9bba28b0bcb67ee9e3dc14f6", + "sha256:949389b64c34ca9472f6335189e9fe0b2e9704436d4f0850e39e9b7145909082" ], - "markers": "python_version >= '3.8'", - "version": "==3.1.0.20241221" + "markers": "python_version >= '3.9'", + "version": "==3.1.0.20250401" }, "types-nanoid": { "hashes": [ @@ -1581,19 +1617,28 @@ }, "types-psutil": { "hashes": [ - "sha256:1447a30c282aafefcf8941ece854e1100eee7b0296a9d9be9977292f0269b121", - "sha256:1e642cdafe837b240295b23b1cbd4691d80b08a07d29932143cbbae30eb0db9c" + "sha256:2a7d663c0888a079fc1643ebc109ad12e57a21c9552a9e2035da504191336dbf", + "sha256:ed23f7140368104afe4e05a6085a5fa56fbe8c880a0f4dfe8d63e041106071ed" ], "markers": "python_version >= '3.9'", - "version": "==7.0.0.20250218" + "version": "==7.0.0.20250401" }, "types-pyyaml": { "hashes": [ - "sha256:7f07622dbd34bb9c8b264fe860a17e0efcad00d50b5f27e93984909d9363498c", - "sha256:fa4d32565219b68e6dee5f67534c722e53c00d1cfc09c435ef04d7353e1e96e6" + "sha256:652348fa9e7a203d4b0d21066dfb00760d3cbd5a15ebb7cf8d33c88a49546681", + "sha256:d7c13c3e6d335b6af4b0122a01ff1d270aba84ab96d1a1a1063ecba3e13ec075" ], - "markers": "python_version >= '3.8'", - "version": "==6.0.12.20241230" + "markers": "python_version >= '3.9'", + "version": "==6.0.12.20250402" + }, + "types-requests": { + "hashes": [ + "sha256:72ff80f84b15eb3aa7a8e2625fffb6a93f2ad5a0c20215fc1dcfa61117bcb2a2", + "sha256:c9e67228ea103bd811c96984fac36ed2ae8da87a36a633964a21f199d60baf32" + ], + "index": "pypi", + "markers": "python_version >= '3.9'", + "version": "==2.32.0.20250328" }, "types-s3transfer": { "hashes": [ @@ -1605,19 +1650,27 @@ }, "typing-extensions": { "hashes": [ - "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d", - "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8" + "sha256:4b6cf02909eb5495cfbc3f6e8fd49217e6cc7944e145cdda8caa3734777f9e69", + "sha256:98795af00fb9640edec5b8e31fc647597b4691f099ad75f469a2616be1a76dff" ], "markers": "python_version >= '3.8'", - "version": "==4.12.2" + "version": "==4.13.1" }, "tzdata": { "hashes": [ - "sha256:24894909e88cdb28bd1636c6887801df64cb485bd593f2fd83ef29075a81d694", - "sha256:7e127113816800496f027041c570f50bcd464a020098a3b6b199517772303639" + "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", + "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9" ], "markers": "python_version >= '2'", - "version": "==2025.1" + "version": "==2025.2" + }, + "urllib3": { + "hashes": [ + "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df", + "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d" + ], + "markers": "python_version >= '3.9'", + "version": "==2.3.0" }, "wcwidth": { "hashes": [ diff --git a/app/bod/management/commands/bod_sync.py b/app/bod/management/commands/bod_sync.py index 19443fd1..bea940fc 100644 --- a/app/bod/management/commands/bod_sync.py +++ b/app/bod/management/commands/bod_sync.py @@ -5,6 +5,7 @@ from bod.models import BodContactOrganisation from bod.models import BodDataset +from bod.models import BodGeocatPublish from bod.models import BodTranslations from distributions.models import Attribution from distributions.models import Dataset @@ -271,6 +272,36 @@ def import_datasets(self) -> None: ) continue + if legacy_id in (1485, 1486): + # Skip the following datasets as they are hidden in public BGDI services: + # - ch.swisstopo.konsultation-lk10-flurnamen (1485) + # - ch.swisstopo.konsultation-lk10-siedlungsnamen (1486) + # They were used for name verifications of the cantons in the context of the KONAM + # project and are to be deleted. + # Layer ch.swisstopo.landeskarte-farbe-10 (952) has the same + # geocat_id ("cb0f8401-c49a-4bdf-aff6-40a7015ba43a"). + self.print(f"skipping dataset '{bod_dataset.id_dataset}'") + continue + + # Get meta information title and description + bod_meta = BodGeocatPublish.objects.filter(fk_id_dataset=bod_dataset.id_dataset).first() + if not bod_meta: + bod_meta = BodGeocatPublish() + + if not bod_meta.bezeichnung_de: + bod_meta.bezeichnung_de = "#Missing" + if not bod_meta.bezeichnung_fr: + bod_meta.bezeichnung_fr = "#Missing" + if not bod_meta.abstract_de: + bod_meta.abstract_de = "#Missing" + if not bod_meta.abstract_fr: + bod_meta.abstract_fr = "#Missing" + # If english is missing, geocat defaults to german translation + if not bod_meta.bezeichnung_en: + bod_meta.bezeichnung_en = bod_meta.bezeichnung_de + if not bod_meta.abstract_en: + bod_meta.abstract_en = bod_meta.abstract_de + # Get or create dataset is_new_model = False dataset = Dataset.objects.filter( @@ -281,13 +312,24 @@ def import_datasets(self) -> None: dataset = Dataset.objects.create( provider=attribution.provider, attribution=attribution, + geocat_id=bod_dataset.fk_geocat, + title_de=bod_meta.bezeichnung_de, + title_fr=bod_meta.bezeichnung_fr, + title_en=bod_meta.bezeichnung_en, + title_it=bod_meta.bezeichnung_it, + title_rm=bod_meta.bezeichnung_rm, + description_de=bod_meta.abstract_de, + description_fr=bod_meta.abstract_fr, + description_en=bod_meta.abstract_en, + description_it=bod_meta.abstract_it, + description_rm=bod_meta.abstract_rm, _legacy_id=legacy_id, dataset_id='undefined' ) self.increment_counter('dataset', 'added') self.print(f"Added dataset '{bod_dataset.id_dataset}'") - self.update_dataset(dataset, bod_dataset, is_new_model) + self.update_dataset(dataset, bod_dataset, bod_meta, is_new_model) dataset.save() @@ -298,11 +340,18 @@ def import_datasets(self) -> None: model_class = model.split('.')[-1].lower() self.increment_counter(model_class, 'removed', count) - def update_dataset(self, dataset: Dataset, bod_dataset: BodDataset, is_new_model: bool) -> None: + def update_dataset( + self, + dataset: Dataset, + bod_dataset: BodDataset, + bod_meta: BodGeocatPublish, + is_new_model: bool + ) -> None: """ Update the attributes of a dataset. """ any_changed = False - for dataset_attribute, bod_dataset_attribute in (('dataset_id', 'id_dataset'),): + for dataset_attribute, bod_dataset_attribute in (('dataset_id', 'id_dataset'), + ('geocat_id', 'fk_geocat'),): changed = self.update_model( dataset, dataset_attribute, @@ -310,6 +359,20 @@ def update_dataset(self, dataset: Dataset, bod_dataset: BodDataset, is_new_model is_new_model ) any_changed = any_changed or changed + for dataset_attribute, bod_dataset_attribute in (('title_de', 'bezeichnung_de'), + ('title_fr', 'bezeichnung_fr'), + ('title_en', 'bezeichnung_en'), + ('title_it', 'bezeichnung_it'), + ('title_rm', 'bezeichnung_rm'), + ('description_de', 'abstract_de'), + ('description_fr', 'abstract_fr'), + ('description_en', 'abstract_en'), + ('description_it', 'abstract_it'), + ('description_rm', 'abstract_rm'),): + changed = self.update_model( + dataset, dataset_attribute, getattr(bod_meta, bod_dataset_attribute), is_new_model + ) + any_changed = any_changed or changed if any_changed and not is_new_model: self.increment_counter('dataset', 'updated') diff --git a/app/bod/migrations/0001_initial.py b/app/bod/migrations/0001_initial.py index dbf81b9e..6e15ea67 100644 --- a/app/bod/migrations/0001_initial.py +++ b/app/bod/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.0.12 on 2025-03-11 11:57 +# Generated by Django 5.0.14 on 2025-04-10 07:12 from django.conf import settings from django.db import migrations @@ -38,6 +38,7 @@ class Migration(migrations.Migration): fields=[ ('id', models.AutoField(primary_key=True, serialize=False, unique=True)), ('id_dataset', models.TextField()), + ('fk_geocat', models.TextField()), ('fk_contactorganisation_id', models.IntegerField(blank=True, null=True)), ('staging', models.TextField(blank=True, null=True)), ], @@ -46,6 +47,27 @@ class Migration(migrations.Migration): 'managed': settings.TESTING, }, ), + migrations.CreateModel( + name='BodGeocatPublish', + fields=[ + ('bgdi_id', models.AutoField(primary_key=True, serialize=False, unique=True)), + ('fk_id_dataset', models.TextField()), + ('bezeichnung_de', models.CharField(blank=True, max_length=5000, null=True)), + ('bezeichnung_fr', models.CharField(blank=True, max_length=5000, null=True)), + ('bezeichnung_it', models.CharField(blank=True, max_length=5000, null=True)), + ('bezeichnung_rm', models.CharField(blank=True, max_length=5000, null=True)), + ('bezeichnung_en', models.CharField(blank=True, max_length=5000, null=True)), + ('abstract_de', models.CharField(blank=True, max_length=5000, null=True)), + ('abstract_fr', models.CharField(blank=True, max_length=5000, null=True)), + ('abstract_it', models.CharField(blank=True, max_length=5000, null=True)), + ('abstract_rm', models.CharField(blank=True, max_length=5000, null=True)), + ('abstract_en', models.CharField(blank=True, max_length=5000, null=True)), + ], + options={ + 'db_table': 'geocat_publish', + 'managed': settings.TESTING, + }, + ), migrations.CreateModel( name='BodTranslations', fields=[ diff --git a/app/bod/models.py b/app/bod/models.py index 905873f6..9d075091 100644 --- a/app/bod/models.py +++ b/app/bod/models.py @@ -27,6 +27,7 @@ class Meta: class BodDataset(models.Model): id = models.AutoField(unique=True, primary_key=True) id_dataset = models.TextField() + fk_geocat = models.TextField() fk_contactorganisation_id = models.IntegerField(blank=True, null=True) staging = models.TextField(blank=True, null=True) @@ -47,3 +48,24 @@ class BodTranslations(models.Model): class Meta: managed = settings.TESTING db_table = 'translations' + + +class BodGeocatPublish(models.Model): + bgdi_id = models.AutoField(unique=True, primary_key=True) + fk_id_dataset = models.TextField() + + bezeichnung_de = models.CharField(max_length=5000, blank=True, null=True) + bezeichnung_fr = models.CharField(max_length=5000, blank=True, null=True) + bezeichnung_it = models.CharField(max_length=5000, blank=True, null=True) + bezeichnung_rm = models.CharField(max_length=5000, blank=True, null=True) + bezeichnung_en = models.CharField(max_length=5000, blank=True, null=True) + + abstract_de = models.CharField(max_length=5000, blank=True, null=True) + abstract_fr = models.CharField(max_length=5000, blank=True, null=True) + abstract_it = models.CharField(max_length=5000, blank=True, null=True) + abstract_rm = models.CharField(max_length=5000, blank=True, null=True) + abstract_en = models.CharField(max_length=5000, blank=True, null=True) + + class Meta: + managed = settings.TESTING + db_table = 'geocat_publish' diff --git a/app/config/logging-cfg-local.yaml b/app/config/logging-cfg-local.yaml index 2e811f9c..12b977e2 100644 --- a/app/config/logging-cfg-local.yaml +++ b/app/config/logging-cfg-local.yaml @@ -4,7 +4,7 @@ disable_existing_loggers: False root: handlers: - console - level: DEBUG + level: INFO propagate: True # configure loggers per app diff --git a/app/distributions/admin.py b/app/distributions/admin.py index 35a216eb..ae1c17d6 100644 --- a/app/distributions/admin.py +++ b/app/distributions/admin.py @@ -1,4 +1,8 @@ +from typing import Any + from django.contrib import admin +from django.http import HttpRequest +from django.utils.html import format_html from .models import Attribution from .models import Dataset @@ -17,13 +21,34 @@ class AttributionAdmin(admin.ModelAdmin): # type:ignore[type-arg] class DatasetAdmin(admin.ModelAdmin): # type:ignore[type-arg] '''Admin View for Dataset''' - list_display = ('dataset_id', 'provider') + list_display = ('dataset_id', 'title_en', 'provider') list_filter = (('provider', admin.RelatedOnlyFieldListFilter),) + def get_form( + self, + request: HttpRequest, + obj: Dataset | None = None, + change: bool = False, + **kwargs: Any + ) -> Any: + form = super().get_form(request, obj=obj, change=change, **kwargs) + if obj is not None: + self._add_geocat_url_help_text(form, obj.geocat_id) + return form + + def _add_geocat_url_help_text(self, form: Any, geocat_id: str) -> None: + form.base_fields["geocat_id"].help_text = format_html( + "{url}", + url=f"https://www.geocat.ch/datahub/dataset/{geocat_id}", + ) + @admin.register(PackageDistribution) class PackageDistributionAdmin(admin.ModelAdmin): # type:ignore[type-arg] '''Admin View for Package Distribution''' list_display = ('package_distribution_id', 'managed_by_stac', 'dataset') - list_filter = ('managed_by_stac',) + list_filter = ( + 'managed_by_stac', + ('dataset__provider', admin.RelatedOnlyFieldListFilter), + ) diff --git a/app/distributions/api.py b/app/distributions/api.py index c05c3f58..8f4e0586 100644 --- a/app/distributions/api.py +++ b/app/distributions/api.py @@ -45,12 +45,28 @@ def attribution_to_response(model: Attribution, lang: LanguageCode) -> Attributi return response -def dataset_to_response(model: Dataset) -> DatasetSchema: +def dataset_to_response(model: Dataset, lang: LanguageCode) -> DatasetSchema: """ - Maps the given model to the corresponding schema. + Transforms the given model using the given language into a response object. """ response = DatasetSchema( id=model.dataset_id, + title=get_translation(model, "title", lang), + title_translations=TranslationsSchema( + de=model.title_de, + fr=model.title_fr, + en=model.title_en, + it=model.title_it, + rm=model.title_rm, + ), + description=get_translation(model, "description", lang), + description_translations=TranslationsSchema( + de=model.description_de, + fr=model.description_fr, + en=model.description_en, + it=model.description_it, + rm=model.description_rm, + ), created=model.created, updated=model.updated, provider_id=model.provider.provider_id, @@ -148,12 +164,15 @@ def attributions(request: HttpRequest, exclude_none=True, auth=PermissionAuth('distributions.view_dataset') ) -def dataset(request: HttpRequest, dataset_id: str) -> DatasetSchema: +def dataset( + request: HttpRequest, dataset_id: str, lang: LanguageCode | None = None +) -> DatasetSchema: """ Get the dataset with the given ID. """ model = get_object_or_404(Dataset, dataset_id=dataset_id) - response = dataset_to_response(model) + lang_to_use = get_language(lang, request.headers) + response = dataset_to_response(model, lang_to_use) return response @@ -163,7 +182,8 @@ def dataset(request: HttpRequest, dataset_id: str) -> DatasetSchema: exclude_none=True, auth=PermissionAuth('distributions.view_dataset') ) -def datasets(request: HttpRequest) -> dict[str, list[DatasetSchema]]: +def datasets(request: HttpRequest, + lang: LanguageCode | None = None) -> dict[str, list[DatasetSchema]]: """ Get all datasets. @@ -171,6 +191,6 @@ def datasets(request: HttpRequest) -> dict[str, list[DatasetSchema]]: corresponding endpoint for a specific attribution. """ models = Dataset.objects.order_by("dataset_id").all() - - responses = [dataset_to_response(model) for model in models] + lang_to_use = get_language(lang, request.headers) + responses = [dataset_to_response(model, lang_to_use) for model in models] return {"items": responses} diff --git a/app/distributions/management/commands/stac_harvest.py b/app/distributions/management/commands/stac_sync.py similarity index 59% rename from app/distributions/management/commands/stac_harvest.py rename to app/distributions/management/commands/stac_sync.py index e359b609..74c8fece 100644 --- a/app/distributions/management/commands/stac_harvest.py +++ b/app/distributions/management/commands/stac_sync.py @@ -1,13 +1,17 @@ from difflib import SequenceMatcher +from re import split from typing import Any from typing import Literal from typing import TypedDict from typing import cast +from urllib.parse import urljoin +from bs4 import BeautifulSoup from distributions.models import Dataset from distributions.models import PackageDistribution from pystac.collection import Collection from pystac_client import Client +from requests import get from utils.command import CommandHandler from utils.command import CustomBaseCommand @@ -26,6 +30,8 @@ def __init__(self, command: CustomBaseCommand, options: dict['str', Any]): self.dry_run = options['dry_run'] self.similarity = options['similarity'] self.url = options['url'] + self.endpoint = options['endpoint'] + self.default_dataset = options['default_dataset'] self.counts: dict[str, Counter] = {} def increment_counter(self, model_name: str, operation: Operation, value: int = 1) -> None: @@ -37,11 +43,55 @@ def increment_counter(self, model_name: str, operation: Operation, value: int = def clear_package_distributions(self) -> None: """ Remove existing package distributions previously imported from STAC. """ - _, cleared = PackageDistribution.objects.filter(managed_by_stac=True).delete() + _, cleared = PackageDistribution.objects.filter(_legacy_imported=True).delete() for model_class, count in cleared.items(): model_name = model_class.split('.')[-1].lower() self.increment_counter(model_name, 'cleared', count) + def update_package_distribution( + self, collection_id: str, managed_by_stac: bool + ) -> Dataset | None: + """ Create or update the package distribution with the given ID. """ + managed = 'managed' if managed_by_stac else 'unmanaged' + + # Get dataset + dataset = ( + Dataset.objects.filter(dataset_id=collection_id).first() or + Dataset.objects.filter(dataset_id=self.default_dataset).first() + ) + if not dataset: + self.print_warning("No dataset for collection id '%s'", collection_id) + if self.default_dataset: + self.print_warning("Default dataset '%s' does not exist", self.default_dataset) + return None + + # Get or create package distribution + package_distribution = PackageDistribution.objects.filter( + package_distribution_id=collection_id, _legacy_imported=True + ).first() + if not package_distribution: + package_distribution = PackageDistribution.objects.create( + package_distribution_id=collection_id, + _legacy_imported=True, + managed_by_stac=managed_by_stac, + dataset=dataset + ) + self.increment_counter('package_distribution', 'added') + self.print(f"Added package distribution '{collection_id}' ({managed})") + + # Update package distribution + if ( + package_distribution.managed_by_stac != managed_by_stac or + package_distribution.dataset != dataset + ): + package_distribution.managed_by_stac = managed_by_stac + package_distribution.dataset = dataset + package_distribution.save() + self.increment_counter('package_distribution', 'updated') + self.print(f"Updated package distribution '{collection_id}' ({managed})") + + return dataset + def import_package_distributions(self) -> None: """ Import package distributions from STAC. @@ -53,43 +103,47 @@ def import_package_distributions(self) -> None: """ processed = set() - # Get collections - client = Client.open(self.url) + # Get managed collections from STAC API + client = Client.open(urljoin(self.url, self.endpoint)) for collection in client.collection_search().collections(): collection_id = collection.id - processed.add(collection_id) - # Get dataset - dataset = Dataset.objects.filter(dataset_id=collection_id).first() + dataset = self.update_package_distribution(collection_id, True) if not dataset: - self.print_warning("No dataset for collection id '%s'", collection_id) continue - # Get or create package distribution - package_distribution = PackageDistribution.objects.filter( - package_distribution_id=collection_id - ).first() - if not package_distribution: - package_distribution = PackageDistribution.objects.create( - package_distribution_id=collection_id, managed_by_stac=True, dataset=dataset - ) - self.increment_counter('package_distribution', 'added') - self.print(f"Added package distribution '{collection_id}'") - - # Update package distribution - if not package_distribution.managed_by_stac or package_distribution.dataset != dataset: - package_distribution.managed_by_stac = True - package_distribution.dataset = dataset - package_distribution.save() - self.increment_counter('package_distribution', 'updated') - self.print(f"Updated package distribution '{collection_id}'") - self.check_provider(collection, dataset) + processed.add(collection_id) + + # Get unmanaged collections from the HTML root + response = get(self.url, timeout=60) + element = BeautifulSoup(response.text, 'html.parser').find('div', id="data") + if not element: + raise ValueError(f"Error parsing {self.url}") + + for line in split(r'\r?\n', element.text.strip()): + line = line.strip() + if not line: + continue + + values = line.split(' ') + if len(values) == 0: + continue + + collection_id = values[0] + if collection_id in processed: + continue + + dataset = self.update_package_distribution(collection_id, False) + if not dataset: + continue + + processed.add(collection_id) # Remove orphaned package distributions - orphans = PackageDistribution.objects.filter(managed_by_stac=True).exclude( - package_distribution_id__in=processed - ) + orphans = PackageDistribution.objects.filter( + _legacy_imported=True + ).exclude(package_distribution_id__in=processed,) _, removed = orphans.delete() for model_class, count in removed.items(): model_name = model_class.split('.')[-1].lower() @@ -166,8 +220,13 @@ def add_arguments(self, parser: CommandParser) -> None: default=1.0, help="Similarity threshold to use when comparing providers" ) + parser.add_argument("--url", type=str, default="https://data.geo.admin.ch", help="STAC URL") + parser.add_argument("--endpoint", type=str, default="/api/stac/v1", help="STAC endpoint") parser.add_argument( - "--url", type=str, default="https://data.geo.admin.ch/api/stac/v0.9", help="STAC URL" + "--default-dataset", + type=str, + default="", + help="Add packages with missing dataset to this dataset" ) def handle(self, *args: Any, **options: Any) -> None: diff --git a/app/distributions/migrations/0011_dataset_description_de_dataset_description_en_and_more.py b/app/distributions/migrations/0011_dataset_description_de_dataset_description_en_and_more.py new file mode 100644 index 00000000..1e805366 --- /dev/null +++ b/app/distributions/migrations/0011_dataset_description_de_dataset_description_en_and_more.py @@ -0,0 +1,70 @@ +# Generated by Django 5.0.13 on 2025-04-07 13:31 + +from django.db import migrations +from django.db import models + + +class Migration(migrations.Migration): + + dependencies = [ + ('distributions', '0010_rename_slug_attribution_attribution_id_and_more_updated'), + ] + + operations = [ + migrations.AddField( + model_name='dataset', + name='description_de', + field=models.CharField(default='changeme', verbose_name='Description (German)'), + preserve_default=False, + ), + migrations.AddField( + model_name='dataset', + name='description_en', + field=models.CharField(default='changeme', verbose_name='Description (English)'), + preserve_default=False, + ), + migrations.AddField( + model_name='dataset', + name='description_fr', + field=models.CharField(default='changeme', verbose_name='Description (French)'), + preserve_default=False, + ), + migrations.AddField( + model_name='dataset', + name='description_it', + field=models.CharField(blank=True, null=True, verbose_name='Description (Italian)'), + ), + migrations.AddField( + model_name='dataset', + name='description_rm', + field=models.CharField(blank=True, null=True, verbose_name='Description (Romansh)'), + ), + migrations.AddField( + model_name='dataset', + name='title_de', + field=models.CharField(default='changeme', verbose_name='Title (German)'), + preserve_default=False, + ), + migrations.AddField( + model_name='dataset', + name='title_en', + field=models.CharField(default='changeme', verbose_name='Title (English)'), + preserve_default=False, + ), + migrations.AddField( + model_name='dataset', + name='title_fr', + field=models.CharField(default='changeme', verbose_name='Title (French)'), + preserve_default=False, + ), + migrations.AddField( + model_name='dataset', + name='title_it', + field=models.CharField(blank=True, null=True, verbose_name='Title (Italian)'), + ), + migrations.AddField( + model_name='dataset', + name='title_rm', + field=models.CharField(blank=True, null=True, verbose_name='Title (Romansh)'), + ), + ] diff --git a/app/distributions/migrations/0012_dataset_geocat_id.py b/app/distributions/migrations/0012_dataset_geocat_id.py new file mode 100644 index 00000000..5ac9b81b --- /dev/null +++ b/app/distributions/migrations/0012_dataset_geocat_id.py @@ -0,0 +1,37 @@ +# Generated by Django 5.0.13 on 2025-04-08 06:39 + +from utils.short_id import generate_short_id + +from django.apps.registry import Apps +from django.db import migrations +from django.db import models +from django.db.backends.base.schema import BaseDatabaseSchemaEditor + + +def populate_geocat(apps: Apps, schema_editor: BaseDatabaseSchemaEditor) -> None: + Dataset = apps.get_model('distributions', 'Dataset') + for obj in Dataset.objects.all(): + obj.geocat_id = 'changeme ' + generate_short_id() + obj.save() + + +class Migration(migrations.Migration): + + dependencies = [ + ('distributions', '0011_dataset_description_de_dataset_description_en_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='dataset', + name='geocat_id', + field=models.CharField(default='changeme', max_length=100, verbose_name='Geocat ID'), + preserve_default=False, + ), + migrations.RunPython(populate_geocat, migrations.RunPython.noop), + migrations.AlterField( + model_name='dataset', + name='geocat_id', + field=models.CharField(max_length=100, unique=True, verbose_name='Geocat ID'), + ), + ] diff --git a/app/distributions/migrations/0013_packagedistribution__legacy_imported_and_more.py b/app/distributions/migrations/0013_packagedistribution__legacy_imported_and_more.py new file mode 100644 index 00000000..cbf26304 --- /dev/null +++ b/app/distributions/migrations/0013_packagedistribution__legacy_imported_and_more.py @@ -0,0 +1,28 @@ +# Generated by Django 5.0.14 on 2025-04-10 13:40 + +from django.db import migrations +from django.db import models + + +class Migration(migrations.Migration): + + dependencies = [ + ('distributions', '0012_dataset_geocat_id'), + ] + + operations = [ + migrations.AddField( + model_name='packagedistribution', + name='_legacy_imported', + field=models.BooleanField( + default=False, + help_text='This field is used to track objects imported from STAC', + verbose_name='Legacy Imported' + ), + ), + migrations.AlterField( + model_name='packagedistribution', + name='managed_by_stac', + field=models.BooleanField(verbose_name='Managed by STAC'), + ), + ] diff --git a/app/distributions/models.py b/app/distributions/models.py index 40faaf2b..13d5a806 100644 --- a/app/distributions/models.py +++ b/app/distributions/models.py @@ -69,6 +69,18 @@ def __str__(self) -> str: created = models.DateTimeField(_(_context, "Created"), auto_now_add=True) updated = models.DateTimeField(_(_context, "Updated"), auto_now=True) + title_de = models.CharField(_(_context, "Title (German)")) + title_fr = models.CharField(_(_context, "Title (French)")) + title_en = models.CharField(_(_context, "Title (English)")) + title_it = models.CharField(_(_context, "Title (Italian)"), null=True, blank=True) + title_rm = models.CharField(_(_context, "Title (Romansh)"), null=True, blank=True) + description_de = models.CharField(_(_context, "Description (German)")) + description_fr = models.CharField(_(_context, "Description (French)")) + description_en = models.CharField(_(_context, "Description (English)")) + description_it = models.CharField(_(_context, "Description (Italian)"), null=True, blank=True) + description_rm = models.CharField(_(_context, "Description (Romansh)"), null=True, blank=True) + + geocat_id = models.CharField(_(_context, "Geocat ID"), unique=True, max_length=100) provider = models.ForeignKey("provider.Provider", on_delete=models.CASCADE) attribution = models.ForeignKey(Attribution, on_delete=models.CASCADE) @@ -107,7 +119,14 @@ def __str__(self) -> str: package_distribution_id = CustomSlugField( _(_context, "External ID"), unique=True, max_length=100 ) - managed_by_stac = models.BooleanField(_(_context, "Managed by STAC"), max_length=100) + managed_by_stac = models.BooleanField(_(_context, "Managed by STAC")) + + _legacy_imported = models.BooleanField( + _(_context, "Legacy Imported"), + db_index=False, + default=False, + help_text="This field is used to track objects imported from STAC" + ) dataset = models.ForeignKey(Dataset, on_delete=models.CASCADE) diff --git a/app/distributions/schemas.py b/app/distributions/schemas.py index a25cf9e1..c3a98c51 100644 --- a/app/distributions/schemas.py +++ b/app/distributions/schemas.py @@ -19,6 +19,10 @@ class AttributionListSchema(Schema): class DatasetSchema(Schema): id: str + title: str + title_translations: TranslationsSchema + description: str + description_translations: TranslationsSchema created: datetime updated: datetime provider_id: str diff --git a/app/tests/bod/test_bod_sync_command.py b/app/tests/bod/test_bod_sync_command.py index 527ff6d8..48b6653e 100644 --- a/app/tests/bod/test_bod_sync_command.py +++ b/app/tests/bod/test_bod_sync_command.py @@ -2,6 +2,7 @@ from bod.models import BodContactOrganisation from bod.models import BodDataset +from bod.models import BodGeocatPublish from bod.models import BodTranslations from distributions.models import Attribution from distributions.models import Dataset @@ -41,12 +42,43 @@ def fixture_bod_dataset(bod_contact_organisation): yield BodDataset.objects.create( id=170, id_dataset="ch.bafu.auen-vegetationskarten", + fk_geocat="ab76361f-657d-4705-9053-95f89ecab126", fk_contactorganisation_id=bod_contact_organisation.pk_contactorganisation_id, staging="prod" ) -def test_command_imports(bod_dataset): +@fixture(name='bod_geocat_publish') +def fixture_bod_geocat_publish(bod_dataset): + yield BodGeocatPublish.objects.create( + bgdi_id=170, + fk_id_dataset="ch.bafu.auen-vegetationskarten", + bezeichnung_de="vegetationskarten_de", + bezeichnung_fr="vegetationskarten_fr", + bezeichnung_it="vegetationskarten_it", + bezeichnung_rm="vegetationskarten_rm", + bezeichnung_en="vegetationskarten_en", + abstract_de="abstract_vegetationskarten_de", + abstract_fr="abstract_vegetationskarten_fr", + abstract_it="abstract_vegetationskarten_it", + abstract_rm="abstract_vegetationskarten_rm", + abstract_en="abstract_vegetationskarten_en" + ) + + +@fixture(name='bod_geocat_publish_missing_english') +def fixture_bod_geocat_publish_missing_english(bod_dataset): + yield BodGeocatPublish.objects.create( + bgdi_id=170, + fk_id_dataset="ch.bafu.auen-vegetationskarten", + bezeichnung_de="vegetationskarten_de", + bezeichnung_fr="vegetationskarten_fr", + abstract_de="abstract_vegetationskarten_de", + abstract_fr="abstract_vegetationskarten_fr", + ) + + +def test_command_imports(bod_geocat_publish): out = StringIO() call_command( "bod_sync", providers=True, attributions=True, datasets=True, verbosity=2, stdout=out @@ -88,6 +120,17 @@ def test_command_imports(bod_dataset): dataset = provider.dataset_set.first() assert dataset.attribution == attribution assert dataset.dataset_id == "ch.bafu.auen-vegetationskarten" + assert dataset.geocat_id == "ab76361f-657d-4705-9053-95f89ecab126" + assert dataset.title_de == "vegetationskarten_de" + assert dataset.title_fr == "vegetationskarten_fr" + assert dataset.title_it == "vegetationskarten_it" + assert dataset.title_rm == "vegetationskarten_rm" + assert dataset.title_en == "vegetationskarten_en" + assert dataset.description_de == "abstract_vegetationskarten_de" + assert dataset.description_fr == "abstract_vegetationskarten_fr" + assert dataset.description_it == "abstract_vegetationskarten_it" + assert dataset.description_rm == "abstract_vegetationskarten_rm" + assert dataset.description_en == "abstract_vegetationskarten_en" def test_command_does_not_need_to_import(db): @@ -98,7 +141,7 @@ def test_command_does_not_need_to_import(db): assert 'nothing to be done, already in sync' in out.getvalue() -def test_command_no_flag_set(bod_dataset): +def test_command_no_flag_set(bod_geocat_publish): out = StringIO() call_command( "bod_sync", providers=False, attributions=False, datasets=False, verbosity=2, stdout=out @@ -106,7 +149,7 @@ def test_command_no_flag_set(bod_dataset): assert 'no option provided, nothing changed' in out.getvalue() -def test_command_imports_providers(bod_dataset): +def test_command_imports_providers(bod_geocat_publish): out = StringIO() call_command( "bod_sync", providers=True, attributions=False, datasets=False, verbosity=2, stdout=out @@ -132,7 +175,7 @@ def test_command_imports_providers(bod_dataset): assert provider.acronym_rm == "UFAM" -def test_command_skips_invalid_providers(bod_contact_organisation, bod_dataset): +def test_command_skips_invalid_providers(bod_contact_organisation, bod_geocat_publish): for attribution in ("somethingelse", "", None): bod_contact_organisation.attribution = attribution bod_contact_organisation.save() @@ -147,7 +190,7 @@ def test_command_skips_invalid_providers(bod_contact_organisation, bod_dataset): assert Dataset.objects.count() == 0 -def test_command_imports_attributions(bod_contact_organisation, bod_dataset): +def test_command_imports_attributions(bod_contact_organisation, bod_geocat_publish): provider = Provider.objects.create( provider_id="ch.bafu", name_de="XXX", @@ -183,7 +226,7 @@ def test_command_imports_attributions(bod_contact_organisation, bod_dataset): assert attribution.description_rm == "UFAM" -def test_command_skips_invalid_attributions(bod_contact_organisation, bod_dataset): +def test_command_skips_invalid_attributions(bod_contact_organisation, bod_geocat_publish): for attribution in (bod_contact_organisation.attribution, "", None): bod_contact_organisation.attribution = attribution bod_contact_organisation.save() @@ -200,7 +243,7 @@ def test_command_skips_invalid_attributions(bod_contact_organisation, bod_datase assert Dataset.objects.count() == 0 -def test_command_imports_datasets(bod_contact_organisation, bod_dataset): +def test_command_imports_datasets(bod_contact_organisation, bod_geocat_publish): provider = Provider.objects.create( provider_id="ch.bafu", name_de="XXX", @@ -236,9 +279,120 @@ def test_command_imports_datasets(bod_contact_organisation, bod_dataset): dataset = provider.dataset_set.first() assert dataset.attribution == attribution assert dataset.dataset_id == "ch.bafu.auen-vegetationskarten" + assert dataset.geocat_id == "ab76361f-657d-4705-9053-95f89ecab126" + assert dataset.title_de == "vegetationskarten_de" + assert dataset.title_fr == "vegetationskarten_fr" + assert dataset.title_it == "vegetationskarten_it" + assert dataset.title_rm == "vegetationskarten_rm" + assert dataset.title_en == "vegetationskarten_en" + assert dataset.description_de == "abstract_vegetationskarten_de" + assert dataset.description_fr == "abstract_vegetationskarten_fr" + assert dataset.description_it == "abstract_vegetationskarten_it" + assert dataset.description_rm == "abstract_vegetationskarten_rm" + assert dataset.description_en == "abstract_vegetationskarten_en" + + +def test_command_imports_datasets_missing_english( + bod_contact_organisation, bod_geocat_publish_missing_english +): + provider = Provider.objects.create( + provider_id="ch.bafu", + name_de="XXX", + name_fr="XXX", + name_en="XXX", + acronym_de="BAFU", + acronym_fr="XXX", + acronym_en="XXX", + _legacy_id=bod_contact_organisation.pk_contactorganisation_id + ) + attribution = Attribution.objects.create( + attribution_id="ch.bafu", + name_de="XXX", + name_fr="XXX", + name_en="XXX", + description_de="BAFU", + description_fr="XXX", + description_en="XXX", + provider=provider, + _legacy_id=bod_contact_organisation.pk_contactorganisation_id + ) + + out = StringIO() + call_command( + "bod_sync", providers=False, attributions=False, datasets=True, verbosity=2, stdout=out + ) + assert "Added dataset 'ch.bafu.auen-vegetationskarten'" in out.getvalue() + assert "1 dataset(s) added" in out.getvalue() + assert Provider.objects.count() == 1 + assert Attribution.objects.count() == 1 + assert Dataset.objects.count() == 1 + + dataset = provider.dataset_set.first() + assert dataset.attribution == attribution + assert dataset.dataset_id == "ch.bafu.auen-vegetationskarten" + assert dataset.geocat_id == "ab76361f-657d-4705-9053-95f89ecab126" + assert dataset.title_de == "vegetationskarten_de" + assert dataset.title_fr == "vegetationskarten_fr" + assert dataset.title_it is None + assert dataset.title_rm is None + assert dataset.title_en == "vegetationskarten_de" + assert dataset.description_de == "abstract_vegetationskarten_de" + assert dataset.description_fr == "abstract_vegetationskarten_fr" + assert dataset.description_it is None + assert dataset.description_rm is None + assert dataset.description_en == "abstract_vegetationskarten_de" + + +def test_command_imports_datasets_missing_geocat(bod_contact_organisation, bod_dataset): + provider = Provider.objects.create( + provider_id="ch.bafu", + name_de="XXX", + name_fr="XXX", + name_en="XXX", + acronym_de="BAFU", + acronym_fr="XXX", + acronym_en="XXX", + _legacy_id=bod_contact_organisation.pk_contactorganisation_id + ) + attribution = Attribution.objects.create( + attribution_id="ch.bafu", + name_de="XXX", + name_fr="XXX", + name_en="XXX", + description_de="BAFU", + description_fr="XXX", + description_en="XXX", + provider=provider, + _legacy_id=bod_contact_organisation.pk_contactorganisation_id + ) + out = StringIO() + call_command( + "bod_sync", providers=False, attributions=False, datasets=True, verbosity=2, stdout=out + ) + assert "Added dataset 'ch.bafu.auen-vegetationskarten'" in out.getvalue() + assert "1 dataset(s) added" in out.getvalue() + assert Provider.objects.count() == 1 + assert Attribution.objects.count() == 1 + assert Dataset.objects.count() == 1 -def test_command_skips_invalid_datasets(bod_dataset): + dataset = provider.dataset_set.first() + assert dataset.attribution == attribution + assert dataset.dataset_id == "ch.bafu.auen-vegetationskarten" + assert dataset.geocat_id == "ab76361f-657d-4705-9053-95f89ecab126" + assert dataset.title_de == "#Missing" + assert dataset.title_fr == "#Missing" + assert dataset.title_it is None + assert dataset.title_rm is None + assert dataset.title_en == "#Missing" + assert dataset.description_de == "#Missing" + assert dataset.description_fr == "#Missing" + assert dataset.description_it is None + assert dataset.description_rm is None + assert dataset.description_en == "#Missing" + + +def test_command_skips_invalid_datasets(bod_geocat_publish): out = StringIO() call_command( "bod_sync", providers=False, attributions=False, datasets=True, verbosity=2, stdout=out @@ -253,7 +407,7 @@ def test_command_skips_invalid_datasets(bod_dataset): assert Dataset.objects.count() == 0 -def test_command_skips_non_prod_datasets(bod_dataset): +def test_command_skips_non_prod_datasets(bod_dataset, bod_geocat_publish): bod_dataset.staging = 'test' out = StringIO() @@ -266,7 +420,8 @@ def test_command_skips_non_prod_datasets(bod_dataset): assert Dataset.objects.count() == 0 -def test_command_updates(bod_contact_organisation, bod_dataset): +#pylint: disable=too-many-statements +def test_command_updates(bod_contact_organisation, bod_dataset, bod_geocat_publish): # Add objects that will be updated provider = Provider.objects.create( provider_id="ch.bafu", @@ -290,7 +445,17 @@ def test_command_updates(bod_contact_organisation, bod_dataset): _legacy_id=bod_contact_organisation.pk_contactorganisation_id ) dataset = Dataset.objects.create( - dataset_id="xxx", provider=provider, attribution=attribution, _legacy_id=bod_dataset.id + dataset_id="xxx", + geocat_id="xxx", + title_de="xxx", + title_fr="xxx", + title_en="xxx", + description_de="xxx", + description_fr="xxx", + description_en="xxx", + provider=provider, + attribution=attribution, + _legacy_id=bod_dataset.id ) out = StringIO() @@ -337,9 +502,20 @@ def test_command_updates(bod_contact_organisation, bod_dataset): dataset = provider.dataset_set.first() assert dataset.dataset_id == "ch.bafu.auen-vegetationskarten" - - -def test_command_removes_orphaned_provider(bod_dataset): + assert dataset.geocat_id == "ab76361f-657d-4705-9053-95f89ecab126" + assert dataset.title_de == "vegetationskarten_de" + assert dataset.title_fr == "vegetationskarten_fr" + assert dataset.title_it == "vegetationskarten_it" + assert dataset.title_rm == "vegetationskarten_rm" + assert dataset.title_en == "vegetationskarten_en" + assert dataset.description_de == "abstract_vegetationskarten_de" + assert dataset.description_fr == "abstract_vegetationskarten_fr" + assert dataset.description_it == "abstract_vegetationskarten_it" + assert dataset.description_rm == "abstract_vegetationskarten_rm" + assert dataset.description_en == "abstract_vegetationskarten_en" + + +def test_command_removes_orphaned_provider(bod_geocat_publish): # Add objects which will be removed provider = Provider.objects.create( provider_id="ch.xxx", @@ -363,7 +539,17 @@ def test_command_removes_orphaned_provider(bod_dataset): _legacy_id=16 ) Dataset.objects.create( - dataset_id="xxx", provider=provider, attribution=attribution, _legacy_id=160 + dataset_id="xxx", + geocat_id="xxx", + title_de="xxx", + title_fr="xxx", + title_en="xxx", + description_de="xxx", + description_fr="xxx", + description_en="xxx", + provider=provider, + attribution=attribution, + _legacy_id=160 ) # Add objects which will not be removed @@ -386,7 +572,18 @@ def test_command_removes_orphaned_provider(bod_dataset): description_en="YYY", provider=provider ) - Dataset.objects.create(dataset_id="yyyy", provider=provider, attribution=attribution) + Dataset.objects.create( + dataset_id="yyyy", + geocat_id="yyyy", + title_de="yyyy", + title_fr="yyyy", + title_en="yyyy", + description_de="yyyy", + description_fr="yyyy", + description_en="yyyy", + provider=provider, + attribution=attribution + ) out = StringIO() call_command( @@ -433,7 +630,17 @@ def test_command_removes_orphaned_attribution(bod_contact_organisation): _legacy_id=16 ) Dataset.objects.create( - dataset_id="xxx", provider=provider, attribution=attribution, _legacy_id=160 + dataset_id="xxx", + geocat_id="xxx", + title_de="xxx", + title_fr="xxx", + title_en="xxx", + description_de="xxx", + description_fr="xxx", + description_en="xxx", + provider=provider, + attribution=attribution, + _legacy_id=160 ) out = StringIO() @@ -479,7 +686,17 @@ def test_command_removes_orphaned_dataset(bod_contact_organisation): # Add objects which will be removed Dataset.objects.create( - dataset_id="xxx", provider=provider, attribution=attribution, _legacy_id=160 + dataset_id="xxx", + geocat_id="xxx", + title_de="xxx", + title_fr="xxx", + title_en="xxx", + description_de="xxx", + description_fr="xxx", + description_en="xxx", + provider=provider, + attribution=attribution, + _legacy_id=160 ) out = StringIO() @@ -499,7 +716,7 @@ def test_command_removes_orphaned_dataset(bod_contact_organisation): assert {'BAFU'} == set(Attribution.objects.values_list('name_de', flat=True)) -def test_command_does_not_import_if_dry_run(bod_dataset): +def test_command_does_not_import_if_dry_run(bod_geocat_publish): out = StringIO() call_command( "bod_sync", providers=True, attributions=True, datasets=True, dry_run=True, stdout=out @@ -513,7 +730,7 @@ def test_command_does_not_import_if_dry_run(bod_dataset): assert Dataset.objects.count() == 0 -def test_command_clears_existing_data(bod_dataset): +def test_command_clears_existing_data(bod_geocat_publish): provider = Provider.objects.create( provider_id="ch.xxx", name_de="XXX", @@ -534,7 +751,18 @@ def test_command_clears_existing_data(bod_dataset): description_en="XXX", provider=provider ) - Dataset.objects.create(dataset_id="yyyy", provider=provider, attribution=attribution) + Dataset.objects.create( + dataset_id="yyyy", + geocat_id="yyyy", + title_de="yyyy", + title_fr="yyyy", + title_en="yyyy", + description_de="yyyy", + description_fr="yyyy", + description_en="yyyy", + provider=provider, + attribution=attribution + ) out = StringIO() call_command( diff --git a/app/tests/distributions/test_dataset_model.py b/app/tests/distributions/test_dataset_model.py index 31f91daa..1b8ac9b0 100644 --- a/app/tests/distributions/test_dataset_model.py +++ b/app/tests/distributions/test_dataset_model.py @@ -7,12 +7,35 @@ from django.core.exceptions import ValidationError +# pylint: disable=too-many-locals def test_object_created_in_db_with_all_fields_defined(provider, attribution): dataset_id = "ch.bafu.neophyten-haargurke" + geocat_id = "ab76361f-657d-4705-9053-95f89ecab126" time_created = datetime.datetime(2024, 9, 12, 15, 28, 0, tzinfo=datetime.UTC) + title_de = "Invasive gebietsfremde Pflanzen - Potentialkarte Haargurke" + title_fr = "Plantes exotiques envahissantes - Carte de distribution potentiel Sicyos anguleux" + title_en = "Invasive alien plants - map of the potential area Sicyos angulatus" + title_it = "Piante esotiche invasive - carte di distribuzione potenziale Sicios angoloso" + title_rm = "Plantas exoticas invasivas - Charta da la derasaziun potenziala dal sichius angulus" + description_de = "Beschreibung Haargurke" + description_fr = "Description Sicyos anguleux" + description_en = "Description Sicyos angulatus" + description_it = "Descrizione Sicios angoloso" + description_rm = "Descripziun Sicyos angulatus" with mock.patch('django.utils.timezone.now', mock.Mock(return_value=time_created)): Dataset.objects.create( dataset_id=dataset_id, + geocat_id=geocat_id, + title_de=title_de, + title_fr=title_fr, + title_en=title_en, + title_it=title_it, + title_rm=title_rm, + description_de=description_de, + description_fr=description_fr, + description_en=description_en, + description_it=description_it, + description_rm=description_rm, provider=provider, attribution=attribution, ) @@ -23,18 +46,37 @@ def test_object_created_in_db_with_all_fields_defined(provider, attribution): dataset = Dataset.objects.last() assert dataset.dataset_id == dataset_id + assert dataset.geocat_id == geocat_id assert dataset.provider.acronym_de == "BAFU" assert dataset.attribution.name_de == "BAFU + Kantone" assert dataset.created == time_created assert dataset.updated == time_created + assert dataset.title_de == title_de + assert dataset.title_fr == title_fr + assert dataset.title_en == title_en + assert dataset.title_it == title_it + assert dataset.title_rm == title_rm + assert dataset.description_de == description_de + assert dataset.description_fr == description_fr + assert dataset.description_en == description_en + assert dataset.description_it == description_it + assert dataset.description_rm == description_rm + def test_field_created_matches_creation_time(provider, attribution): time_created = datetime.datetime(2024, 9, 12, 15, 28, 0, tzinfo=datetime.UTC) with mock.patch('django.utils.timezone.now', mock.Mock(return_value=time_created)): dataset = Dataset.objects.create( dataset_id='xxxx', + geocat_id='xxxx', + title_de="xxxx", + title_fr="xxxx", + title_en="xxxx", + description_de="xxxx", + description_fr="xxxx", + description_en="xxxx", provider=provider, attribution=attribution, ) @@ -42,7 +84,18 @@ def test_field_created_matches_creation_time(provider, attribution): def test_field_updated_matches_update_time(provider, attribution): - dataset = Dataset.objects.create(dataset_id='xxxx', provider=provider, attribution=attribution) + dataset = Dataset.objects.create( + dataset_id='xxxx', + geocat_id='xxxx', + title_de="xxxx", + title_fr="xxxx", + title_en="xxxx", + description_de="xxxx", + description_fr="xxxx", + description_en="xxxx", + provider=provider, + attribution=attribution + ) time_updated = datetime.datetime(2024, 9, 12, 15, 42, 0, tzinfo=datetime.UTC) with mock.patch('django.utils.timezone.now', mock.Mock(return_value=time_updated)): diff --git a/app/tests/distributions/test_distributions_api.py b/app/tests/distributions/test_distributions_api.py index 3d515d37..a278fb87 100644 --- a/app/tests/distributions/test_distributions_api.py +++ b/app/tests/distributions/test_distributions_api.py @@ -20,6 +20,19 @@ def fixture_dataset(attribution, time_created): with mock.patch('django.utils.timezone.now', mock.Mock(return_value=time_created)): yield Dataset.objects.create( dataset_id="ch.bafu.neophyten-haargurke", + geocat_id="ab76361f-657d-4705-9053-95f89ecab126", + title_de="Invasive gebietsfremde Pflanzen - Potentialkarte Haargurke", + title_fr= + "Plantes exotiques envahissantes - Carte de distribution potentiel Sicyos anguleux", + title_en="Invasive alien plants - map of the potential area Sicyos angulatus", + title_it="Piante esotiche invasive - carte di distribuzione potenziale Sicios angoloso", + title_rm= + "Plantas exoticas invasivas - Charta da la derasaziun potenziala dal sichius angulus", + description_de="Beschreibung Haargurke", + description_fr="Description Sicyos anguleux", + description_en="Description Sicyos angulatus", + description_it="Descrizione Sicios angoloso", + description_rm="Descripziun Sicyos angulatus", provider=attribution.provider, attribution=attribution ) @@ -584,6 +597,25 @@ def test_get_dataset_returns_specified_dataset(dataset, client, django_user_fact assert response.status_code == 200 assert response.json() == { "id": "ch.bafu.neophyten-haargurke", + "title": "Invasive alien plants - map of the potential area Sicyos angulatus", + "title_translations": { + "de": "Invasive gebietsfremde Pflanzen - Potentialkarte Haargurke", + "fr": + "Plantes exotiques envahissantes - Carte de distribution potentiel Sicyos anguleux", + "en": "Invasive alien plants - map of the potential area Sicyos angulatus", + "it": "Piante esotiche invasive - carte di distribuzione potenziale Sicios angoloso", + "rm": + "Plantas exoticas invasivas - Charta da " + + "la derasaziun potenziala dal sichius angulus", + }, + "description": "Description Sicyos angulatus", + "description_translations": { + "de": "Beschreibung Haargurke", + "fr": "Description Sicyos anguleux", + "en": "Description Sicyos angulatus", + "it": "Descrizione Sicios angoloso", + "rm": "Descripziun Sicyos angulatus", + }, "created": time_created.strftime("%Y-%m-%dT%H:%M:%SZ"), "updated": time_created.strftime("%Y-%m-%dT%H:%M:%SZ"), "provider_id": "ch.bafu", @@ -620,6 +652,28 @@ def test_get_datasets_returns_single_dataset_as_expected( assert response.json() == { "items": [{ "id": "ch.bafu.neophyten-haargurke", + "title": "Invasive alien plants - map of the potential area Sicyos angulatus", + "title_translations": { + "de": "Invasive gebietsfremde Pflanzen - Potentialkarte Haargurke", + "fr": + "Plantes exotiques envahissantes - " + + "Carte de distribution potentiel Sicyos anguleux", + "en": "Invasive alien plants - map of the potential area Sicyos angulatus", + "it": + "Piante esotiche invasive - carte " + + "di distribuzione potenziale Sicios angoloso", + "rm": + "Plantas exoticas invasivas - Charta " + + "da la derasaziun potenziala dal sichius angulus", + }, + "description": "Description Sicyos angulatus", + "description_translations": { + "de": "Beschreibung Haargurke", + "fr": "Description Sicyos anguleux", + "en": "Description Sicyos angulatus", + "it": "Descrizione Sicios angoloso", + "rm": "Descripziun Sicyos angulatus", + }, "created": time_created.strftime("%Y-%m-%dT%H:%M:%SZ"), "updated": time_created.strftime("%Y-%m-%dT%H:%M:%SZ"), "provider_id": "ch.bafu", @@ -655,6 +709,13 @@ def test_get_datasets_returns_all_datasets_ordered_by_dataset_id( ) model_fields2 = { "dataset_id": "ch.provider2.dataset2", + "geocat_id": "dataset2", + "title_de": "dataset2", + "title_fr": "dataset2", + "title_en": "dataset2", + "description_de": "dataset2", + "description_fr": "dataset2", + "description_en": "dataset2", "provider": provider2, "attribution": attribution2, } @@ -669,6 +730,28 @@ def test_get_datasets_returns_all_datasets_ordered_by_dataset_id( "items": [ { "id": "ch.bafu.neophyten-haargurke", + "title": "Invasive alien plants - map of the potential area Sicyos angulatus", + "title_translations": { + "de": "Invasive gebietsfremde Pflanzen - Potentialkarte Haargurke", + "fr": + "Plantes exotiques envahissantes - " + + "Carte de distribution potentiel Sicyos anguleux", + "en": "Invasive alien plants - map of the potential area Sicyos angulatus", + "it": + "Piante esotiche invasive - carte di" + + " distribuzione potenziale Sicios angoloso", + "rm": + "Plantas exoticas invasivas - Charta " + + "da la derasaziun potenziala dal sichius angulus", + }, + "description": "Description Sicyos angulatus", + "description_translations": { + "de": "Beschreibung Haargurke", + "fr": "Description Sicyos anguleux", + "en": "Description Sicyos angulatus", + "it": "Descrizione Sicios angoloso", + "rm": "Descripziun Sicyos angulatus", + }, "created": time_created.strftime("%Y-%m-%dT%H:%M:%SZ"), "updated": time_created.strftime("%Y-%m-%dT%H:%M:%SZ"), "provider_id": "ch.bafu", @@ -676,6 +759,18 @@ def test_get_datasets_returns_all_datasets_ordered_by_dataset_id( }, { "id": "ch.provider2.dataset2", + "title": "dataset2", + "title_translations": { + "de": "dataset2", + "fr": "dataset2", + "en": "dataset2", + }, + "description": "dataset2", + "description_translations": { + "de": "dataset2", + "fr": "dataset2", + "en": "dataset2", + }, "created": dataset2.created.strftime("%Y-%m-%dT%H:%M:%SZ"), "updated": dataset2.updated.strftime("%Y-%m-%dT%H:%M:%SZ"), "provider_id": "ch.provider2", diff --git a/app/tests/distributions/test_package_distribution_model.py b/app/tests/distributions/test_package_distribution_model.py index 05b8b7d9..00078e2f 100644 --- a/app/tests/distributions/test_package_distribution_model.py +++ b/app/tests/distributions/test_package_distribution_model.py @@ -10,6 +10,13 @@ def fixture_dataset(provider, attribution): yield Dataset.objects.create( dataset_id="ch.agroscope.feuchtflaechenpotential-kulturlandschaft", + geocat_id="ab76361f-657d-4705-9053-95f89ecab126", + title_de="Feuchtflächenpotential Agrarland", + title_fr="Potentiel des surfaces humides", + title_en="Wetness potential agricultural land", + description_de="Feuchtflächenpotential Agrarland", + description_fr="Potentiel des surfaces humides", + description_en="Wetness potential agricultural land", provider=provider, attribution=attribution, ) diff --git a/app/tests/distributions/test_stac_harvest_command.py b/app/tests/distributions/test_stac_harvest_command.py deleted file mode 100644 index bd4859c1..00000000 --- a/app/tests/distributions/test_stac_harvest_command.py +++ /dev/null @@ -1,331 +0,0 @@ -from io import StringIO -from unittest.mock import patch - -from distributions.models import Dataset -from distributions.models import PackageDistribution -from pystac.collection import Collection -from pystac.provider import Provider as StacProvider - -from django.core.management import call_command - - -@patch('distributions.management.commands.stac_harvest.Client') -def test_command_imports(stac_client, provider, attribution): - dataset = Dataset.objects.create( - dataset_id="ch.bafu.alpweiden-herdenschutzhunde", - provider=provider, - attribution=attribution, - ) - - stac_client.open.return_value.collection_search.return_value.collections.return_value = [ - Collection( - id='ch.bafu.alpweiden-herdenschutzhunde', - description=None, - extent=None, - providers=[StacProvider(name='Federal Office for the Environment')] - ) - ] - - out = StringIO() - call_command("stac_harvest", verbosity=2, stdout=out) - out = out.getvalue() - assert "Added package distribution 'ch.bafu.alpweiden-herdenschutzhunde'" in out - assert "1 package_distribution(s) added" in out - - package_distribution = PackageDistribution.objects.first() - assert package_distribution.package_distribution_id == 'ch.bafu.alpweiden-herdenschutzhunde' - assert package_distribution.managed_by_stac is True - assert package_distribution.dataset == dataset - - -@patch('distributions.management.commands.stac_harvest.Client') -def test_command_fails_to_import_if_dataset_is_missing(stac_client, provider, attribution): - stac_client.open.return_value.collection_search.return_value.collections.return_value = [ - Collection( - id='ch.bafu.alpweiden-herdenschutzhunde', - description=None, - extent=None, - providers=[StacProvider(name='Federal Office for the Environment')] - ) - ] - - out = StringIO() - call_command("stac_harvest", verbosity=2, stdout=out) - out = out.getvalue() - assert "No dataset for collection id 'ch.bafu.alpweiden-herdenschutzhunde'" in out - assert PackageDistribution.objects.count() == 0 - - -@patch('distributions.management.commands.stac_harvest.Client') -def test_command_does_not_need_to_import(stac_client, provider, attribution): - dataset = Dataset.objects.create( - dataset_id="ch.bafu.alpweiden-herdenschutzhunde", - provider=provider, - attribution=attribution, - ) - PackageDistribution.objects.create( - package_distribution_id='ch.bafu.alpweiden-herdenschutzhunde', - managed_by_stac=True, - dataset=dataset - ) - - stac_client.open.return_value.collection_search.return_value.collections.return_value = [ - Collection( - id='ch.bafu.alpweiden-herdenschutzhunde', - description=None, - extent=None, - providers=[StacProvider(name='Federal Office for the Environment')] - ) - ] - - out = StringIO() - call_command("stac_harvest", verbosity=2, stdout=out) - out = out.getvalue() - assert "nothing to be done, already up to date" in out - - -@patch('distributions.management.commands.stac_harvest.Client') -def test_command_updates(stac_client, provider, attribution): - dataset_old = Dataset.objects.create( - dataset_id="ch.bafu.amphibienwanderung-verkehrskonflikte", - provider=provider, - attribution=attribution, - ) - dataset_new = Dataset.objects.create( - dataset_id="ch.bafu.alpweiden-herdenschutzhunde", - provider=provider, - attribution=attribution, - ) - PackageDistribution.objects.create( - package_distribution_id='ch.bafu.alpweiden-herdenschutzhunde', - managed_by_stac=False, - dataset=dataset_old - ) - - stac_client.open.return_value.collection_search.return_value.collections.return_value = [ - Collection( - id='ch.bafu.alpweiden-herdenschutzhunde', - description=None, - extent=None, - providers=[StacProvider(name='Federal Office for the Environment')] - ) - ] - - out = StringIO() - call_command("stac_harvest", verbosity=2, stdout=out) - out = out.getvalue() - assert "Updated package distribution 'ch.bafu.alpweiden-herdenschutzhunde'" in out - assert "package_distribution(s) updated" in out - - package_distribution = PackageDistribution.objects.first() - assert package_distribution.package_distribution_id == 'ch.bafu.alpweiden-herdenschutzhunde' - assert package_distribution.managed_by_stac is True - assert package_distribution.dataset == dataset_new - - -@patch('distributions.management.commands.stac_harvest.Client') -def test_command_removes_orphans(stac_client, provider, attribution): - dataset = Dataset.objects.create( - dataset_id="ch.bafu.alpweiden-herdenschutzhunde", - provider=provider, - attribution=attribution, - ) - PackageDistribution.objects.create( - package_distribution_id='ch.bafu.alpweiden-herdenschutzhunde.1', - managed_by_stac=False, - dataset=dataset - ) - PackageDistribution.objects.create( - package_distribution_id='ch.bafu.alpweiden-herdenschutzhunde.2', - managed_by_stac=True, - dataset=dataset - ) - - stac_client.open.return_value.collection_search.return_value.collections.return_value = [] - - out = StringIO() - call_command("stac_harvest", verbosity=2, stdout=out) - out = out.getvalue() - assert "1 packagedistribution(s) removed" in out - - assert PackageDistribution.objects.count() == 1 - package_distribution = PackageDistribution.objects.first() - assert package_distribution.package_distribution_id == 'ch.bafu.alpweiden-herdenschutzhunde.1' - - -@patch('distributions.management.commands.stac_harvest.Client') -def test_command_clears(stac_client, provider, attribution): - dataset = Dataset.objects.create( - dataset_id="ch.bafu.alpweiden-herdenschutzhunde", - provider=provider, - attribution=attribution, - ) - PackageDistribution.objects.create( - package_distribution_id='ch.bafu.alpweiden-herdenschutzhunde.1', - managed_by_stac=False, - dataset=dataset - ) - PackageDistribution.objects.create( - package_distribution_id='ch.bafu.alpweiden-herdenschutzhunde.2', - managed_by_stac=True, - dataset=dataset - ) - - stac_client.open.return_value.collection_search.return_value.collections.return_value = [] - - out = StringIO() - call_command("stac_harvest", clear=True, verbosity=2, stdout=out) - out = out.getvalue() - - assert "1 packagedistribution(s) cleared" in out - - assert PackageDistribution.objects.count() == 1 - assert PackageDistribution.objects.first( - ).package_distribution_id == 'ch.bafu.alpweiden-herdenschutzhunde.1' - - -@patch('distributions.management.commands.stac_harvest.Client') -def test_command_runs_dry(stac_client, provider, attribution): - dataset_old = Dataset.objects.create( - dataset_id="ch.bafu.amphibienwanderung-verkehrskonflikte", - provider=provider, - attribution=attribution, - ) - dataset_new = Dataset.objects.create( - dataset_id="ch.bafu.alpweiden-herdenschutzhunde", - provider=provider, - attribution=attribution, - ) - PackageDistribution.objects.create( - package_distribution_id='ch.bafu.alpweiden-herdenschutzhunde', - managed_by_stac=False, - dataset=dataset_old - ) - - stac_client.open.return_value.collection_search.return_value.collections.return_value = [ - Collection( - id='ch.bafu.alpweiden-herdenschutzhunde', - description=None, - extent=None, - providers=[StacProvider(name='Federal Office for the Environment')] - ), - Collection( - id='ch.bafu.alpweiden-herdenschutzhunde.1', - description=None, - extent=None, - providers=[StacProvider(name='Federal Office for the Environment')] - ) - ] - - out = StringIO() - call_command("stac_harvest", dry_run=True, verbosity=2, stdout=out) - out = out.getvalue() - assert "Updated package distribution 'ch.bafu.alpweiden-herdenschutzhunde'" in out - assert "package_distribution(s) updated" in out - assert "dry run, aborting transaction" in out - - assert PackageDistribution.objects.count() == 1 - package_distribution = PackageDistribution.objects.first() - assert package_distribution.package_distribution_id == 'ch.bafu.alpweiden-herdenschutzhunde' - assert package_distribution.managed_by_stac is False - assert package_distribution.dataset == dataset_old - - -@patch('distributions.management.commands.stac_harvest.Client') -def test_command_warns_about_missing_provider(stac_client, provider, attribution): - Dataset.objects.create( - dataset_id="ch.bafu.alpweiden-herdenschutzhunde", - provider=provider, - attribution=attribution, - ) - stac_client.open.return_value.collection_search.return_value.collections.return_value = [ - Collection( - id='ch.bafu.alpweiden-herdenschutzhunde', description=None, extent=None, providers=[] - ) - ] - - out = StringIO() - call_command("stac_harvest", verbosity=2, stdout=out) - out = out.getvalue() - assert "Added package distribution 'ch.bafu.alpweiden-herdenschutzhunde'" in out - assert "1 package_distribution(s) added" in out - assert "Collection 'ch.bafu.alpweiden-herdenschutzhunde' has no providers" in out - assert PackageDistribution.objects.first() - - -@patch('distributions.management.commands.stac_harvest.Client') -def test_command_warns_about_multiple_providers(stac_client, provider, attribution): - Dataset.objects.create( - dataset_id="ch.bafu.alpweiden-herdenschutzhunde", - provider=provider, - attribution=attribution, - ) - stac_client.open.return_value.collection_search.return_value.collections.return_value = [ - Collection( - id='ch.bafu.alpweiden-herdenschutzhunde', - description=None, - extent=None, - providers=[ - StacProvider(name='Federal Office for the Environment'), - StacProvider(name='Federal Office for the Environment') - ] - ), - ] - - out = StringIO() - call_command("stac_harvest", verbosity=2, stdout=out) - out = out.getvalue() - assert "Added package distribution 'ch.bafu.alpweiden-herdenschutzhunde'" in out - assert "1 package_distribution(s) added" in out - assert "Collection 'ch.bafu.alpweiden-herdenschutzhunde' has more than one provider" in out - assert PackageDistribution.objects.first() - - -@patch('distributions.management.commands.stac_harvest.Client') -def test_command_warns_about_unknown_provider(stac_client, provider, attribution): - Dataset.objects.create( - dataset_id="ch.bafu.alpweiden-herdenschutzhunde", - provider=provider, - attribution=attribution, - ) - stac_client.open.return_value.collection_search.return_value.collections.return_value = [ - Collection( - id='ch.bafu.alpweiden-herdenschutzhunde', - description=None, - extent=None, - providers=[StacProvider(name='Federal Office of Culture FOC')] - ) - ] - - out = StringIO() - call_command("stac_harvest", verbosity=2, stdout=out) - out = out.getvalue() - assert "Added package distribution 'ch.bafu.alpweiden-herdenschutzhunde'" in out - assert "1 package_distribution(s) added" in out - assert "Provider in collection and dataset differ" in out - assert PackageDistribution.objects.first() - - -@patch('distributions.management.commands.stac_harvest.Client') -def test_command_does_not_warn_about_similar_provider(stac_client, provider, attribution): - Dataset.objects.create( - dataset_id="ch.bafu.alpweiden-herdenschutzhunde", - provider=provider, - attribution=attribution, - ) - stac_client.open.return_value.collection_search.return_value.collections.return_value = [ - Collection( - id='ch.bafu.alpweiden-herdenschutzhunde', - description=None, - extent=None, - providers=[StacProvider(name='Federal office for the environment')] - ) - ] - - out = StringIO() - call_command("stac_harvest", similarity=0.5, verbosity=2, stdout=out) - out = out.getvalue() - assert "Added package distribution 'ch.bafu.alpweiden-herdenschutzhunde'" in out - assert "1 package_distribution(s) added" in out - assert "Provider in collection and dataset differ" not in out - assert PackageDistribution.objects.first() diff --git a/app/tests/distributions/test_stac_sync_command.py b/app/tests/distributions/test_stac_sync_command.py new file mode 100644 index 00000000..f55bf902 --- /dev/null +++ b/app/tests/distributions/test_stac_sync_command.py @@ -0,0 +1,888 @@ +from io import StringIO +from unittest.mock import patch + +from distributions.models import Dataset +from distributions.models import PackageDistribution +from pystac.collection import Collection +from pystac.provider import Provider as StacProvider + +from django.core.management import call_command + + +@patch('distributions.management.commands.stac_sync.Client') +@patch('distributions.management.commands.stac_sync.get') +def test_command_imports(get, stac_client, provider, attribution): + dataset_1 = Dataset.objects.create( + dataset_id="ch.bafu.alpweiden-herdenschutzhunde", + geocat_id="ab76361f-657d-4705-9053-95f89ecab126", + title_de="Alpweiden mit Herdenschutzhunden", + title_fr="Alpages protégés par des chiens", + title_en="Alps with livestock guardian dogs", + title_it="Alpeggi con cani da guardiania", + title_rm="Pastgiras d'alp cun chauns prot.", + description_de="Beschreibung", + description_fr="Description", + description_en="Description", + description_it="Descrizione", + description_rm="Descripziun", + provider=provider, + attribution=attribution, + ) + dataset_2 = Dataset.objects.create( + dataset_id="ch.bafu.hydrologie-hintergrundkarte", + geocat_id="bbc6aedb-019f-4a49-8a6c-437264b3bd37", + title_de="Generalisierte Hintergrundkarte zur Darstellung hydrologischer Daten", + title_fr="Fond cartographique pour la présentation des données hydrologiques ", + title_en="Generalised background map for the representation of hydrological data ", + title_it="Carta base generalizzata per la rappresentazione di dati idrologici ", + title_rm="", + description_de="Beschreibung", + description_fr="Description", + description_en="Description", + description_it="Descrizione", + description_rm="", + provider=provider, + attribution=attribution, + ) + + stac_client.open.return_value.collection_search.return_value.collections.return_value = [ + Collection( + id='ch.bafu.alpweiden-herdenschutzhunde', + description=None, + extent=None, + providers=[StacProvider(name='Federal Office for the Environment')] + ) + ] + get.return_value.text = '