diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 9f07cf70..4d571637 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -28,16 +28,16 @@ updates: patterns: - '*' - - package-ecosystem: 'pip' - directory: '/data/src' - schedule: - interval: 'weekly' - target-branch: 'staging' - commit-message: - prefix: 'deps' - open-pull-requests-limit: 10 - groups: - all-pip: - applies-to: version-updates - patterns: - - '*' + # - package-ecosystem: 'pip' + # directory: '/data/src' + # schedule: + # interval: 'weekly' + # target-branch: 'staging' + # commit-message: + # prefix: 'deps' + # open-pull-requests-limit: 10 + # groups: + # all-pip: + # applies-to: version-updates + # patterns: + # - '*' diff --git a/data/src/Pipfile b/data/src/Pipfile index 5c2b41e5..ebe4fd46 100644 --- a/data/src/Pipfile +++ b/data/src/Pipfile @@ -13,29 +13,29 @@ matplotlib = "*" rasterio = "*" scikit-learn = "*" mapclassify = "*" -black = "*" fiona = "*" esridump = "*" sqlalchemy = "*" psycopg2-binary = "*" -geoalchemy2 = "*" mapbox = "*" google-cloud-storage = "*" pydantic = "==2.8.2" -data-diff = {extras = ["postgresql"], version = "*"} future = "*" slack-sdk = "*" -pytest = "*" networkx = "*" libpysal = "*" jenkspy = "*" pyarrow = "*" tqdm = "*" +geoalchemy2 ="*" + +[dev-packages] +black = "*" +pytest = "*" vulture = "*" pylint = "*" radon = "*" - -[dev-packages] +ruff = "*" [requires] python_version = "3.11" diff --git a/data/src/Pipfile.lock b/data/src/Pipfile.lock index 1d6d561e..a88f8015 100644 --- a/data/src/Pipfile.lock +++ b/data/src/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "96d3d538b2f2943e62c3ec5ca3d96a8e55831888b586e1d916cc400b1161e330" + "sha256": "b0cc0a48b0d260c226c0a714f60c880ea2b05027dcaba59b3eff007abe2ac1c9" }, "pipfile-spec": 6, "requires": { @@ -25,13 +25,6 @@ "markers": "python_version >= '3.7'", "version": "==2.4.0" }, - "agate": { - "hashes": [ - "sha256:1cf329510b3dde07c4ad1740b7587c9c679abc3dcd92bb1107eabc10c2e03c50", - "sha256:bc60880c2ee59636a2a80cd8603d63f995be64526abf3cbba12f00767bcd5b3d" - ], - "version": "==1.9.1" - }, "annotated-types": { "hashes": [ "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", @@ -42,11 +35,11 @@ }, "anyio": { "hashes": [ - "sha256:137b4559cbb034c477165047febb6ff83f390fc3b20bf181c1fc0a728cb8beeb", - "sha256:c7d2e9d63e31599eeb636c8c5c03a7e108d73b345f064f1c19fdc87b79036a9a" + "sha256:2f834749c602966b7d456a7567cafcb309f96482b5081d14ac93ccd457f9dd48", + "sha256:ea60c3723ab42ba6fff7e8ccb0488c898ec538ff4df1f1d5e642c3601d07e352" ], "markers": "python_version >= '3.9'", - "version": "==4.6.0" + "version": "==4.7.0" }, "argon2-cffi": { "hashes": [ @@ -91,20 +84,13 @@ "markers": "python_version >= '3.8'", "version": "==1.3.0" }, - "astroid": { - "hashes": [ - "sha256:5eba185467253501b62a9f113c263524b4f5d55e1b30456370eed4cdbd6438fd", - "sha256:e73d0b62dd680a7c07cb2cd0ce3c22570b044dd01bd994bc3a2dd16c6cbba162" - ], - "markers": "python_full_version >= '3.9.0'", - "version": "==3.3.4" - }, "asttokens": { "hashes": [ - "sha256:051ed49c3dcae8913ea7cd08e46a606dba30b79993209636c4875bc1d637bc24", - "sha256:b03869718ba9a6eb027e134bfdf69f38a236d681c83c160d510768af11254ba0" + "sha256:0dcd8baa8d62b0c1d118b399b2ddba3c4aff271d0d7a9e0d4c1681c79035bbc7", + "sha256:e3078351a059199dd5138cb1c706e6430c05eff2ff136af5eb4790f9d28932e2" ], - "version": "==2.4.1" + "markers": "python_version >= '3.8'", + "version": "==3.0.0" }, "async-lru": { "hashes": [ @@ -130,14 +116,6 @@ "markers": "python_version >= '3.8'", "version": "==2.16.0" }, - "backports.tarfile": { - "hashes": [ - "sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34", - "sha256:d75e02c268746e1b8144c278978b6e98e85de6ad16f8e4b0844a154557eca991" - ], - "markers": "python_version < '3.12'", - "version": "==1.2.0" - }, "beautifulsoup4": { "hashes": [ "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051", @@ -146,66 +124,37 @@ "markers": "python_full_version >= '3.6.0'", "version": "==4.12.3" }, - "black": { - "hashes": [ - "sha256:09cdeb74d494ec023ded657f7092ba518e8cf78fa8386155e4a03fdcc44679e6", - "sha256:1f13f7f386f86f8121d76599114bb8c17b69d962137fc70efe56137727c7047e", - "sha256:2500945420b6784c38b9ee885af039f5e7471ef284ab03fa35ecdde4688cd83f", - "sha256:2b59b250fdba5f9a9cd9d0ece6e6d993d91ce877d121d161e4698af3eb9c1018", - "sha256:3c4285573d4897a7610054af5a890bde7c65cb466040c5f0c8b732812d7f0e5e", - "sha256:505289f17ceda596658ae81b61ebbe2d9b25aa78067035184ed0a9d855d18afd", - "sha256:62e8730977f0b77998029da7971fa896ceefa2c4c4933fcd593fa599ecbf97a4", - "sha256:649f6d84ccbae73ab767e206772cc2d7a393a001070a4c814a546afd0d423aed", - "sha256:6e55d30d44bed36593c3163b9bc63bf58b3b30e4611e4d88a0c3c239930ed5b2", - "sha256:707a1ca89221bc8a1a64fb5e15ef39cd755633daa672a9db7498d1c19de66a42", - "sha256:72901b4913cbac8972ad911dc4098d5753704d1f3c56e44ae8dce99eecb0e3af", - "sha256:73bbf84ed136e45d451a260c6b73ed674652f90a2b3211d6a35e78054563a9bb", - "sha256:7c046c1d1eeb7aea9335da62472481d3bbf3fd986e093cffd35f4385c94ae368", - "sha256:81c6742da39f33b08e791da38410f32e27d632260e599df7245cccee2064afeb", - "sha256:837fd281f1908d0076844bc2b801ad2d369c78c45cf800cad7b61686051041af", - "sha256:972085c618ee94f402da1af548a4f218c754ea7e5dc70acb168bfaca4c2542ed", - "sha256:9e84e33b37be070ba135176c123ae52a51f82306def9f7d063ee302ecab2cf47", - "sha256:b19c9ad992c7883ad84c9b22aaa73562a16b819c1d8db7a1a1a49fb7ec13c7d2", - "sha256:d6417535d99c37cee4091a2f24eb2b6d5ec42b144d50f1f2e436d9fe1916fe1a", - "sha256:eab4dd44ce80dea27dc69db40dab62d4ca96112f87996bca68cd75639aeb2e4c", - "sha256:f490dbd59680d809ca31efdae20e634f3fae27fba3ce0ba3208333b713bc3920", - "sha256:fb6e2c0b86bbd43dee042e48059c9ad7830abd5c94b0bc518c0eeec57c3eddc1" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==24.8.0" - }, "bleach": { "hashes": [ - "sha256:0a31f1837963c41d46bbf1331b8778e1308ea0791db03cc4e7357b97cf42a8fe", - "sha256:3225f354cfc436b9789c66c4ee030194bee0568fbf9cbdad3bc8b5c26c5f12b6" + "sha256:117d9c6097a7c3d22fd578fcd8d35ff1e125df6736f554da4e432fdd63f31e5e", + "sha256:123e894118b8a599fd80d3ec1a6d4cc7ce4e5882b1317a7e1ba69b56e95f991f" ], - "markers": "python_version >= '3.8'", - "version": "==6.1.0" + "markers": "python_version >= '3.9'", + "version": "==6.2.0" }, "boto3": { "hashes": [ - "sha256:d2851aec8e9dc6937977acbe9a5124ecc31b3ad5f50a10cd9ae52636da3f52fa", - "sha256:d89c3459db89c5408e83219ab849ffd0146bc4285e75cdc67c6e45d390a12df2" + "sha256:5ef7166fe5060637b92af8dc152cd7acecf96b3fc9c5456706a886cadb534391", + "sha256:fc8001519c8842e766ad3793bde3fbd0bb39e821a582fc12cf67876b8f3cf7f1" ], "markers": "python_version >= '3.8'", - "version": "==1.35.30" + "version": "==1.35.78" }, "botocore": { "hashes": [ - "sha256:3bb9f9dde001608671ea74681ac3cec06bbbb10cba8cb8c1387a25e843075ce0", - "sha256:ab5350e8a50e48d371fa2d517d65c29a40c43788cb9a15387f93eac5a23df0fd" + "sha256:41c37bd7c0326f25122f33ec84fb80fc0a14d7fcc9961431b0e57568e88c9cb5", + "sha256:6905036c25449ae8dba5e950e4b908e4b8a6fe6b516bf61e007ecb62fa21f323" ], "markers": "python_version >= '3.8'", - "version": "==1.35.30" + "version": "==1.35.78" }, "cachecontrol": { "hashes": [ - "sha256:7db1195b41c81f8274a7bbd97c956f44e8348265a1bc7641c37dfebc39f0c938", - "sha256:f5bf3f0620c38db2e5122c0726bdebb0d16869de966ea6a2befe92470b740ea0" + "sha256:06ef916a1e4eb7dba9948cdfc9c76e749db2e02104a9a1277e8b642591a0f717", + "sha256:65e3abd62b06382ce3894df60dde9e0deb92aeb734724f68fa4f3b91e97206b9" ], - "markers": "python_version >= '3.7'", - "version": "==0.14.0" + "markers": "python_version >= '3.8'", + "version": "==0.14.1" }, "cachetools": { "hashes": [ @@ -293,104 +242,119 @@ "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87", "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b" ], - "markers": "platform_python_implementation != 'PyPy'", + "markers": "python_version >= '3.8'", "version": "==1.17.1" }, "charset-normalizer": { "hashes": [ - "sha256:06435b539f889b1f6f4ac1758871aae42dc3a8c0e24ac9e60c2384973ad73027", - "sha256:06a81e93cd441c56a9b65d8e1d043daeb97a3d0856d177d5c90ba85acb3db087", - "sha256:0a55554a2fa0d408816b3b5cedf0045f4b8e1a6065aec45849de2d6f3f8e9786", - "sha256:0b2b64d2bb6d3fb9112bafa732def486049e63de9618b5843bcdd081d8144cd8", - "sha256:10955842570876604d404661fbccbc9c7e684caf432c09c715ec38fbae45ae09", - "sha256:122c7fa62b130ed55f8f285bfd56d5f4b4a5b503609d181f9ad85e55c89f4185", - "sha256:1ceae2f17a9c33cb48e3263960dc5fc8005351ee19db217e9b1bb15d28c02574", - "sha256:1d3193f4a680c64b4b6a9115943538edb896edc190f0b222e73761716519268e", - "sha256:1f79682fbe303db92bc2b1136016a38a42e835d932bab5b3b1bfcfbf0640e519", - "sha256:2127566c664442652f024c837091890cb1942c30937add288223dc895793f898", - "sha256:22afcb9f253dac0696b5a4be4a1c0f8762f8239e21b99680099abd9b2b1b2269", - "sha256:25baf083bf6f6b341f4121c2f3c548875ee6f5339300e08be3f2b2ba1721cdd3", - "sha256:2e81c7b9c8979ce92ed306c249d46894776a909505d8f5a4ba55b14206e3222f", - "sha256:3287761bc4ee9e33561a7e058c72ac0938c4f57fe49a09eae428fd88aafe7bb6", - "sha256:34d1c8da1e78d2e001f363791c98a272bb734000fcef47a491c1e3b0505657a8", - "sha256:37e55c8e51c236f95b033f6fb391d7d7970ba5fe7ff453dad675e88cf303377a", - "sha256:3d47fa203a7bd9c5b6cee4736ee84ca03b8ef23193c0d1ca99b5089f72645c73", - "sha256:3e4d1f6587322d2788836a99c69062fbb091331ec940e02d12d179c1d53e25fc", - "sha256:42cb296636fcc8b0644486d15c12376cb9fa75443e00fb25de0b8602e64c1714", - "sha256:45485e01ff4d3630ec0d9617310448a8702f70e9c01906b0d0118bdf9d124cf2", - "sha256:4a78b2b446bd7c934f5dcedc588903fb2f5eec172f3d29e52a9096a43722adfc", - "sha256:4ab2fe47fae9e0f9dee8c04187ce5d09f48eabe611be8259444906793ab7cbce", - "sha256:4d0d1650369165a14e14e1e47b372cfcb31d6ab44e6e33cb2d4e57265290044d", - "sha256:549a3a73da901d5bc3ce8d24e0600d1fa85524c10287f6004fbab87672bf3e1e", - "sha256:55086ee1064215781fff39a1af09518bc9255b50d6333f2e4c74ca09fac6a8f6", - "sha256:572c3763a264ba47b3cf708a44ce965d98555f618ca42c926a9c1616d8f34269", - "sha256:573f6eac48f4769d667c4442081b1794f52919e7edada77495aaed9236d13a96", - "sha256:5b4c145409bef602a690e7cfad0a15a55c13320ff7a3ad7ca59c13bb8ba4d45d", - "sha256:6463effa3186ea09411d50efc7d85360b38d5f09b870c48e4600f63af490e56a", - "sha256:65f6f63034100ead094b8744b3b97965785388f308a64cf8d7c34f2f2e5be0c4", - "sha256:663946639d296df6a2bb2aa51b60a2454ca1cb29835324c640dafb5ff2131a77", - "sha256:6897af51655e3691ff853668779c7bad41579facacf5fd7253b0133308cf000d", - "sha256:68d1f8a9e9e37c1223b656399be5d6b448dea850bed7d0f87a8311f1ff3dabb0", - "sha256:6ac7ffc7ad6d040517be39eb591cac5ff87416c2537df6ba3cba3bae290c0fed", - "sha256:6b3251890fff30ee142c44144871185dbe13b11bab478a88887a639655be1068", - "sha256:6c4caeef8fa63d06bd437cd4bdcf3ffefe6738fb1b25951440d80dc7df8c03ac", - "sha256:6ef1d82a3af9d3eecdba2321dc1b3c238245d890843e040e41e470ffa64c3e25", - "sha256:753f10e867343b4511128c6ed8c82f7bec3bd026875576dfd88483c5c73b2fd8", - "sha256:7cd13a2e3ddeed6913a65e66e94b51d80a041145a026c27e6bb76c31a853c6ab", - "sha256:7ed9e526742851e8d5cc9e6cf41427dfc6068d4f5a3bb03659444b4cabf6bc26", - "sha256:7f04c839ed0b6b98b1a7501a002144b76c18fb1c1850c8b98d458ac269e26ed2", - "sha256:802fe99cca7457642125a8a88a084cef28ff0cf9407060f7b93dca5aa25480db", - "sha256:80402cd6ee291dcb72644d6eac93785fe2c8b9cb30893c1af5b8fdd753b9d40f", - "sha256:8465322196c8b4d7ab6d1e049e4c5cb460d0394da4a27d23cc242fbf0034b6b5", - "sha256:86216b5cee4b06df986d214f664305142d9c76df9b6512be2738aa72a2048f99", - "sha256:87d1351268731db79e0f8e745d92493ee2841c974128ef629dc518b937d9194c", - "sha256:8bdb58ff7ba23002a4c5808d608e4e6c687175724f54a5dade5fa8c67b604e4d", - "sha256:8c622a5fe39a48f78944a87d4fb8a53ee07344641b0562c540d840748571b811", - "sha256:8d756e44e94489e49571086ef83b2bb8ce311e730092d2c34ca8f7d925cb20aa", - "sha256:8f4a014bc36d3c57402e2977dada34f9c12300af536839dc38c0beab8878f38a", - "sha256:9063e24fdb1e498ab71cb7419e24622516c4a04476b17a2dab57e8baa30d6e03", - "sha256:90d558489962fd4918143277a773316e56c72da56ec7aa3dc3dbbe20fdfed15b", - "sha256:923c0c831b7cfcb071580d3f46c4baf50f174be571576556269530f4bbd79d04", - "sha256:95f2a5796329323b8f0512e09dbb7a1860c46a39da62ecb2324f116fa8fdc85c", - "sha256:96b02a3dc4381e5494fad39be677abcb5e6634bf7b4fa83a6dd3112607547001", - "sha256:9f96df6923e21816da7e0ad3fd47dd8f94b2a5ce594e00677c0013018b813458", - "sha256:a10af20b82360ab00827f916a6058451b723b4e65030c5a18577c8b2de5b3389", - "sha256:a50aebfa173e157099939b17f18600f72f84eed3049e743b68ad15bd69b6bf99", - "sha256:a981a536974bbc7a512cf44ed14938cf01030a99e9b3a06dd59578882f06f985", - "sha256:a9a8e9031d613fd2009c182b69c7b2c1ef8239a0efb1df3f7c8da66d5dd3d537", - "sha256:ae5f4161f18c61806f411a13b0310bea87f987c7d2ecdbdaad0e94eb2e404238", - "sha256:aed38f6e4fb3f5d6bf81bfa990a07806be9d83cf7bacef998ab1a9bd660a581f", - "sha256:b01b88d45a6fcb69667cd6d2f7a9aeb4bf53760d7fc536bf679ec94fe9f3ff3d", - "sha256:b261ccdec7821281dade748d088bb6e9b69e6d15b30652b74cbbac25e280b796", - "sha256:b2b0a0c0517616b6869869f8c581d4eb2dd83a4d79e0ebcb7d373ef9956aeb0a", - "sha256:b4a23f61ce87adf89be746c8a8974fe1c823c891d8f86eb218bb957c924bb143", - "sha256:bd8f7df7d12c2db9fab40bdd87a7c09b1530128315d047a086fa3ae3435cb3a8", - "sha256:beb58fe5cdb101e3a055192ac291b7a21e3b7ef4f67fa1d74e331a7f2124341c", - "sha256:c002b4ffc0be611f0d9da932eb0f704fe2602a9a949d1f738e4c34c75b0863d5", - "sha256:c083af607d2515612056a31f0a8d9e0fcb5876b7bfc0abad3ecd275bc4ebc2d5", - "sha256:c180f51afb394e165eafe4ac2936a14bee3eb10debc9d9e4db8958fe36afe711", - "sha256:c235ebd9baae02f1b77bcea61bce332cb4331dc3617d254df3323aa01ab47bd4", - "sha256:cd70574b12bb8a4d2aaa0094515df2463cb429d8536cfb6c7ce983246983e5a6", - "sha256:d0eccceffcb53201b5bfebb52600a5fb483a20b61da9dbc885f8b103cbe7598c", - "sha256:d965bba47ddeec8cd560687584e88cf699fd28f192ceb452d1d7ee807c5597b7", - "sha256:db364eca23f876da6f9e16c9da0df51aa4f104a972735574842618b8c6d999d4", - "sha256:ddbb2551d7e0102e7252db79ba445cdab71b26640817ab1e3e3648dad515003b", - "sha256:deb6be0ac38ece9ba87dea880e438f25ca3eddfac8b002a2ec3d9183a454e8ae", - "sha256:e06ed3eb3218bc64786f7db41917d4e686cc4856944f53d5bdf83a6884432e12", - "sha256:e27ad930a842b4c5eb8ac0016b0a54f5aebbe679340c26101df33424142c143c", - "sha256:e537484df0d8f426ce2afb2d0f8e1c3d0b114b83f8850e5f2fbea0e797bd82ae", - "sha256:eb00ed941194665c332bf8e078baf037d6c35d7c4f3102ea2d4f16ca94a26dc8", - "sha256:eb6904c354526e758fda7167b33005998fb68c46fbc10e013ca97f21ca5c8887", - "sha256:eb8821e09e916165e160797a6c17edda0679379a4be5c716c260e836e122f54b", - "sha256:efcb3f6676480691518c177e3b465bcddf57cea040302f9f4e6e191af91174d4", - "sha256:f27273b60488abe721a075bcca6d7f3964f9f6f067c8c4c605743023d7d3944f", - "sha256:f30c3cb33b24454a82faecaf01b19c18562b1e89558fb6c56de4d9118a032fd5", - "sha256:fb69256e180cb6c8a894fee62b3afebae785babc1ee98b81cdf68bbca1987f33", - "sha256:fd1abc0d89e30cc4e02e4064dc67fcc51bd941eb395c502aac3ec19fab46b519", - "sha256:ff8fa367d09b717b2a17a052544193ad76cd49979c805768879cb63d9ca50561" + "sha256:0099d79bdfcf5c1f0c2c72f91516702ebf8b0b8ddd8905f97a8aecf49712c621", + "sha256:0713f3adb9d03d49d365b70b84775d0a0d18e4ab08d12bc46baa6132ba78aaf6", + "sha256:07afec21bbbbf8a5cc3651aa96b980afe2526e7f048fdfb7f1014d84acc8b6d8", + "sha256:0b309d1747110feb25d7ed6b01afdec269c647d382c857ef4663bbe6ad95a912", + "sha256:0d99dd8ff461990f12d6e42c7347fd9ab2532fb70e9621ba520f9e8637161d7c", + "sha256:0de7b687289d3c1b3e8660d0741874abe7888100efe14bd0f9fd7141bcbda92b", + "sha256:1110e22af8ca26b90bd6364fe4c763329b0ebf1ee213ba32b68c73de5752323d", + "sha256:130272c698667a982a5d0e626851ceff662565379baf0ff2cc58067b81d4f11d", + "sha256:136815f06a3ae311fae551c3df1f998a1ebd01ddd424aa5603a4336997629e95", + "sha256:14215b71a762336254351b00ec720a8e85cada43b987da5a042e4ce3e82bd68e", + "sha256:1db4e7fefefd0f548d73e2e2e041f9df5c59e178b4c72fbac4cc6f535cfb1565", + "sha256:1ffd9493de4c922f2a38c2bf62b831dcec90ac673ed1ca182fe11b4d8e9f2a64", + "sha256:2006769bd1640bdf4d5641c69a3d63b71b81445473cac5ded39740a226fa88ab", + "sha256:20587d20f557fe189b7947d8e7ec5afa110ccf72a3128d61a2a387c3313f46be", + "sha256:223217c3d4f82c3ac5e29032b3f1c2eb0fb591b72161f86d93f5719079dae93e", + "sha256:27623ba66c183eca01bf9ff833875b459cad267aeeb044477fedac35e19ba907", + "sha256:285e96d9d53422efc0d7a17c60e59f37fbf3dfa942073f666db4ac71e8d726d0", + "sha256:2de62e8801ddfff069cd5c504ce3bc9672b23266597d4e4f50eda28846c322f2", + "sha256:2f6c34da58ea9c1a9515621f4d9ac379871a8f21168ba1b5e09d74250de5ad62", + "sha256:309a7de0a0ff3040acaebb35ec45d18db4b28232f21998851cfa709eeff49d62", + "sha256:35c404d74c2926d0287fbd63ed5d27eb911eb9e4a3bb2c6d294f3cfd4a9e0c23", + "sha256:3710a9751938947e6327ea9f3ea6332a09bf0ba0c09cae9cb1f250bd1f1549bc", + "sha256:3d59d125ffbd6d552765510e3f31ed75ebac2c7470c7274195b9161a32350284", + "sha256:40d3ff7fc90b98c637bda91c89d51264a3dcf210cade3a2c6f838c7268d7a4ca", + "sha256:425c5f215d0eecee9a56cdb703203dda90423247421bf0d67125add85d0c4455", + "sha256:43193c5cda5d612f247172016c4bb71251c784d7a4d9314677186a838ad34858", + "sha256:44aeb140295a2f0659e113b31cfe92c9061622cadbc9e2a2f7b8ef6b1e29ef4b", + "sha256:47334db71978b23ebcf3c0f9f5ee98b8d65992b65c9c4f2d34c2eaf5bcaf0594", + "sha256:4796efc4faf6b53a18e3d46343535caed491776a22af773f366534056c4e1fbc", + "sha256:4a51b48f42d9358460b78725283f04bddaf44a9358197b889657deba38f329db", + "sha256:4b67fdab07fdd3c10bb21edab3cbfe8cf5696f453afce75d815d9d7223fbe88b", + "sha256:4ec9dd88a5b71abfc74e9df5ebe7921c35cbb3b641181a531ca65cdb5e8e4dea", + "sha256:4f9fc98dad6c2eaa32fc3af1417d95b5e3d08aff968df0cd320066def971f9a6", + "sha256:54b6a92d009cbe2fb11054ba694bc9e284dad30a26757b1e372a1fdddaf21920", + "sha256:55f56e2ebd4e3bc50442fbc0888c9d8c94e4e06a933804e2af3e89e2f9c1c749", + "sha256:5726cf76c982532c1863fb64d8c6dd0e4c90b6ece9feb06c9f202417a31f7dd7", + "sha256:5d447056e2ca60382d460a604b6302d8db69476fd2015c81e7c35417cfabe4cd", + "sha256:5ed2e36c3e9b4f21dd9422f6893dec0abf2cca553af509b10cd630f878d3eb99", + "sha256:5ff2ed8194587faf56555927b3aa10e6fb69d931e33953943bc4f837dfee2242", + "sha256:62f60aebecfc7f4b82e3f639a7d1433a20ec32824db2199a11ad4f5e146ef5ee", + "sha256:63bc5c4ae26e4bc6be6469943b8253c0fd4e4186c43ad46e713ea61a0ba49129", + "sha256:6b40e8d38afe634559e398cc32b1472f376a4099c75fe6299ae607e404c033b2", + "sha256:6b493a043635eb376e50eedf7818f2f322eabbaa974e948bd8bdd29eb7ef2a51", + "sha256:6dba5d19c4dfab08e58d5b36304b3f92f3bd5d42c1a3fa37b5ba5cdf6dfcbcee", + "sha256:6fd30dc99682dc2c603c2b315bded2799019cea829f8bf57dc6b61efde6611c8", + "sha256:707b82d19e65c9bd28b81dde95249b07bf9f5b90ebe1ef17d9b57473f8a64b7b", + "sha256:7706f5850360ac01d80c89bcef1640683cc12ed87f42579dab6c5d3ed6888613", + "sha256:7782afc9b6b42200f7362858f9e73b1f8316afb276d316336c0ec3bd73312742", + "sha256:79983512b108e4a164b9c8d34de3992f76d48cadc9554c9e60b43f308988aabe", + "sha256:7f683ddc7eedd742e2889d2bfb96d69573fde1d92fcb811979cdb7165bb9c7d3", + "sha256:82357d85de703176b5587dbe6ade8ff67f9f69a41c0733cf2425378b49954de5", + "sha256:84450ba661fb96e9fd67629b93d2941c871ca86fc38d835d19d4225ff946a631", + "sha256:86f4e8cca779080f66ff4f191a685ced73d2f72d50216f7112185dc02b90b9b7", + "sha256:8cda06946eac330cbe6598f77bb54e690b4ca93f593dee1568ad22b04f347c15", + "sha256:8ce7fd6767a1cc5a92a639b391891bf1c268b03ec7e021c7d6d902285259685c", + "sha256:8ff4e7cdfdb1ab5698e675ca622e72d58a6fa2a8aa58195de0c0061288e6e3ea", + "sha256:9289fd5dddcf57bab41d044f1756550f9e7cf0c8e373b8cdf0ce8773dc4bd417", + "sha256:92a7e36b000bf022ef3dbb9c46bfe2d52c047d5e3f3343f43204263c5addc250", + "sha256:92db3c28b5b2a273346bebb24857fda45601aef6ae1c011c0a997106581e8a88", + "sha256:95c3c157765b031331dd4db3c775e58deaee050a3042fcad72cbc4189d7c8dca", + "sha256:980b4f289d1d90ca5efcf07958d3eb38ed9c0b7676bf2831a54d4f66f9c27dfa", + "sha256:9ae4ef0b3f6b41bad6366fb0ea4fc1d7ed051528e113a60fa2a65a9abb5b1d99", + "sha256:9c98230f5042f4945f957d006edccc2af1e03ed5e37ce7c373f00a5a4daa6149", + "sha256:9fa2566ca27d67c86569e8c85297aaf413ffab85a8960500f12ea34ff98e4c41", + "sha256:a14969b8691f7998e74663b77b4c36c0337cb1df552da83d5c9004a93afdb574", + "sha256:a8aacce6e2e1edcb6ac625fb0f8c3a9570ccc7bfba1f63419b3769ccf6a00ed0", + "sha256:a8e538f46104c815be19c975572d74afb53f29650ea2025bbfaef359d2de2f7f", + "sha256:aa41e526a5d4a9dfcfbab0716c7e8a1b215abd3f3df5a45cf18a12721d31cb5d", + "sha256:aa693779a8b50cd97570e5a0f343538a8dbd3e496fa5dcb87e29406ad0299654", + "sha256:ab22fbd9765e6954bc0bcff24c25ff71dcbfdb185fcdaca49e81bac68fe724d3", + "sha256:ab2e5bef076f5a235c3774b4f4028a680432cded7cad37bba0fd90d64b187d19", + "sha256:ab973df98fc99ab39080bfb0eb3a925181454d7c3ac8a1e695fddfae696d9e90", + "sha256:af73657b7a68211996527dbfeffbb0864e043d270580c5aef06dc4b659a4b578", + "sha256:b197e7094f232959f8f20541ead1d9862ac5ebea1d58e9849c1bf979255dfac9", + "sha256:b295729485b06c1a0683af02a9e42d2caa9db04a373dc38a6a58cdd1e8abddf1", + "sha256:b8831399554b92b72af5932cdbbd4ddc55c55f631bb13ff8fe4e6536a06c5c51", + "sha256:b8dcd239c743aa2f9c22ce674a145e0a25cb1566c495928440a181ca1ccf6719", + "sha256:bcb4f8ea87d03bc51ad04add8ceaf9b0f085ac045ab4d74e73bbc2dc033f0236", + "sha256:bd7af3717683bea4c87acd8c0d3d5b44d56120b26fd3f8a692bdd2d5260c620a", + "sha256:bf4475b82be41b07cc5e5ff94810e6a01f276e37c2d55571e3fe175e467a1a1c", + "sha256:c3e446d253bd88f6377260d07c895816ebf33ffffd56c1c792b13bff9c3e1ade", + "sha256:c57516e58fd17d03ebe67e181a4e4e2ccab1168f8c2976c6a334d4f819fe5944", + "sha256:c94057af19bc953643a33581844649a7fdab902624d2eb739738a30e2b3e60fc", + "sha256:cab5d0b79d987c67f3b9e9c53f54a61360422a5a0bc075f43cab5621d530c3b6", + "sha256:ce031db0408e487fd2775d745ce30a7cd2923667cf3b69d48d219f1d8f5ddeb6", + "sha256:cee4373f4d3ad28f1ab6290684d8e2ebdb9e7a1b74fdc39e4c211995f77bec27", + "sha256:d5b054862739d276e09928de37c79ddeec42a6e1bfc55863be96a36ba22926f6", + "sha256:dbe03226baf438ac4fda9e2d0715022fd579cb641c4cf639fa40d53b2fe6f3e2", + "sha256:dc15e99b2d8a656f8e666854404f1ba54765871104e50c8e9813af8a7db07f12", + "sha256:dcaf7c1524c0542ee2fc82cc8ec337f7a9f7edee2532421ab200d2b920fc97cf", + "sha256:dd4eda173a9fcccb5f2e2bd2a9f423d180194b1bf17cf59e3269899235b2a114", + "sha256:dd9a8bd8900e65504a305bf8ae6fa9fbc66de94178c420791d0293702fce2df7", + "sha256:de7376c29d95d6719048c194a9cf1a1b0393fbe8488a22008610b0361d834ecf", + "sha256:e7fdd52961feb4c96507aa649550ec2a0d527c086d284749b2f582f2d40a2e0d", + "sha256:e91f541a85298cf35433bf66f3fab2a4a2cff05c127eeca4af174f6d497f0d4b", + "sha256:e9e3c4c9e1ed40ea53acf11e2a386383c3304212c965773704e4603d589343ed", + "sha256:ee803480535c44e7f5ad00788526da7d85525cfefaf8acf8ab9a310000be4b03", + "sha256:f09cb5a7bbe1ecae6e87901a2eb23e0256bb524a79ccc53eb0b7629fbe7677c4", + "sha256:f19c1585933c82098c2a520f8ec1227f20e339e33aca8fa6f956f6691b784e67", + "sha256:f1a2f519ae173b5b6a2c9d5fa3116ce16e48b3462c8b96dfdded11055e3d6365", + "sha256:f28f891ccd15c514a0981f3b9db9aa23d62fe1a99997512b0491d2ed323d229a", + "sha256:f3e73a4255342d4eb26ef6df01e3962e73aa29baa3124a8e824c5d3364a65748", + "sha256:f606a1881d2663630ea5b8ce2efe2111740df4b687bd78b34a8131baa007f79b", + "sha256:fe9f97feb71aa9896b81973a7bbada8c49501dc73e58a10fcef6663af95e5079", + "sha256:ffc519621dce0c767e96b9c53f09c5d215578e10b02c285809f76509a3931482" ], "markers": "python_full_version >= '3.7.0'", - "version": "==3.3.2" + "version": "==3.4.0" }, "click": { "hashes": [ @@ -415,14 +379,6 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3' and python_version < '4'", "version": "==0.7.2" }, - "colorama": { - "hashes": [ - "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", - "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6" - ], - "markers": "python_version >= '3.5'", - "version": "==0.4.6" - }, "comm": { "hashes": [ "sha256:3fd7a84065306e07bea1773df6eb8282de51ba82f77c72f9c85716ab11fe980e", @@ -433,107 +389,63 @@ }, "contourpy": { "hashes": [ - "sha256:00ccd0dbaad6d804ab259820fa7cb0b8036bda0686ef844d24125d8287178ce0", - "sha256:0be4d8425bfa755e0fd76ee1e019636ccc7c29f77a7c86b4328a9eb6a26d0639", - "sha256:0dce35502151b6bd35027ac39ba6e5a44be13a68f55735c3612c568cac3805fd", - "sha256:0fa4c02abe6c446ba70d96ece336e621efa4aecae43eaa9b030ae5fb92b309ad", - "sha256:14e262f67bd7e6eb6880bc564dcda30b15e351a594657e55b7eec94b6ef72843", - "sha256:167d6c890815e1dac9536dca00828b445d5d0df4d6a8c6adb4a7ec3166812fa8", - "sha256:1ec4dc6bf570f5b22ed0d7efba0dfa9c5b9e0431aeea7581aa217542d9e809a4", - "sha256:303c252947ab4b14c08afeb52375b26781ccd6a5ccd81abcdfc1fafd14cf93c1", - "sha256:31cd3a85dbdf1fc002280c65caa7e2b5f65e4a973fcdf70dd2fdcb9868069294", - "sha256:32b238b3b3b649e09ce9aaf51f0c261d38644bdfa35cbaf7b263457850957a84", - "sha256:33c92cdae89ec5135d036e7218e69b0bb2851206077251f04a6c4e0e21f03927", - "sha256:345af746d7766821d05d72cb8f3845dfd08dd137101a2cb9b24de277d716def8", - "sha256:3634b5385c6716c258d0419c46d05c8aa7dc8cb70326c9a4fb66b69ad2b52e09", - "sha256:364174c2a76057feef647c802652f00953b575723062560498dc7930fc9b1cb7", - "sha256:36e0cff201bcb17a0a8ecc7f454fe078437fa6bda730e695a92f2d9932bd507f", - "sha256:36f965570cff02b874773c49bfe85562b47030805d7d8360748f3eca570f4cab", - "sha256:3bb3808858a9dc68f6f03d319acd5f1b8a337e6cdda197f02f4b8ff67ad2057b", - "sha256:3e1c7fa44aaae40a2247e2e8e0627f4bea3dd257014764aa644f319a5f8600e3", - "sha256:3faeb2998e4fcb256542e8a926d08da08977f7f5e62cf733f3c211c2a5586223", - "sha256:420d39daa61aab1221567b42eecb01112908b2cab7f1b4106a52caaec8d36973", - "sha256:4553c421929ec95fb07b3aaca0fae668b2eb5a5203d1217ca7c34c063c53d087", - "sha256:4865cd1d419e0c7a7bf6de1777b185eebdc51470800a9f42b9e9decf17762081", - "sha256:4cfb5c62ce023dfc410d6059c936dcf96442ba40814aefbfa575425a3a7f19dc", - "sha256:4d63ee447261e963af02642ffcb864e5a2ee4cbfd78080657a9880b8b1868e18", - "sha256:570ef7cf892f0afbe5b2ee410c507ce12e15a5fa91017a0009f79f7d93a1268f", - "sha256:637f674226be46f6ba372fd29d9523dd977a291f66ab2a74fbeb5530bb3f445d", - "sha256:68a32389b06b82c2fdd68276148d7b9275b5f5cf13e5417e4252f6d1a34f72a2", - "sha256:69375194457ad0fad3a839b9e29aa0b0ed53bb54db1bfb6c3ae43d111c31ce41", - "sha256:6cb6cc968059db9c62cb35fbf70248f40994dfcd7aa10444bbf8b3faeb7c2d67", - "sha256:710a26b3dc80c0e4febf04555de66f5fd17e9cf7170a7b08000601a10570bda6", - "sha256:732896af21716b29ab3e988d4ce14bc5133733b85956316fb0c56355f398099b", - "sha256:75ee7cb1a14c617f34a51d11fa7524173e56551646828353c4af859c56b766e2", - "sha256:76a896b2f195b57db25d6b44e7e03f221d32fe318d03ede41f8b4d9ba1bff53c", - "sha256:76c905ef940a4474a6289c71d53122a4f77766eef23c03cd57016ce19d0f7b42", - "sha256:7a52040312b1a858b5e31ef28c2e865376a386c60c0e248370bbea2d3f3b760d", - "sha256:7ffa0db17717a8ffb127efd0c95a4362d996b892c2904db72428d5b52e1938a4", - "sha256:81cb5ed4952aae6014bc9d0421dec7c5835c9c8c31cdf51910b708f548cf58e5", - "sha256:834e0cfe17ba12f79963861e0f908556b2cedd52e1f75e6578801febcc6a9f49", - "sha256:87ddffef1dbe5e669b5c2440b643d3fdd8622a348fe1983fad7a0f0ccb1cd67b", - "sha256:880ea32e5c774634f9fcd46504bf9f080a41ad855f4fef54f5380f5133d343c7", - "sha256:8ca947601224119117f7c19c9cdf6b3ab54c5726ef1d906aa4a69dfb6dd58102", - "sha256:90f73a5116ad1ba7174341ef3ea5c3150ddf20b024b98fb0c3b29034752c8aeb", - "sha256:92f8557cbb07415a4d6fa191f20fd9d2d9eb9c0b61d1b2f52a8926e43c6e9af7", - "sha256:94e848a6b83da10898cbf1311a815f770acc9b6a3f2d646f330d57eb4e87592e", - "sha256:9c0da700bf58f6e0b65312d0a5e695179a71d0163957fa381bb3c1f72972537c", - "sha256:a11077e395f67ffc2c44ec2418cfebed032cd6da3022a94fc227b6faf8e2acb8", - "sha256:aea348f053c645100612b333adc5983d87be69acdc6d77d3169c090d3b01dc35", - "sha256:b11b39aea6be6764f84360fce6c82211a9db32a7c7de8fa6dd5397cf1d079c3b", - "sha256:c6c7c2408b7048082932cf4e641fa3b8ca848259212f51c8c59c45aa7ac18f14", - "sha256:c6ec93afeb848a0845a18989da3beca3eec2c0f852322efe21af1931147d12cb", - "sha256:cacd81e2d4b6f89c9f8a5b69b86490152ff39afc58a95af002a398273e5ce589", - "sha256:d402880b84df3bec6eab53cd0cf802cae6a2ef9537e70cf75e91618a3801c20c", - "sha256:d51fca85f9f7ad0b65b4b9fe800406d0d77017d7270d31ec3fb1cc07358fdea0", - "sha256:d73f659398a0904e125280836ae6f88ba9b178b2fed6884f3b1f95b989d2c8da", - "sha256:d78ab28a03c854a873787a0a42254a0ccb3cb133c672f645c9f9c8f3ae9d0800", - "sha256:da84c537cb8b97d153e9fb208c221c45605f73147bd4cadd23bdae915042aad6", - "sha256:dbc4c3217eee163fa3984fd1567632b48d6dfd29216da3ded3d7b844a8014a66", - "sha256:e12968fdfd5bb45ffdf6192a590bd8ddd3ba9e58360b29683c6bb71a7b41edca", - "sha256:e1fd23e9d01591bab45546c089ae89d926917a66dceb3abcf01f6105d927e2cb", - "sha256:e8134301d7e204c88ed7ab50028ba06c683000040ede1d617298611f9dc6240c", - "sha256:eb8b141bb00fa977d9122636b16aa67d37fd40a3d8b52dd837e536d64b9a4d06", - "sha256:eca7e17a65f72a5133bdbec9ecf22401c62bcf4821361ef7811faee695799779", - "sha256:f317576606de89da6b7e0861cf6061f6146ead3528acabff9236458a6ba467f8", - "sha256:fd2a0fc506eccaaa7595b7e1418951f213cf8255be2600f1ea1b61e46a60c55f", - "sha256:fe41b41505a5a33aeaed2a613dccaeaa74e0e3ead6dd6fd3a118fb471644fd6c" + "sha256:041b640d4ec01922083645a94bb3b2e777e6b626788f4095cf21abbe266413c1", + "sha256:05e806338bfeaa006acbdeba0ad681a10be63b26e1b17317bfac3c5d98f36cda", + "sha256:08d9d449a61cf53033612cb368f3a1b26cd7835d9b8cd326647efe43bca7568d", + "sha256:0ffa84be8e0bd33410b17189f7164c3589c229ce5db85798076a3fa136d0e509", + "sha256:113231fe3825ebf6f15eaa8bc1f5b0ddc19d42b733345eae0934cb291beb88b6", + "sha256:14c102b0eab282427b662cb590f2e9340a9d91a1c297f48729431f2dcd16e14f", + "sha256:174e758c66bbc1c8576992cec9599ce8b6672b741b5d336b5c74e35ac382b18e", + "sha256:19c1555a6801c2f084c7ddc1c6e11f02eb6a6016ca1318dd5452ba3f613a1751", + "sha256:19d40d37c1c3a4961b4619dd9d77b12124a453cc3d02bb31a07d58ef684d3d86", + "sha256:1bf98051f1045b15c87868dbaea84f92408337d4f81d0e449ee41920ea121d3b", + "sha256:20914c8c973f41456337652a6eeca26d2148aa96dd7ac323b74516988bea89fc", + "sha256:287ccc248c9e0d0566934e7d606201abd74761b5703d804ff3df8935f523d546", + "sha256:2ba94a401342fc0f8b948e57d977557fbf4d515f03c67682dd5c6191cb2d16ec", + "sha256:31c1b55c1f34f80557d3830d3dd93ba722ce7e33a0b472cba0ec3b6535684d8f", + "sha256:36987a15e8ace5f58d4d5da9dca82d498c2bbb28dff6e5d04fbfcc35a9cb3a82", + "sha256:3a04ecd68acbd77fa2d39723ceca4c3197cb2969633836ced1bea14e219d077c", + "sha256:3e8b974d8db2c5610fb4e76307e265de0edb655ae8169e8b21f41807ccbeec4b", + "sha256:3ea9924d28fc5586bf0b42d15f590b10c224117e74409dd7a0be3b62b74a501c", + "sha256:4318af1c925fb9a4fb190559ef3eec206845f63e80fb603d47f2d6d67683901c", + "sha256:44a29502ca9c7b5ba389e620d44f2fbe792b1fb5734e8b931ad307071ec58c53", + "sha256:47734d7073fb4590b4a40122b35917cd77be5722d80683b249dac1de266aac80", + "sha256:4d76d5993a34ef3df5181ba3c92fabb93f1eaa5729504fb03423fcd9f3177242", + "sha256:4dbbc03a40f916a8420e420d63e96a1258d3d1b58cbdfd8d1f07b49fcbd38e85", + "sha256:500360b77259914f7805af7462e41f9cb7ca92ad38e9f94d6c8641b089338124", + "sha256:523a8ee12edfa36f6d2a49407f705a6ef4c5098de4f498619787e272de93f2d5", + "sha256:573abb30e0e05bf31ed067d2f82500ecfdaec15627a59d63ea2d95714790f5c2", + "sha256:5b75aa69cb4d6f137b36f7eb2ace9280cfb60c55dc5f61c731fdf6f037f958a3", + "sha256:61332c87493b00091423e747ea78200659dc09bdf7fd69edd5e98cef5d3e9a8d", + "sha256:805617228ba7e2cbbfb6c503858e626ab528ac2a32a04a2fe88ffaf6b02c32bc", + "sha256:841ad858cff65c2c04bf93875e384ccb82b654574a6d7f30453a04f04af71342", + "sha256:89785bb2a1980c1bd87f0cb1517a71cde374776a5f150936b82580ae6ead44a1", + "sha256:8eb96e79b9f3dcadbad2a3891672f81cdcab7f95b27f28f1c67d75f045b6b4f1", + "sha256:974d8145f8ca354498005b5b981165b74a195abfae9a8129df3e56771961d595", + "sha256:9ddeb796389dadcd884c7eb07bd14ef12408aaae358f0e2ae24114d797eede30", + "sha256:a045f341a77b77e1c5de31e74e966537bba9f3c4099b35bf4c2e3939dd54cdab", + "sha256:a0cffcbede75c059f535725c1680dfb17b6ba8753f0c74b14e6a9c68c29d7ea3", + "sha256:a761d9ccfc5e2ecd1bf05534eda382aa14c3e4f9205ba5b1684ecfe400716ef2", + "sha256:a7895f46d47671fa7ceec40f31fae721da51ad34bdca0bee83e38870b1f47ffd", + "sha256:a9fa36448e6a3a1a9a2ba23c02012c43ed88905ec80163f2ffe2421c7192a5d7", + "sha256:ab29962927945d89d9b293eabd0d59aea28d887d4f3be6c22deaefbb938a7277", + "sha256:abbb49fb7dac584e5abc6636b7b2a7227111c4f771005853e7d25176daaf8453", + "sha256:ac4578ac281983f63b400f7fe6c101bedc10651650eef012be1ccffcbacf3697", + "sha256:adce39d67c0edf383647a3a007de0a45fd1b08dedaa5318404f1a73059c2512b", + "sha256:ade08d343436a94e633db932e7e8407fe7de8083967962b46bdfc1b0ced39454", + "sha256:b2bdca22a27e35f16794cf585832e542123296b4687f9fd96822db6bae17bfc9", + "sha256:b2f926efda994cdf3c8d3fdb40b9962f86edbc4457e739277b961eced3d0b4c1", + "sha256:b457d6430833cee8e4b8e9b6f07aa1c161e5e0d52e118dc102c8f9bd7dd060d6", + "sha256:c414fc1ed8ee1dbd5da626cf3710c6013d3d27456651d156711fa24f24bd1291", + "sha256:cb76c1a154b83991a3cbbf0dfeb26ec2833ad56f95540b442c73950af2013750", + "sha256:dfd97abd83335045a913e3bcc4a09c0ceadbe66580cf573fe961f4a825efa699", + "sha256:e914a8cb05ce5c809dd0fe350cfbb4e881bde5e2a38dc04e3afe1b3e58bd158e", + "sha256:ece6df05e2c41bd46776fbc712e0996f7c94e0d0543af1656956d150c4ca7c81", + "sha256:efa874e87e4a647fd2e4f514d5e91c7d493697127beb95e77d2f7561f6905bd9", + "sha256:f611e628ef06670df83fce17805c344710ca5cde01edfdc72751311da8585375" ], - "markers": "python_version >= '3.9'", - "version": "==1.3.0" - }, - "cryptography": { - "hashes": [ - "sha256:014f58110f53237ace6a408b5beb6c427b64e084eb451ef25a28308270086494", - "sha256:1bbcce1a551e262dfbafb6e6252f1ae36a248e615ca44ba302df077a846a8806", - "sha256:203e92a75716d8cfb491dc47c79e17d0d9207ccffcbcb35f598fbe463ae3444d", - "sha256:27e613d7077ac613e399270253259d9d53872aaf657471473ebfc9a52935c062", - "sha256:2bd51274dcd59f09dd952afb696bf9c61a7a49dfc764c04dd33ef7a6b502a1e2", - "sha256:38926c50cff6f533f8a2dae3d7f19541432610d114a70808f0926d5aaa7121e4", - "sha256:511f4273808ab590912a93ddb4e3914dfd8a388fed883361b02dea3791f292e1", - "sha256:58d4e9129985185a06d849aa6df265bdd5a74ca6e1b736a77959b498e0505b85", - "sha256:5b43d1ea6b378b54a1dc99dd8a2b5be47658fe9a7ce0a58ff0b55f4b43ef2b84", - "sha256:61ec41068b7b74268fa86e3e9e12b9f0c21fcf65434571dbb13d954bceb08042", - "sha256:666ae11966643886c2987b3b721899d250855718d6d9ce41b521252a17985f4d", - "sha256:68aaecc4178e90719e95298515979814bda0cbada1256a4485414860bd7ab962", - "sha256:7c05650fe8023c5ed0d46793d4b7d7e6cd9c04e68eabe5b0aeea836e37bdcec2", - "sha256:80eda8b3e173f0f247f711eef62be51b599b5d425c429b5d4ca6a05e9e856baa", - "sha256:8385d98f6a3bf8bb2d65a73e17ed87a3ba84f6991c155691c51112075f9ffc5d", - "sha256:88cce104c36870d70c49c7c8fd22885875d950d9ee6ab54df2745f83ba0dc365", - "sha256:9d3cdb25fa98afdd3d0892d132b8d7139e2c087da1712041f6b762e4f807cc96", - "sha256:a575913fb06e05e6b4b814d7f7468c2c660e8bb16d8d5a1faf9b33ccc569dd47", - "sha256:ac119bb76b9faa00f48128b7f5679e1d8d437365c5d26f1c2c3f0da4ce1b553d", - "sha256:c1332724be35d23a854994ff0b66530119500b6053d0bd3363265f7e5e77288d", - "sha256:d03a475165f3134f773d1388aeb19c2d25ba88b6a9733c5c590b9ff7bbfa2e0c", - "sha256:d75601ad10b059ec832e78823b348bfa1a59f6b8d545db3a24fd44362a1564cb", - "sha256:de41fd81a41e53267cb020bb3a7212861da53a7d39f863585d13ea11049cf277", - "sha256:e710bf40870f4db63c3d7d929aa9e09e4e7ee219e703f949ec4073b4294f6172", - "sha256:ea25acb556320250756e53f9e20a4177515f012c9eaea17eb7587a8c4d8ae034", - "sha256:f98bf604c82c416bc829e490c700ca1553eafdf2912a91e23a79d97d9801372a", - "sha256:fba1007b3ef89946dbbb515aeeb41e30203b004f0b4b00e5e16078b518563289" - ], - "markers": "python_version >= '3.7'", - "version": "==43.0.1" + "markers": "python_version >= '3.10'", + "version": "==1.3.1" }, "cycler": { "hashes": [ @@ -543,104 +455,13 @@ "markers": "python_version >= '3.8'", "version": "==0.12.1" }, - "daff": { - "hashes": [ - "sha256:22d0da9fd6a3275b54c926a9c97b180f9258aad65113ea18f3fec52cbadcd818" - ], - "version": "==1.3.46" - }, - "data-diff": { - "extras": [ - "postgresql" - ], - "hashes": [ - "sha256:4a6d34a5017098673d65dd926335fe602d25ec9159976ead207ebb17528768d0", - "sha256:4db93b763ad87e1442c8a8575c52b85cc4c5a752672d4a791d5d6c14ccfc3de5" - ], - "markers": "python_version < '4.0' and python_full_version >= '3.8.0'", - "version": "==0.11.2" - }, - "dbt-adapters": { - "hashes": [ - "sha256:ad3392794ed0504e2082e19b3e447701982af1ab28b91f829bb3feb986bd1b29", - "sha256:f192294112d5722c6a0981a104f7a9f57548aeeefe31b0b9d5708493f74a09f5" - ], - "markers": "python_full_version >= '3.8.0'", - "version": "==1.7.0" - }, - "dbt-common": { - "hashes": [ - "sha256:06e6fe9c6f7e13a6f1b513f98f7b1d093f1fb9c02cdbebb3b8904ec6746cc8cf", - "sha256:cfd9f46e9de4f9c2c95ec70210d1be53ac41e279038d18a7928cbbfd3f9b67af" - ], - "markers": "python_version >= '3.8'", - "version": "==1.10.0" - }, - "dbt-core": { - "hashes": [ - "sha256:0786999515f6eb704a0b2337f7d1846fee54eaf74add71e7a1c5f83778fc224f", - "sha256:3dc6a5c994cb58a2448e092bf80c071ae2bfa8e3ecdde8fa4bcd45b78c41695d" - ], - "markers": "python_version >= '3.8'", - "version": "==1.8.7" - }, - "dbt-extractor": { - "hashes": [ - "sha256:100453ba06e169cbdb118234ab3f06f6722a2e0e316089b81c88dea701212abc", - "sha256:1b25fa7a276ab26aa2d70ff6e0cf4cfb1490d7831fb57ee1337c24d2b0333b84", - "sha256:3614ce9f83ae4cd0dc95f77730034a793a1c090a52dcf698ba1c94050afe3a8b", - "sha256:3b91e6106b967d908b34f83929d3f50ee2b498876a1be9c055fe060ed728c556", - "sha256:475e2c05b17eb4976eff6c8f7635be42bec33f15a74ceb87a40242c94a99cebf", - "sha256:62e4f040fd338b652683421ce48e903812e27fd6e7af58b1b70a4e1f9f2c79e3", - "sha256:6916aae085fd5f2af069fd6947933e78b742c9e3d2165e1740c2e28ae543309a", - "sha256:91e25ad78f1f4feadd27587ebbcc46ad909cfad843118908f30336d08d8400ca", - "sha256:c0ce901d4ebf0664977e4e1cbf596d4afc6c1339fcc7d2cf67ce3481566a626f", - "sha256:c5651e458be910ff567c0da3ea2eb084fd01884cc88888ac2cf1e240dcddacc2", - "sha256:cbe338b76e9ffaa18275456e041af56c21bb517f6fbda7a58308138703da0996", - "sha256:cd5d95576a8dea4190240aaf9936a37fd74b4b7913ca69a3c368fc4472bb7e13", - "sha256:cdf9938b36cd098bcdd80f43dc03864da3f69f57d903a9160a32236540d4ddcd", - "sha256:d3b9bf50eb062b4344d9546fe42038996c6e7e7daa10724aa955d64717260e5d", - "sha256:ea4edf33035d0a060b1e01c42fb2d99316457d44c954d6ed4eed9f1948664d87", - "sha256:eecc08f3743e802a8ede60c89f7b2bce872acc86120cbc0ae7df229bb8a95083" - ], - "markers": "python_full_version >= '3.6.1'", - "version": "==0.5.1" - }, - "dbt-semantic-interfaces": { + "debugpy": { "hashes": [ - "sha256:3a497abef1ba8112affdf804b26bfdcd5468ed95cc924b509068e18d371c7c4d", - "sha256:b95ff3a6721dc30f6278cb84933d95e0ef27766e67eeb6bb41906242e77f7c9b" + "sha256:97aa00af95983887806e06f37e144909d35215d66db74f8b0e9799b4eef40cfd", + "sha256:ee4ed903cbeb14ee1839549f953af519ffa512598ec987b2051f9c868e2249a8" ], "markers": "python_version >= '3.8'", - "version": "==0.5.1" - }, - "debugpy": { - "hashes": [ - "sha256:0a85707c6a84b0c5b3db92a2df685b5230dd8fb8c108298ba4f11dba157a615a", - "sha256:22140bc02c66cda6053b6eb56dfe01bbe22a4447846581ba1dd6df2c9f97982d", - "sha256:30f467c5345d9dfdcc0afdb10e018e47f092e383447500f125b4e013236bf14b", - "sha256:3358aa619a073b620cd0d51d8a6176590af24abcc3fe2e479929a154bf591b51", - "sha256:43996632bee7435583952155c06881074b9a742a86cee74e701d87ca532fe833", - "sha256:538c6cdcdcdad310bbefd96d7850be1cd46e703079cc9e67d42a9ca776cdc8a8", - "sha256:567419081ff67da766c898ccf21e79f1adad0e321381b0dfc7a9c8f7a9347972", - "sha256:5d73d8c52614432f4215d0fe79a7e595d0dd162b5c15233762565be2f014803b", - "sha256:67479a94cf5fd2c2d88f9615e087fcb4fec169ec780464a3f2ba4a9a2bb79955", - "sha256:9fb8653f6cbf1dd0a305ac1aa66ec246002145074ea57933978346ea5afdf70b", - "sha256:b48892df4d810eff21d3ef37274f4c60d32cdcafc462ad5647239036b0f0649f", - "sha256:c1cef65cffbc96e7b392d9178dbfd524ab0750da6c0023c027ddcac968fd1caa", - "sha256:c931a9371a86784cee25dec8d65bc2dc7a21f3f1552e3833d9ef8f919d22280a", - "sha256:c9834dfd701a1f6bf0f7f0b8b1573970ae99ebbeee68314116e0ccc5c78eea3c", - "sha256:cdaf0b9691879da2d13fa39b61c01887c34558d1ff6e5c30e2eb698f5384cd43", - "sha256:db891b141fc6ee4b5fc6d1cc8035ec329cabc64bdd2ae672b4550c87d4ecb128", - "sha256:df5dc9eb4ca050273b8e374a4cd967c43be1327eeb42bfe2f58b3cdfe7c68dcb", - "sha256:e3a82da039cfe717b6fb1886cbbe5c4a3f15d7df4765af857f4307585121c2dd", - "sha256:e3e182cd98eac20ee23a00653503315085b29ab44ed66269482349d307b08df9", - "sha256:e4ce0570aa4aca87137890d23b86faeadf184924ad892d20c54237bcaab75d8f", - "sha256:f1e60bd06bb3cc5c0e957df748d1fab501e01416c43a7bdc756d2a992ea1b881", - "sha256:f7158252803d0752ed5398d291dee4c553bb12d14547c0e1843ab74ee9c31123" - ], - "markers": "python_version >= '3.8'", - "version": "==1.8.6" + "version": "==1.8.10" }, "decorator": { "hashes": [ @@ -650,14 +471,6 @@ "markers": "python_version >= '3.5'", "version": "==5.1.1" }, - "deepdiff": { - "hashes": [ - "sha256:260c16f052d4badbf60351b4f77e8390bee03a0b516246f6839bc813fb429ddf", - "sha256:447760081918216aa4fd4ca78a4b6a848b81307b2ea94c810255334b759e1dc3" - ], - "markers": "python_version >= '3.8'", - "version": "==7.0.1" - }, "defusedxml": { "hashes": [ "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69", @@ -666,20 +479,6 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "version": "==0.7.1" }, - "dill": { - "hashes": [ - "sha256:468dff3b89520b474c0397703366b7b95eebe6303f108adf9b19da1f702be87a", - "sha256:81aa267dddf68cbfe8029c42ca9ec6a4ab3b22371d1c450abc54422577b4512c" - ], - "markers": "python_version >= '3.11'", - "version": "==0.3.9" - }, - "dsnparse": { - "hashes": [ - "sha256:2ac5705b17cb28e8b115053c2d51cf3321dc2041b1d75e2db6157e05146d0fba" - ], - "version": "==0.1.15" - }, "esridump": { "hashes": [ "sha256:3ea0b856da1a55b327375fb1eec24d08ec2a5eeda8700a9e696159252876dca7", @@ -698,10 +497,10 @@ }, "fastjsonschema": { "hashes": [ - "sha256:3d48fc5300ee96f5d116f10fe6f28d938e6008f59a6a025c2649475b87f76a23", - "sha256:5875f0b0fa7a0043a91e93a9b8f793bcbbba9691e7fd83dca95c28ba26d21f0a" + "sha256:794d4f0a58f848961ba16af7b9c85a3e88cd360df008c59aac6fc5ae9323b5d4", + "sha256:c9e5b7e908310918cf494a434eeb31384dd84a98b57a30bcb1f535015b554667" ], - "version": "==2.20.0" + "version": "==2.21.1" }, "fiona": { "hashes": [ @@ -737,63 +536,66 @@ }, "fonttools": { "hashes": [ - "sha256:07e005dc454eee1cc60105d6a29593459a06321c21897f769a281ff2d08939f6", - "sha256:0a911591200114969befa7f2cb74ac148bce5a91df5645443371aba6d222e263", - "sha256:0d1d353ef198c422515a3e974a1e8d5b304cd54a4c2eebcae708e37cd9eeffb1", - "sha256:0e88e3018ac809b9662615072dcd6b84dca4c2d991c6d66e1970a112503bba7e", - "sha256:1d152d1be65652fc65e695e5619e0aa0982295a95a9b29b52b85775243c06556", - "sha256:262705b1663f18c04250bd1242b0515d3bbae177bee7752be67c979b7d47f43d", - "sha256:278913a168f90d53378c20c23b80f4e599dca62fbffae4cc620c8eed476b723e", - "sha256:301540e89cf4ce89d462eb23a89464fef50915255ece765d10eee8b2bf9d75b2", - "sha256:31c32d7d4b0958600eac75eaf524b7b7cb68d3a8c196635252b7a2c30d80e986", - "sha256:357cacb988a18aace66e5e55fe1247f2ee706e01debc4b1a20d77400354cddeb", - "sha256:37cddd62d83dc4f72f7c3f3c2bcf2697e89a30efb152079896544a93907733bd", - "sha256:41bb0b250c8132b2fcac148e2e9198e62ff06f3cc472065dff839327945c5882", - "sha256:4aa4817f0031206e637d1e685251ac61be64d1adef111060df84fdcbc6ab6c44", - "sha256:4e10d2e0a12e18f4e2dd031e1bf7c3d7017be5c8dbe524d07706179f355c5dac", - "sha256:5419771b64248484299fa77689d4f3aeed643ea6630b2ea750eeab219588ba20", - "sha256:54471032f7cb5fca694b5f1a0aaeba4af6e10ae989df408e0216f7fd6cdc405d", - "sha256:58974b4987b2a71ee08ade1e7f47f410c367cdfc5a94fabd599c88165f56213a", - "sha256:58d29b9a294573d8319f16f2f79e42428ba9b6480442fa1836e4eb89c4d9d61c", - "sha256:5eb2474a7c5be8a5331146758debb2669bf5635c021aee00fd7c353558fc659d", - "sha256:6e37561751b017cf5c40fce0d90fd9e8274716de327ec4ffb0df957160be3bff", - "sha256:76ae5091547e74e7efecc3cbf8e75200bc92daaeb88e5433c5e3e95ea8ce5aa7", - "sha256:7965af9b67dd546e52afcf2e38641b5be956d68c425bef2158e95af11d229f10", - "sha256:7e3b7d44e18c085fd8c16dcc6f1ad6c61b71ff463636fcb13df7b1b818bd0c02", - "sha256:7ed7ee041ff7b34cc62f07545e55e1468808691dddfd315d51dd82a6b37ddef2", - "sha256:82834962b3d7c5ca98cb56001c33cf20eb110ecf442725dc5fdf36d16ed1ab07", - "sha256:8583e563df41fdecef31b793b4dd3af8a9caa03397be648945ad32717a92885b", - "sha256:8fa92cb248e573daab8d032919623cc309c005086d743afb014c836636166f08", - "sha256:93d458c8a6a354dc8b48fc78d66d2a8a90b941f7fec30e94c7ad9982b1fa6bab", - "sha256:957f669d4922f92c171ba01bef7f29410668db09f6c02111e22b2bce446f3285", - "sha256:9dc080e5a1c3b2656caff2ac2633d009b3a9ff7b5e93d0452f40cd76d3da3b3c", - "sha256:9ef1b167e22709b46bf8168368b7b5d3efeaaa746c6d39661c1b4405b6352e58", - "sha256:a7a310c6e0471602fe3bf8efaf193d396ea561486aeaa7adc1f132e02d30c4b9", - "sha256:ab774fa225238986218a463f3fe151e04d8c25d7de09df7f0f5fce27b1243dbc", - "sha256:ada215fd079e23e060157aab12eba0d66704316547f334eee9ff26f8c0d7b8ab", - "sha256:c39287f5c8f4a0c5a55daf9eaf9ccd223ea59eed3f6d467133cc727d7b943a55", - "sha256:c9c563351ddc230725c4bdf7d9e1e92cbe6ae8553942bd1fb2b2ff0884e8b714", - "sha256:d26732ae002cc3d2ecab04897bb02ae3f11f06dd7575d1df46acd2f7c012a8d8", - "sha256:d3b659d1029946f4ff9b6183984578041b520ce0f8fb7078bb37ec7445806b33", - "sha256:dd9cc95b8d6e27d01e1e1f1fae8559ef3c02c76317da650a19047f249acd519d", - "sha256:e4564cf40cebcb53f3dc825e85910bf54835e8a8b6880d59e5159f0f325e637e", - "sha256:e7d82b9e56716ed32574ee106cabca80992e6bbdcf25a88d97d21f73a0aae664", - "sha256:e8a4b261c1ef91e7188a30571be6ad98d1c6d9fa2427244c545e2fa0a2494dd7", - "sha256:e96bc94c8cda58f577277d4a71f51c8e2129b8b36fd05adece6320dd3d57de8a", - "sha256:ed2f80ca07025551636c555dec2b755dd005e2ea8fbeb99fc5cdff319b70b23b", - "sha256:f5b8a096e649768c2f4233f947cf9737f8dbf8728b90e2771e2497c6e3d21d13", - "sha256:f8e953cc0bddc2beaf3a3c3b5dd9ab7554677da72dfaf46951e193c9653e515a", - "sha256:fda582236fee135d4daeca056c8c88ec5f6f6d88a004a79b84a02547c8f57386", - "sha256:fdb062893fd6d47b527d39346e0c5578b7957dcea6d6a3b6794569370013d9ac" - ], - "markers": "python_version >= '3.8'", - "version": "==4.54.1" + "sha256:07f8288aacf0a38d174445fc78377a97fb0b83cfe352a90c9d9c1400571963c7", + "sha256:11e5de1ee0d95af4ae23c1a138b184b7f06e0b6abacabf1d0db41c90b03d834b", + "sha256:1bc7ad24ff98846282eef1cbeac05d013c2154f977a79886bb943015d2b1b261", + "sha256:1dcc07934a2165ccdc3a5a608db56fb3c24b609658a5b340aee4ecf3ba679dc0", + "sha256:22f38464daa6cdb7b6aebd14ab06609328fe1e9705bb0fcc7d1e69de7109ee02", + "sha256:27e4ae3592e62eba83cd2c4ccd9462dcfa603ff78e09110680a5444c6925d841", + "sha256:3983313c2a04d6cc1fe9251f8fc647754cf49a61dac6cb1e7249ae67afaafc45", + "sha256:529cef2ce91dc44f8e407cc567fae6e49a1786f2fefefa73a294704c415322a4", + "sha256:5323a22eabddf4b24f66d26894f1229261021dacd9d29e89f7872dd8c63f0b8b", + "sha256:54153c49913f45065c8d9e6d0c101396725c5621c8aee744719300f79771d75a", + "sha256:546565028e244a701f73df6d8dd6be489d01617863ec0c6a42fa25bf45d43048", + "sha256:5480673f599ad410695ca2ddef2dfefe9df779a9a5cda89503881e503c9c7d90", + "sha256:5e8d657cd7326eeaba27de2740e847c6b39dde2f8d7cd7cc56f6aad404ddf0bd", + "sha256:62d65a3022c35e404d19ca14f291c89cc5890032ff04f6c17af0bd1927299674", + "sha256:6314bf82c54c53c71805318fcf6786d986461622dd926d92a465199ff54b1b72", + "sha256:7a8aa2c5e5b8b3bcb2e4538d929f6589a5c6bdb84fd16e2ed92649fb5454f11c", + "sha256:827e95fdbbd3e51f8b459af5ea10ecb4e30af50221ca103bea68218e9615de07", + "sha256:859c358ebf41db18fb72342d3080bce67c02b39e86b9fbcf1610cca14984841b", + "sha256:86721fbc389ef5cc1e2f477019e5069e8e4421e8d9576e9c26f840dbb04678de", + "sha256:89bdc5d88bdeec1b15af790810e267e8332d92561dce4f0748c2b95c9bdf3926", + "sha256:8c4491699bad88efe95772543cd49870cf756b019ad56294f6498982408ab03e", + "sha256:8c5ec45428edaa7022f1c949a632a6f298edc7b481312fc7dc258921e9399628", + "sha256:8e75f12c82127486fac2d8bfbf5bf058202f54bf4f158d367e41647b972342ca", + "sha256:a430178ad3e650e695167cb53242dae3477b35c95bef6525b074d87493c4bf29", + "sha256:a8c2794ded89399cc2169c4d0bf7941247b8d5932b2659e09834adfbb01589aa", + "sha256:aca318b77f23523309eec4475d1fbbb00a6b133eb766a8bdc401faba91261abe", + "sha256:ae3b6600565b2d80b7c05acb8e24d2b26ac407b27a3f2e078229721ba5698427", + "sha256:aedbeb1db64496d098e6be92b2e63b5fac4e53b1b92032dfc6988e1ea9134a4d", + "sha256:aee3b57643827e237ff6ec6d28d9ff9766bd8b21e08cd13bff479e13d4b14765", + "sha256:b54baf65c52952db65df39fcd4820668d0ef4766c0ccdf32879b77f7c804d5c5", + "sha256:b586ab5b15b6097f2fb71cafa3c98edfd0dba1ad8027229e7b1e204a58b0e09d", + "sha256:b8d5e8916c0970fbc0f6f1bece0063363bb5857a7f170121a4493e31c3db3314", + "sha256:bc5dbb4685e51235ef487e4bd501ddfc49be5aede5e40f4cefcccabc6e60fb4b", + "sha256:bdcc9f04b36c6c20978d3f060e5323a43f6222accc4e7fcbef3f428e216d96af", + "sha256:c3ca99e0d460eff46e033cd3992a969658c3169ffcd533e0a39c63a38beb6831", + "sha256:caf8230f3e10f8f5d7593eb6d252a37caf58c480b19a17e250a63dad63834cf3", + "sha256:cd70de1a52a8ee2d1877b6293af8a2484ac82514f10b1c67c1c5762d38073e56", + "sha256:cf4fe7c124aa3f4e4c1940880156e13f2f4d98170d35c749e6b4f119a872551e", + "sha256:d342e88764fb201286d185093781bf6628bbe380a913c24adf772d901baa8276", + "sha256:da9da6d65cd7aa6b0f806556f4985bcbf603bf0c5c590e61b43aa3e5a0f822d0", + "sha256:dc5294a3d5c84226e3dbba1b6f61d7ad813a8c0238fceea4e09aa04848c3d851", + "sha256:dd68c87a2bfe37c5b33bcda0fba39b65a353876d3b9006fde3adae31f97b3ef5", + "sha256:e6e8766eeeb2de759e862004aa11a9ea3d6f6d5ec710551a88b476192b64fd54", + "sha256:e894b5bd60d9f473bed7a8f506515549cc194de08064d829464088d23097331b", + "sha256:eb6ca911c4c17eb51853143624d8dc87cdcdf12a711fc38bf5bd21521e79715f", + "sha256:ed63959d00b61959b035c7d47f9313c2c1ece090ff63afea702fe86de00dbed4", + "sha256:f412604ccbeee81b091b420272841e5ec5ef68967a9790e80bffd0e30b8e2977", + "sha256:f7d66c15ba875432a2d2fb419523f5d3d347f91f48f57b8b08a2dfc3c39b8a3f", + "sha256:f9e736f60f4911061235603a6119e72053073a12c6d7904011df2d8fad2c0e35", + "sha256:fb594b5a99943042c702c550d5494bdd7577f6ef19b0bc73877c948a63184a32" + ], + "markers": "python_version >= '3.8'", + "version": "==4.55.3" }, "fqdn": { "hashes": [ "sha256:105ed3677e767fb5ca086a0c1f4bb66ebc3c100be518f0e0d755d9eae164d89f", "sha256:3a179af3761e4df6eb2e026ff9e1a3033d3587bf980a0b1b2e1e5d08d7358014" ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", "version": "==1.5.1" }, "future": { @@ -807,12 +609,12 @@ }, "geoalchemy2": { "hashes": [ - "sha256:3af0272db927373e74ee3b064cdc9464ba08defdb945c51745db1b841482f5dc", - "sha256:546455dc39f5bcdfc5b871e57d3f7546c8a6f798eb364c474200f488ace6fd32" + "sha256:b0f27d5500ee757af4654c6262e0f834b7a843504d193653ec747ef1128d2ab5", + "sha256:df64bb72af70daafaac3f359492c96501c37ab85ed20f9510c99cc6d02881100" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==0.15.2" + "version": "==0.16.0" }, "geopandas": { "hashes": [ @@ -825,19 +627,19 @@ }, "google-api-core": { "hashes": [ - "sha256:ef0591ef03c30bb83f79b3d0575c3f31219001fc9c5cf37024d08310aeffed8a", - "sha256:f74dff1889ba291a4b76c5079df0711810e2d9da81abfdc99957bc961c1eb28f" + "sha256:10d82ac0fca69c82a25b3efdeefccf6f28e02ebb97925a8cce8edbfe379929d9", + "sha256:e255640547a597a4da010876d333208ddac417d60add22b6851a0c66a831fcaf" ], "markers": "python_version >= '3.7'", - "version": "==2.20.0" + "version": "==2.24.0" }, "google-auth": { "hashes": [ - "sha256:25df55f327ef021de8be50bad0dfd4a916ad0de96da86cd05661c9297723ad3f", - "sha256:f4c64ed4e01e8e8b646ef34c018f8bf3338df0c8e37d8b3bba40e7f574a3278a" + "sha256:51a15d47028b66fd36e5c64a82d2d57480075bccc7da37cde257fc94177a61fb", + "sha256:545e9618f2df0bcbb7dcbc45a546485b1212624716975a1ea5ae8149ce769ab1" ], "markers": "python_version >= '3.7'", - "version": "==2.35.0" + "version": "==2.36.0" }, "google-cloud-core": { "hashes": [ @@ -849,12 +651,12 @@ }, "google-cloud-storage": { "hashes": [ - "sha256:97a4d45c368b7d401ed48c4fdfe86e1e1cb96401c9e199e419d289e2c0370166", - "sha256:aaf7acd70cdad9f274d29332673fcab98708d0e1f4dceb5a5356aaef06af4d99" + "sha256:aeb971b5c29cf8ab98445082cbfe7b161a1f48ed275822f59ed3f1524ea54fba", + "sha256:cd05e9e7191ba6cb68934d8eb76054d9be4562aa89dbc4236feee4d7d51342b2" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==2.18.2" + "version": "==2.19.0" }, "google-crc32c": { "hashes": [ @@ -899,11 +701,11 @@ }, "googleapis-common-protos": { "hashes": [ - "sha256:2972e6c496f435b92590fd54045060867f3fe9be2c82ab148fc8885035479a63", - "sha256:334a29d07cddc3aa01dee4988f9afd9b2916ee2ff49d6b757155dc0d197852c0" + "sha256:c3e7b33d15fdca5374cc0a7346dd92ffa847425cc4ea941d970f13680052ec8c", + "sha256:d7abcd75fabb2e0ec9f74466401f6c119a0b498e27370e9be4c94cb7e382b8ed" ], "markers": "python_version >= '3.7'", - "version": "==1.65.0" + "version": "==1.66.0" }, "greenlet": { "hashes": [ @@ -994,19 +796,19 @@ }, "httpcore": { "hashes": [ - "sha256:34a38e2f9291467ee3b44e89dd52615370e152954ba21721378a87b2960f7a61", - "sha256:421f18bac248b25d310f3cacd198d55b8e6125c107797b609ff9b7a6ba7991b5" + "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c", + "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd" ], "markers": "python_version >= '3.8'", - "version": "==1.0.5" + "version": "==1.0.7" }, "httpx": { "hashes": [ - "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0", - "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2" + "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", + "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad" ], "markers": "python_version >= '3.8'", - "version": "==0.27.2" + "version": "==0.28.1" }, "idna": { "hashes": [ @@ -1016,22 +818,6 @@ "markers": "python_version >= '3.6'", "version": "==3.10" }, - "importlib-metadata": { - "hashes": [ - "sha256:1231cf92d825c9e03cfc4da076a16de6422c863558229ea0b22b675657463443", - "sha256:f0afba6205ad8f8947c7d338b5342d5db2afbfd82f9cbef7879a9539cc12eb9b" - ], - "markers": "python_version < '3.12'", - "version": "==6.11.0" - }, - "iniconfig": { - "hashes": [ - "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", - "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374" - ], - "markers": "python_version >= '3.7'", - "version": "==2.0.0" - }, "ipykernel": { "hashes": [ "sha256:afdb66ba5aa354b09b91379bac28ae4afebbb30e8b39510c9690afb7a10421b5", @@ -1042,11 +828,11 @@ }, "ipython": { "hashes": [ - "sha256:0b99a2dc9f15fd68692e898e5568725c6d49c527d36a9fb5960ffbdeaa82ff7e", - "sha256:f68b3cb8bde357a5d7adc9598d57e22a45dfbea19eb6b98286fa3b288c9cd55c" + "sha256:85ec56a7e20f6c38fce7727dcca699ae4ffc85985aa7b23635a8008f918ae321", + "sha256:cb0a405a306d2995a5cbb9901894d240784a9f341394c6ba3f4fe8c6eb89ff6e" ], "markers": "python_version >= '3.10'", - "version": "==8.27.0" + "version": "==8.30.0" }, "ipywidgets": { "hashes": [ @@ -1064,67 +850,21 @@ "markers": "python_version >= '3.6'", "version": "==2.1.1" }, - "isodate": { - "hashes": [ - "sha256:0751eece944162659049d35f4f549ed815792b38793f07cf73381c1c87cbed96", - "sha256:48c5881de7e8b0a0d648cb024c8062dc84e7b840ed81e864c7614fd3c127bde9" - ], - "version": "==0.6.1" - }, "isoduration": { "hashes": [ "sha256:ac2f9015137935279eac671f94f89eb00584f940f5dc49462a0c4ee692ba1bd9", "sha256:b2904c2a4228c3d44f409c8ae8e2370eb21a26f7ac2ec5446df141dde3452042" ], + "markers": "python_version >= '3.7'", "version": "==20.11.0" }, - "isort": { - "hashes": [ - "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109", - "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6" - ], - "markers": "python_full_version >= '3.8.0'", - "version": "==5.13.2" - }, - "jaraco.classes": { - "hashes": [ - "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", - "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790" - ], - "markers": "python_version >= '3.8'", - "version": "==3.4.0" - }, - "jaraco.context": { - "hashes": [ - "sha256:9bae4ea555cf0b14938dc0aee7c9f32ed303aa20a3b73e7dc80111628792d1b3", - "sha256:f797fc481b490edb305122c9181830a3a5b76d84ef6d1aef2fb9b47ab956f9e4" - ], - "markers": "python_version >= '3.8'", - "version": "==6.0.1" - }, - "jaraco.functools": { - "hashes": [ - "sha256:70f7e0e2ae076498e212562325e805204fc092d7b4c17e0e86c959e249701a9d", - "sha256:ad159f13428bc4acbf5541ad6dec511f91573b90fba04df61dafa2a1231cf649" - ], - "markers": "python_version >= '3.8'", - "version": "==4.1.0" - }, "jedi": { "hashes": [ - "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd", - "sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0" + "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", + "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9" ], "markers": "python_version >= '3.6'", - "version": "==0.19.1" - }, - "jeepney": { - "hashes": [ - "sha256:5efe48d255973902f6badc3ce55e2aa6c5c3b3bc642059ef3a91247bcfcc5806", - "sha256:c0a454ad016ca575060802ee4d590dd912e35c122fa04e70306de3d076cce755" - ], - "markers": "sys_platform == 'linux'", - "version": "==0.8.0" + "version": "==0.19.2" }, "jenkspy": { "hashes": [ @@ -1183,17 +923,18 @@ }, "json5": { "hashes": [ - "sha256:34ed7d834b1341a86987ed52f3f76cd8ee184394906b6e22a1e0deb9ab294e8f", - "sha256:548e41b9be043f9426776f05df8635a00fe06104ea51ed24b67f908856e151ae" + "sha256:19b23410220a7271e8377f81ba8aacba2fdd56947fbb137ee5977cbe1f5e8dfa", + "sha256:e66941c8f0a02026943c52c2eb34ebeb2a6f819a0be05920a6f5243cd30fd559" ], - "markers": "python_version >= '3.8'", - "version": "==0.9.25" + "markers": "python_full_version >= '3.8.0'", + "version": "==0.10.0" }, "jsonpointer": { "hashes": [ "sha256:13e088adc14fca8b6aa8177c044e12701e6ad4b28ff10e65f2267a90109c9942", "sha256:2b2d729f2091522d61c3b31f82e11870f60b68f43fbc705cb76bf4b832af59ef" ], + "markers": "python_version >= '3.7'", "version": "==3.0.0" }, "jsonschema": { @@ -1209,11 +950,11 @@ }, "jsonschema-specifications": { "hashes": [ - "sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc", - "sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c" + "sha256:0f38b83639958ce1152d02a7f062902c41c8fd20d558b0c34344292d417ae272", + "sha256:a09a0680616357d9a0ecf05c12ad234479f549239d0f5b55f3deea67475da9bf" ], - "markers": "python_version >= '3.8'", - "version": "==2023.12.1" + "markers": "python_version >= '3.9'", + "version": "==2024.10.1" }, "jupyter": { "hashes": [ @@ -1281,11 +1022,11 @@ }, "jupyterlab": { "hashes": [ - "sha256:73b6e0775d41a9fee7ee756c80f58a6bed4040869ccc21411dc559818874d321", - "sha256:ae7f3a1b8cb88b4f55009ce79fa7c06f99d70cd63601ee4aa91815d054f46f75" + "sha256:32a8fd30677e734ffcc3916a4758b9dab21b02015b668c60eb36f84357b7d4b1", + "sha256:76fa39e548fdac94dc1204af5956c556f54c785f70ee26aa47ea08eda4d5bbcd" ], "markers": "python_version >= '3.8'", - "version": "==4.2.5" + "version": "==4.3.3" }, "jupyterlab-pygments": { "hashes": [ @@ -1311,14 +1052,6 @@ "markers": "python_version >= '3.7'", "version": "==3.0.13" }, - "keyring": { - "hashes": [ - "sha256:5426f817cf7f6f007ba5ec722b1bcad95a75b27d780343772ad76b17cb47b0bf", - "sha256:b07ebc55f3e8ed86ac81dd31ef14e81ace9dd9c3d4b5d77a6e9a2016d0d71a1b" - ], - "markers": "python_version >= '3.8'", - "version": "==25.4.1" - }, "kiwisolver": { "hashes": [ "sha256:073a36c8273647592ea332e816e75ef8da5c303236ec0167196793eb1e34657a", @@ -1439,13 +1172,6 @@ "markers": "python_version >= '3.8'", "version": "==1.4.7" }, - "leather": { - "hashes": [ - "sha256:18290bc93749ae39039af5e31e871fcfad74d26c4c3ea28ea4f681f4571b3a2b", - "sha256:f964bec2086f3153a6c16e707f20cb718f811f57af116075f4c0f4805c608b95" - ], - "version": "==0.4.0" - }, "libpysal": { "hashes": [ "sha256:ce89d3c9aa944a7df052545ae37a5c802d707c672e04a76f7b1ee93f781110a9", @@ -1455,27 +1181,6 @@ "markers": "python_version >= '3.10'", "version": "==4.12.1" }, - "logbook": { - "hashes": [ - "sha256:0cf2cdbfb65a03b5987d19109dacad13417809dcf697f66e1a7084fb21744ea9", - "sha256:2dc85f1510533fddb481e97677bb7bca913560862734c0b3b289bfed04f78c92", - "sha256:56ee54c11df3377314cedcd6507638f015b4b88c0238c2e01b5eb44fd3a6ad1b", - "sha256:66f454ada0f56eae43066f604a222b09893f98c1adc18df169710761b8f32fe8", - "sha256:7c533eb728b3d220b1b5414ba4635292d149d79f74f6973b4aa744c850ca944a", - "sha256:8f76a2e7b1f72595f753228732f81ce342caf03babc3fed6bbdcf366f2f20f18", - "sha256:94e2e11ff3c2304b0d09a36c6208e5ae756eb948b210e5cbd63cd8d27f911542", - "sha256:97fee1bd9605f76335b169430ed65e15e457a844b2121bd1d90a08cf7e30aba0", - "sha256:e18f7422214b1cf0240c56f884fd9c9b4ff9d0da2eabca9abccba56df7222f66" - ], - "version": "==1.5.3" - }, - "mando": { - "hashes": [ - "sha256:18baa999b4b613faefb00eac4efadcf14f510b59b924b66e08289aa1de8c3500", - "sha256:26ef1d70928b6057ee3ca12583d73c63e05c49de8972d620c278a7b206581a8a" - ], - "version": "==0.7.1" - }, "mapbox": { "hashes": [ "sha256:13c0774d8e4a4de07c224db00f04f6da21a40c1df7febda1791444d95c3d3c2b", @@ -1493,137 +1198,120 @@ "markers": "python_version >= '3.9'", "version": "==2.8.1" }, - "markdown-it-py": { - "hashes": [ - "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", - "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb" - ], - "markers": "python_version >= '3.8'", - "version": "==3.0.0" - }, "markupsafe": { "hashes": [ - "sha256:00e046b6dd71aa03a41079792f8473dc494d564611a8f89bbbd7cb93295ebdcf", - "sha256:075202fa5b72c86ad32dc7d0b56024ebdbcf2048c0ba09f1cde31bfdd57bcfff", - "sha256:0e397ac966fdf721b2c528cf028494e86172b4feba51d65f81ffd65c63798f3f", - "sha256:17b950fccb810b3293638215058e432159d2b71005c74371d784862b7e4683f3", - "sha256:1f3fbcb7ef1f16e48246f704ab79d79da8a46891e2da03f8783a5b6fa41a9532", - "sha256:2174c595a0d73a3080ca3257b40096db99799265e1c27cc5a610743acd86d62f", - "sha256:2b7c57a4dfc4f16f7142221afe5ba4e093e09e728ca65c51f5620c9aaeb9a617", - "sha256:2d2d793e36e230fd32babe143b04cec8a8b3eb8a3122d2aceb4a371e6b09b8df", - "sha256:30b600cf0a7ac9234b2638fbc0fb6158ba5bdcdf46aeb631ead21248b9affbc4", - "sha256:397081c1a0bfb5124355710fe79478cdbeb39626492b15d399526ae53422b906", - "sha256:3a57fdd7ce31c7ff06cdfbf31dafa96cc533c21e443d57f5b1ecc6cdc668ec7f", - "sha256:3c6b973f22eb18a789b1460b4b91bf04ae3f0c4234a0a6aa6b0a92f6f7b951d4", - "sha256:3e53af139f8579a6d5f7b76549125f0d94d7e630761a2111bc431fd820e163b8", - "sha256:4096e9de5c6fdf43fb4f04c26fb114f61ef0bf2e5604b6ee3019d51b69e8c371", - "sha256:4275d846e41ecefa46e2015117a9f491e57a71ddd59bbead77e904dc02b1bed2", - "sha256:4c31f53cdae6ecfa91a77820e8b151dba54ab528ba65dfd235c80b086d68a465", - "sha256:4f11aa001c540f62c6166c7726f71f7573b52c68c31f014c25cc7901deea0b52", - "sha256:5049256f536511ee3f7e1b3f87d1d1209d327e818e6ae1365e8653d7e3abb6a6", - "sha256:58c98fee265677f63a4385256a6d7683ab1832f3ddd1e66fe948d5880c21a169", - "sha256:598e3276b64aff0e7b3451b72e94fa3c238d452e7ddcd893c3ab324717456bad", - "sha256:5b7b716f97b52c5a14bffdf688f971b2d5ef4029127f1ad7a513973cfd818df2", - "sha256:5dedb4db619ba5a2787a94d877bc8ffc0566f92a01c0ef214865e54ecc9ee5e0", - "sha256:619bc166c4f2de5caa5a633b8b7326fbe98e0ccbfacabd87268a2b15ff73a029", - "sha256:629ddd2ca402ae6dbedfceeba9c46d5f7b2a61d9749597d4307f943ef198fc1f", - "sha256:656f7526c69fac7f600bd1f400991cc282b417d17539a1b228617081106feb4a", - "sha256:6ec585f69cec0aa07d945b20805be741395e28ac1627333b1c5b0105962ffced", - "sha256:72b6be590cc35924b02c78ef34b467da4ba07e4e0f0454a2c5907f473fc50ce5", - "sha256:7502934a33b54030eaf1194c21c692a534196063db72176b0c4028e140f8f32c", - "sha256:7a68b554d356a91cce1236aa7682dc01df0edba8d043fd1ce607c49dd3c1edcf", - "sha256:7b2e5a267c855eea6b4283940daa6e88a285f5f2a67f2220203786dfa59b37e9", - "sha256:823b65d8706e32ad2df51ed89496147a42a2a6e01c13cfb6ffb8b1e92bc910bb", - "sha256:8590b4ae07a35970728874632fed7bd57b26b0102df2d2b233b6d9d82f6c62ad", - "sha256:8dd717634f5a044f860435c1d8c16a270ddf0ef8588d4887037c5028b859b0c3", - "sha256:8dec4936e9c3100156f8a2dc89c4b88d5c435175ff03413b443469c7c8c5f4d1", - "sha256:97cafb1f3cbcd3fd2b6fbfb99ae11cdb14deea0736fc2b0952ee177f2b813a46", - "sha256:a17a92de5231666cfbe003f0e4b9b3a7ae3afb1ec2845aadc2bacc93ff85febc", - "sha256:a549b9c31bec33820e885335b451286e2969a2d9e24879f83fe904a5ce59d70a", - "sha256:ac07bad82163452a6884fe8fa0963fb98c2346ba78d779ec06bd7a6262132aee", - "sha256:ae2ad8ae6ebee9d2d94b17fb62763125f3f374c25618198f40cbb8b525411900", - "sha256:b91c037585eba9095565a3556f611e3cbfaa42ca1e865f7b8015fe5c7336d5a5", - "sha256:bc1667f8b83f48511b94671e0e441401371dfd0f0a795c7daa4a3cd1dde55bea", - "sha256:bec0a414d016ac1a18862a519e54b2fd0fc8bbfd6890376898a6c0891dd82e9f", - "sha256:bf50cd79a75d181c9181df03572cdce0fbb75cc353bc350712073108cba98de5", - "sha256:bff1b4290a66b490a2f4719358c0cdcd9bafb6b8f061e45c7a2460866bf50c2e", - "sha256:c061bb86a71b42465156a3ee7bd58c8c2ceacdbeb95d05a99893e08b8467359a", - "sha256:c8b29db45f8fe46ad280a7294f5c3ec36dbac9491f2d1c17345be8e69cc5928f", - "sha256:ce409136744f6521e39fd8e2a24c53fa18ad67aa5bc7c2cf83645cce5b5c4e50", - "sha256:d050b3361367a06d752db6ead6e7edeb0009be66bc3bae0ee9d97fb326badc2a", - "sha256:d283d37a890ba4c1ae73ffadf8046435c76e7bc2247bbb63c00bd1a709c6544b", - "sha256:d9fad5155d72433c921b782e58892377c44bd6252b5af2f67f16b194987338a4", - "sha256:daa4ee5a243f0f20d528d939d06670a298dd39b1ad5f8a72a4275124a7819eff", - "sha256:db0b55e0f3cc0be60c1f19efdde9a637c32740486004f20d1cff53c3c0ece4d2", - "sha256:e61659ba32cf2cf1481e575d0462554625196a1f2fc06a1c777d3f48e8865d46", - "sha256:ea3d8a3d18833cf4304cd2fc9cbb1efe188ca9b5efef2bdac7adc20594a0e46b", - "sha256:ec6a563cff360b50eed26f13adc43e61bc0c04d94b8be985e6fb24b81f6dcfdf", - "sha256:f5dfb42c4604dddc8e4305050aa6deb084540643ed5804d7455b5df8fe16f5e5", - "sha256:fa173ec60341d6bb97a89f5ea19c85c5643c1e7dedebc22f5181eb73573142c5", - "sha256:fa9db3f79de01457b03d4f01b34cf91bc0048eb2c3846ff26f66687c2f6d16ab", - "sha256:fce659a462a1be54d2ffcacea5e3ba2d74daa74f30f5f143fe0c58636e355fdd", - "sha256:ffee1f21e5ef0d712f9033568f8344d5da8cc2869dbd08d87c84656e6a2d2f68" - ], - "markers": "python_version >= '3.7'", - "version": "==2.1.5" - }, - "mashumaro": { - "extras": [ - "msgpack" - ], - "hashes": [ - "sha256:0248a5c8574aa6cd20696621502d38a7ea66af3d6d93c5d03f93b33298edc878", - "sha256:d2c0fd5e7878987629d41f4986d9b0903d362a92eeb299b7d88b87eb113f4f48" + "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", + "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", + "sha256:1225beacc926f536dc82e45f8a4d68502949dc67eea90eab715dea3a21c1b5f0", + "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", + "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", + "sha256:1a9d3f5f0901fdec14d8d2f66ef7d035f2157240a433441719ac9a3fba440b13", + "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", + "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", + "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", + "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", + "sha256:3169b1eefae027567d1ce6ee7cae382c57fe26e82775f460f0b2778beaad66c0", + "sha256:3809ede931876f5b2ec92eef964286840ed3540dadf803dd570c3b7e13141a3b", + "sha256:38a9ef736c01fccdd6600705b09dc574584b89bea478200c5fbf112a6b0d5579", + "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", + "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", + "sha256:48032821bbdf20f5799ff537c7ac3d1fba0ba032cfc06194faffa8cda8b560ff", + "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", + "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", + "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", + "sha256:57cb5a3cf367aeb1d316576250f65edec5bb3be939e9247ae594b4bcbc317dfb", + "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", + "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", + "sha256:6af100e168aa82a50e186c82875a5893c5597a0c1ccdb0d8b40240b1f28b969a", + "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", + "sha256:6e296a513ca3d94054c2c881cc913116e90fd030ad1c656b3869762b754f5f8a", + "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", + "sha256:7e94c425039cde14257288fd61dcfb01963e658efbc0ff54f5306b06054700f8", + "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", + "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", + "sha256:88b49a3b9ff31e19998750c38e030fc7bb937398b1f78cfa599aaef92d693144", + "sha256:8c4e8c3ce11e1f92f6536ff07154f9d49677ebaaafc32db9db4620bc11ed480f", + "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", + "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", + "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", + "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", + "sha256:9e2d922824181480953426608b81967de705c3cef4d1af983af849d7bd619158", + "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", + "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", + "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", + "sha256:b424c77b206d63d500bcb69fa55ed8d0e6a3774056bdc4839fc9298a7edca171", + "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", + "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", + "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", + "sha256:bbcb445fa71794da8f178f0f6d66789a28d7319071af7a496d4d507ed566270d", + "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", + "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", + "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", + "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", + "sha256:cfad01eed2c2e0c01fd0ecd2ef42c492f7f93902e39a42fc9ee1692961443a29", + "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", + "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", + "sha256:e07c3764494e3776c602c1e78e298937c3315ccc9043ead7e685b7f2b8d47b3c", + "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", + "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", + "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", + "sha256:eaa0a10b7f72326f1372a713e73c3f739b524b3af41feb43e4921cb529f5929a", + "sha256:eb7972a85c54febfb25b5c4b4f3af4dcc731994c7da0d8a0b4a6eb0640e1d178", + "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", + "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", + "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", + "sha256:fcabf5ff6eea076f859677f5f0b6b5c1a51e70a376b0579e0eadef8db48c6b50" ], - "markers": "python_version >= '3.8'", - "version": "==3.10" + "markers": "python_version >= '3.9'", + "version": "==3.0.2" }, "matplotlib": { "hashes": [ - "sha256:039082812cacd6c6bec8e17a9c1e6baca230d4116d522e81e1f63a74d01d2e21", - "sha256:03ba9c1299c920964e8d3857ba27173b4dbb51ca4bab47ffc2c2ba0eb5e2cbc5", - "sha256:050598c2b29e0b9832cde72bcf97627bf00262adbc4a54e2b856426bb2ef0697", - "sha256:18128cc08f0d3cfff10b76baa2f296fc28c4607368a8402de61bb3f2eb33c7d9", - "sha256:1cd93b91ab47a3616b4d3c42b52f8363b88ca021e340804c6ab2536344fad9ca", - "sha256:1d94ff717eb2bd0b58fe66380bd8b14ac35f48a98e7c6765117fe67fb7684e64", - "sha256:306c8dfc73239f0e72ac50e5a9cf19cc4e8e331dd0c54f5e69ca8758550f1e1e", - "sha256:37e51dd1c2db16ede9cfd7b5cabdfc818b2c6397c83f8b10e0e797501c963a03", - "sha256:3fd595f34aa8a55b7fc8bf9ebea8aa665a84c82d275190a61118d33fbc82ccae", - "sha256:4876d7d40219e8ae8bb70f9263bcbe5714415acfdf781086601211335e24f8aa", - "sha256:5413401594cfaff0052f9d8b1aafc6d305b4bd7c4331dccd18f561ff7e1d3bd3", - "sha256:5816b1e1fe8c192cbc013f8f3e3368ac56fbecf02fb41b8f8559303f24c5015e", - "sha256:65aacf95b62272d568044531e41de26285d54aec8cb859031f511f84bd8b495a", - "sha256:6758baae2ed64f2331d4fd19be38b7b4eae3ecec210049a26b6a4f3ae1c85dcc", - "sha256:6d1ce5ed2aefcdce11904fc5bbea7d9c21fff3d5f543841edf3dea84451a09ea", - "sha256:6d9f07a80deab4bb0b82858a9e9ad53d1382fd122be8cde11080f4e7dfedb38b", - "sha256:7741f26a58a240f43bee74965c4882b6c93df3e7eb3de160126d8c8f53a6ae6e", - "sha256:8912ef7c2362f7193b5819d17dae8629b34a95c58603d781329712ada83f9447", - "sha256:909645cce2dc28b735674ce0931a4ac94e12f5b13f6bb0b5a5e65e7cea2c192b", - "sha256:96ab43906269ca64a6366934106fa01534454a69e471b7bf3d79083981aaab92", - "sha256:9d78bbc0cbc891ad55b4f39a48c22182e9bdaea7fc0e5dbd364f49f729ca1bbb", - "sha256:ab68d50c06938ef28681073327795c5db99bb4666214d2d5f880ed11aeaded66", - "sha256:ac43031375a65c3196bee99f6001e7fa5bdfb00ddf43379d3c0609bdca042df9", - "sha256:ae82a14dab96fbfad7965403c643cafe6515e386de723e498cf3eeb1e0b70cc7", - "sha256:b2696efdc08648536efd4e1601b5fd491fd47f4db97a5fbfd175549a7365c1b2", - "sha256:b82c5045cebcecd8496a4d694d43f9cc84aeeb49fe2133e036b207abe73f4d30", - "sha256:be0fc24a5e4531ae4d8e858a1a548c1fe33b176bb13eff7f9d0d38ce5112a27d", - "sha256:bf81de2926c2db243c9b2cbc3917619a0fc85796c6ba4e58f541df814bbf83c7", - "sha256:c375cc72229614632c87355366bdf2570c2dac01ac66b8ad048d2dabadf2d0d4", - "sha256:c797dac8bb9c7a3fd3382b16fe8f215b4cf0f22adccea36f1545a6d7be310b41", - "sha256:cef2a73d06601437be399908cf13aee74e86932a5ccc6ccdf173408ebc5f6bb2", - "sha256:d52a3b618cb1cbb769ce2ee1dcdb333c3ab6e823944e9a2d36e37253815f9556", - "sha256:d719465db13267bcef19ea8954a971db03b9f48b4647e3860e4bc8e6ed86610f", - "sha256:d8dd059447824eec055e829258ab092b56bb0579fc3164fa09c64f3acd478772", - "sha256:dbe196377a8248972f5cede786d4c5508ed5f5ca4a1e09b44bda889958b33f8c", - "sha256:e0830e188029c14e891fadd99702fd90d317df294c3298aad682739c5533721a", - "sha256:f053c40f94bc51bc03832a41b4f153d83f2062d88c72b5e79997072594e97e51", - "sha256:f32c7410c7f246838a77d6d1eff0c0f87f3cb0e7c4247aebea71a6d5a68cab49", - "sha256:f6ee45bc4245533111ced13f1f2cace1e7f89d1c793390392a80c139d6cf0e6c", - "sha256:f7c0410f181a531ec4e93bbc27692f2c71a15c2da16766f5ba9761e7ae518413" + "sha256:026bdf3137ab6022c866efa4813b6bbeddc2ed4c9e7e02f0e323a7bca380dfa0", + "sha256:031b7f5b8e595cc07def77ec5b58464e9bb67dc5760be5d6f26d9da24892481d", + "sha256:0a0a63cb8404d1d1f94968ef35738900038137dab8af836b6c21bb6f03d75465", + "sha256:0a361bd5583bf0bcc08841df3c10269617ee2a36b99ac39d455a767da908bbbc", + "sha256:10d3e5c7a99bd28afb957e1ae661323b0800d75b419f24d041ed1cc5d844a764", + "sha256:1c40c244221a1adbb1256692b1133c6fb89418df27bf759a31a333e7912a4010", + "sha256:203d18df84f5288973b2d56de63d4678cc748250026ca9e1ad8f8a0fd8a75d83", + "sha256:213d6dc25ce686516208d8a3e91120c6a4fdae4a3e06b8505ced5b716b50cc04", + "sha256:3119b2f16de7f7b9212ba76d8fe6a0e9f90b27a1e04683cd89833a991682f639", + "sha256:3fb0b37c896172899a4a93d9442ffdc6f870165f59e05ce2e07c6fded1c15749", + "sha256:41b016e3be4e740b66c79a031a0a6e145728dbc248142e751e8dab4f3188ca1d", + "sha256:4a8d279f78844aad213c4935c18f8292a9432d51af2d88bca99072c903948045", + "sha256:4e6eefae6effa0c35bbbc18c25ee6e0b1da44d2359c3cd526eb0c9e703cf055d", + "sha256:5f2a4ea08e6876206d511365b0bc234edc813d90b930be72c3011bbd7898796f", + "sha256:66d7b171fecf96940ce069923a08ba3df33ef542de82c2ff4fe8caa8346fa95a", + "sha256:687df7ceff57b8f070d02b4db66f75566370e7ae182a0782b6d3d21b0d6917dc", + "sha256:6be0ba61f6ff2e6b68e4270fb63b6813c9e7dec3d15fc3a93f47480444fd72f0", + "sha256:6e9de2b390d253a508dd497e9b5579f3a851f208763ed67fdca5dc0c3ea6849c", + "sha256:760a5e89ebbb172989e8273024a1024b0f084510b9105261b3b00c15e9c9f006", + "sha256:816a966d5d376bf24c92af8f379e78e67278833e4c7cbc9fa41872eec629a060", + "sha256:87ad73763d93add1b6c1f9fcd33af662fd62ed70e620c52fcb79f3ac427cf3a6", + "sha256:896774766fd6be4571a43bc2fcbcb1dcca0807e53cab4a5bf88c4aa861a08e12", + "sha256:8e0143975fc2a6d7136c97e19c637321288371e8f09cff2564ecd73e865ea0b9", + "sha256:90a85a004fefed9e583597478420bf904bb1a065b0b0ee5b9d8d31b04b0f3f70", + "sha256:9b081dac96ab19c54fd8558fac17c9d2c9cb5cc4656e7ed3261ddc927ba3e2c5", + "sha256:9d6b2e8856dec3a6db1ae51aec85c82223e834b228c1d3228aede87eee2b34f9", + "sha256:9f459c8ee2c086455744723628264e43c884be0c7d7b45d84b8cd981310b4815", + "sha256:9fa6e193c14d6944e0685cdb527cb6b38b0e4a518043e7212f214113af7391da", + "sha256:a42b9dc42de2cfe357efa27d9c50c7833fc5ab9b2eb7252ccd5d5f836a84e1e4", + "sha256:b651b0d3642991259109dc0351fc33ad44c624801367bb8307be9bfc35e427ad", + "sha256:b6c12514329ac0d03128cf1dcceb335f4fbf7c11da98bca68dca8dcb983153a9", + "sha256:c52f48eb75fcc119a4fdb68ba83eb5f71656999420375df7c94cc68e0e14686e", + "sha256:c96eeeb8c68b662c7747f91a385688d4b449687d29b691eff7068a4602fe6dc4", + "sha256:cd1077b9a09b16d8c3c7075a8add5ffbfe6a69156a57e290c800ed4d435bef1d", + "sha256:cd5dbbc8e25cad5f706845c4d100e2c8b34691b412b93717ce38d8ae803bcfa5", + "sha256:cf2a60daf6cecff6828bc608df00dbc794380e7234d2411c0ec612811f01969d", + "sha256:d3c93796b44fa111049b88a24105e947f03c01966b5c0cc782e2ee3887b790a3", + "sha256:d796272408f8567ff7eaa00eb2856b3a00524490e47ad505b0b4ca6bb8a7411f", + "sha256:e0fcb7da73fbf67b5f4bdaa57d85bb585a4e913d4a10f3e15b32baea56a67f0a", + "sha256:e14485bb1b83eeb3d55b6878f9560240981e7bbc7a8d4e1e8c38b9bd6ec8d2de", + "sha256:edd14cf733fdc4f6e6fe3f705af97676a7e52859bf0044aa2c84e55be739241c" ], "index": "pypi", "markers": "python_version >= '3.9'", - "version": "==3.9.2" + "version": "==3.9.3" }, "matplotlib-inline": { "hashes": [ @@ -1633,28 +1321,6 @@ "markers": "python_version >= '3.8'", "version": "==0.1.7" }, - "mccabe": { - "hashes": [ - "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", - "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e" - ], - "markers": "python_version >= '3.6'", - "version": "==0.7.0" - }, - "mdurl": { - "hashes": [ - "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", - "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba" - ], - "markers": "python_version >= '3.7'", - "version": "==0.1.2" - }, - "minimal-snowplow-tracker": { - "hashes": [ - "sha256:acabf7572db0e7f5cbf6983d495eef54081f71be392330eb3aadb9ccb39daaa4" - ], - "version": "==0.0.2" - }, "mistune": { "hashes": [ "sha256:71481854c30fdbc938963d3605b72501f5c10a9320ecd412c121c163a1c7d205", @@ -1663,14 +1329,6 @@ "markers": "python_version >= '3.7'", "version": "==3.0.2" }, - "more-itertools": { - "hashes": [ - "sha256:037b0d3203ce90cca8ab1defbbdac29d5f993fc20131f3664dc8d6acfa872aef", - "sha256:5482bfef7849c25dc3c6dd53a6173ae4795da2a41a80faea6700d9f5846c5da6" - ], - "markers": "python_version >= '3.8'", - "version": "==10.5.0" - }, "msgpack": { "hashes": [ "sha256:06f5fd2f6bb2a7914922d935d3b8bb4a7fff3a9a91cfce6d06c13bc42bec975b", @@ -1741,21 +1399,13 @@ "markers": "python_version >= '3.8'", "version": "==1.1.0" }, - "mypy-extensions": { - "hashes": [ - "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", - "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782" - ], - "markers": "python_version >= '3.5'", - "version": "==1.0.0" - }, "nbclient": { "hashes": [ - "sha256:4b3f1b7dba531e498449c4db4f53da339c91d449dc11e9af3a43b4eb5c5abb09", - "sha256:f13e3529332a1f1f81d82a53210322476a168bb7090a0289c795fe9cc11c9d3f" + "sha256:3e93e348ab27e712acd46fccd809139e356eb9a31aab641d1a7991a6eb4e6f68", + "sha256:949019b9240d66897e442888cfb618f69ef23dc71c01cb5fced8499c2cfc084d" ], "markers": "python_full_version >= '3.8.0'", - "version": "==0.10.0" + "version": "==0.10.1" }, "nbconvert": { "hashes": [ @@ -1783,20 +1433,20 @@ }, "networkx": { "hashes": [ - "sha256:0c127d8b2f4865f59ae9cb8aafcd60b5c70f3241ebd66f7defad7c4ab90126c9", - "sha256:28575580c6ebdaf4505b22c6256a2b9de86b316dc63ba9e93abde3d78dfdbcf2" + "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", + "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f" ], "index": "pypi", "markers": "python_version >= '3.10'", - "version": "==3.3" + "version": "==3.4.2" }, "notebook": { "hashes": [ - "sha256:2ef07d4220421623ad3fe88118d687bc0450055570cdd160814a59cf3a1c516e", - "sha256:c89264081f671bc02eec0ed470a627ed791b9156cad9285226b31611d3e9fe1c" + "sha256:212e1486b2230fe22279043f33c7db5cf9a01d29feb063a85cb139747b7c9483", + "sha256:84381c2a82d867517fd25b86e986dae1fe113a70b98f03edff9b94e499fec8fa" ], "markers": "python_version >= '3.8'", - "version": "==7.2.2" + "version": "==7.3.1" }, "notebook-shim": { "hashes": [ @@ -1808,70 +1458,64 @@ }, "numpy": { "hashes": [ - "sha256:046356b19d7ad1890c751b99acad5e82dc4a02232013bd9a9a712fddf8eb60f5", - "sha256:0b8cc2715a84b7c3b161f9ebbd942740aaed913584cae9cdc7f8ad5ad41943d0", - "sha256:0d07841fd284718feffe7dd17a63a2e6c78679b2d386d3e82f44f0108c905550", - "sha256:13cc11c00000848702322af4de0147ced365c81d66053a67c2e962a485b3717c", - "sha256:13ce49a34c44b6de5241f0b38b07e44c1b2dcacd9e36c30f9c2fcb1bb5135db7", - "sha256:24c2ad697bd8593887b019817ddd9974a7f429c14a5469d7fad413f28340a6d2", - "sha256:251105b7c42abe40e3a689881e1793370cc9724ad50d64b30b358bbb3a97553b", - "sha256:2ca4b53e1e0b279142113b8c5eb7d7a877e967c306edc34f3b58e9be12fda8df", - "sha256:3269c9eb8745e8d975980b3a7411a98976824e1fdef11f0aacf76147f662b15f", - "sha256:397bc5ce62d3fb73f304bec332171535c187e0643e176a6e9421a6e3eacef06d", - "sha256:3fc5eabfc720db95d68e6646e88f8b399bfedd235994016351b1d9e062c4b270", - "sha256:50a95ca3560a6058d6ea91d4629a83a897ee27c00630aed9d933dff191f170cd", - "sha256:52ac2e48f5ad847cd43c4755520a2317f3380213493b9d8a4c5e37f3b87df504", - "sha256:53e27293b3a2b661c03f79aa51c3987492bd4641ef933e366e0f9f6c9bf257ec", - "sha256:57eb525e7c2a8fdee02d731f647146ff54ea8c973364f3b850069ffb42799647", - "sha256:5889dd24f03ca5a5b1e8a90a33b5a0846d8977565e4ae003a63d22ecddf6782f", - "sha256:59ca673ad11d4b84ceb385290ed0ebe60266e356641428c845b39cd9df6713ab", - "sha256:6435c48250c12f001920f0751fe50c0348f5f240852cfddc5e2f97e007544cbe", - "sha256:6e5a9cb2be39350ae6c8f79410744e80154df658d5bea06e06e0ac5bb75480d5", - "sha256:7be6a07520b88214ea85d8ac8b7d6d8a1839b0b5cb87412ac9f49fa934eb15d5", - "sha256:7c803b7934a7f59563db459292e6aa078bb38b7ab1446ca38dd138646a38203e", - "sha256:7dd86dfaf7c900c0bbdcb8b16e2f6ddf1eb1fe39c6c8cca6e94844ed3152a8fd", - "sha256:8661c94e3aad18e1ea17a11f60f843a4933ccaf1a25a7c6a9182af70610b2313", - "sha256:8ae0fd135e0b157365ac7cc31fff27f07a5572bdfc38f9c2d43b2aff416cc8b0", - "sha256:910b47a6d0635ec1bd53b88f86120a52bf56dcc27b51f18c7b4a2e2224c29f0f", - "sha256:913cc1d311060b1d409e609947fa1b9753701dac96e6581b58afc36b7ee35af6", - "sha256:920b0911bb2e4414c50e55bd658baeb78281a47feeb064ab40c2b66ecba85553", - "sha256:950802d17a33c07cba7fd7c3dcfa7d64705509206be1606f196d179e539111ed", - "sha256:981707f6b31b59c0c24bcda52e5605f9701cb46da4b86c2e8023656ad3e833cb", - "sha256:98ce7fb5b8063cfdd86596b9c762bf2b5e35a2cdd7e967494ab78a1fa7f8b86e", - "sha256:99f4a9ee60eed1385a86e82288971a51e71df052ed0b2900ed30bc840c0f2e39", - "sha256:9a8e06c7a980869ea67bbf551283bbed2856915f0a792dc32dd0f9dd2fb56728", - "sha256:ae8ce252404cdd4de56dcfce8b11eac3c594a9c16c231d081fb705cf23bd4d9e", - "sha256:afd9c680df4de71cd58582b51e88a61feed4abcc7530bcd3d48483f20fc76f2a", - "sha256:b49742cdb85f1f81e4dc1b39dcf328244f4d8d1ded95dea725b316bd2cf18c95", - "sha256:b5613cfeb1adfe791e8e681128f5f49f22f3fcaa942255a6124d58ca59d9528f", - "sha256:bab7c09454460a487e631ffc0c42057e3d8f2a9ddccd1e60c7bb8ed774992480", - "sha256:c8a0e34993b510fc19b9a2ce7f31cb8e94ecf6e924a40c0c9dd4f62d0aac47d9", - "sha256:caf5d284ddea7462c32b8d4a6b8af030b6c9fd5332afb70e7414d7fdded4bfd0", - "sha256:cea427d1350f3fd0d2818ce7350095c1a2ee33e30961d2f0fef48576ddbbe90f", - "sha256:d0cf7d55b1051387807405b3898efafa862997b4cba8aa5dbe657be794afeafd", - "sha256:d10c39947a2d351d6d466b4ae83dad4c37cd6c3cdd6d5d0fa797da56f710a6ae", - "sha256:d2b9cd92c8f8e7b313b80e93cedc12c0112088541dcedd9197b5dee3738c1201", - "sha256:d4c57b68c8ef5e1ebf47238e99bf27657511ec3f071c465f6b1bccbef12d4136", - "sha256:d51fc141ddbe3f919e91a096ec739f49d686df8af254b2053ba21a910ae518bf", - "sha256:e097507396c0be4e547ff15b13dc3866f45f3680f789c1a1301b07dadd3fbc78", - "sha256:e30356d530528a42eeba51420ae8bf6c6c09559051887196599d96ee5f536468", - "sha256:e8d5f8a8e3bc87334f025194c6193e408903d21ebaeb10952264943a985066ca", - "sha256:e8dfa9e94fc127c40979c3eacbae1e61fda4fe71d84869cc129e2721973231ef", - "sha256:f212d4f46b67ff604d11fff7cc62d36b3e8714edf68e44e9760e19be38c03eb0", - "sha256:f7506387e191fe8cdb267f912469a3cccc538ab108471291636a96a54e599556", - "sha256:fac6e277a41163d27dfab5f4ec1f7a83fac94e170665a4a50191b545721c6521", - "sha256:fcd8f556cdc8cfe35e70efb92463082b7f43dd7e547eb071ffc36abc0ca4699b" + "sha256:0557eebc699c1c34cccdd8c3778c9294e8196df27d713706895edc6f57d29608", + "sha256:0798b138c291d792f8ea40fe3768610f3c7dd2574389e37c3f26573757c8f7ef", + "sha256:0da8495970f6b101ddd0c38ace92edea30e7e12b9a926b57f5fabb1ecc25bb90", + "sha256:0f0986e917aca18f7a567b812ef7ca9391288e2acb7a4308aa9d265bd724bdae", + "sha256:122fd2fcfafdefc889c64ad99c228d5a1f9692c3a83f56c292618a59aa60ae83", + "sha256:140dd80ff8981a583a60980be1a655068f8adebf7a45a06a6858c873fcdcd4a0", + "sha256:16757cf28621e43e252c560d25b15f18a2f11da94fea344bf26c599b9cf54b73", + "sha256:18142b497d70a34b01642b9feabb70156311b326fdddd875a9981f34a369b671", + "sha256:1c92113619f7b272838b8d6702a7f8ebe5edea0df48166c47929611d0b4dea69", + "sha256:1e25507d85da11ff5066269d0bd25d06e0a0f2e908415534f3e603d2a78e4ffa", + "sha256:30bf971c12e4365153afb31fc73f441d4da157153f3400b82db32d04de1e4066", + "sha256:3579eaeb5e07f3ded59298ce22b65f877a86ba8e9fe701f5576c99bb17c283da", + "sha256:36b2b43146f646642b425dd2027730f99bac962618ec2052932157e213a040e9", + "sha256:3905a5fffcc23e597ee4d9fb3fcd209bd658c352657548db7316e810ca80458e", + "sha256:3a4199f519e57d517ebd48cb76b36c82da0360781c6a0353e64c0cac30ecaad3", + "sha256:3f2f5cddeaa4424a0a118924b988746db6ffa8565e5829b1841a8a3bd73eb59a", + "sha256:40deb10198bbaa531509aad0cd2f9fadb26c8b94070831e2208e7df543562b74", + "sha256:440cfb3db4c5029775803794f8638fbdbf71ec702caf32735f53b008e1eaece3", + "sha256:4723a50e1523e1de4fccd1b9a6dcea750c2102461e9a02b2ac55ffeae09a4410", + "sha256:4bddbaa30d78c86329b26bd6aaaea06b1e47444da99eddac7bf1e2fab717bd72", + "sha256:4e58666988605e251d42c2818c7d3d8991555381be26399303053b58a5bbf30d", + "sha256:54dc1d6d66f8d37843ed281773c7174f03bf7ad826523f73435deb88ba60d2d4", + "sha256:57fcc997ffc0bef234b8875a54d4058afa92b0b0c4223fc1f62f24b3b5e86038", + "sha256:58b92a5828bd4d9aa0952492b7de803135038de47343b2aa3cc23f3b71a3dc4e", + "sha256:5a145e956b374e72ad1dff82779177d4a3c62bc8248f41b80cb5122e68f22d13", + "sha256:6ab153263a7c5ccaf6dfe7e53447b74f77789f28ecb278c3b5d49db7ece10d6d", + "sha256:7832f9e8eb00be32f15fdfb9a981d6955ea9adc8574c521d48710171b6c55e95", + "sha256:7fe4bb0695fe986a9e4deec3b6857003b4cfe5c5e4aac0b95f6a658c14635e31", + "sha256:7fe8f3583e0607ad4e43a954e35c1748b553bfe9fdac8635c02058023277d1b3", + "sha256:85ad7d11b309bd132d74397fcf2920933c9d1dc865487128f5c03d580f2c3d03", + "sha256:9874bc2ff574c40ab7a5cbb7464bf9b045d617e36754a7bc93f933d52bd9ffc6", + "sha256:a184288538e6ad699cbe6b24859206e38ce5fba28f3bcfa51c90d0502c1582b2", + "sha256:a222d764352c773aa5ebde02dd84dba3279c81c6db2e482d62a3fa54e5ece69b", + "sha256:a50aeff71d0f97b6450d33940c7181b08be1441c6c193e678211bff11aa725e7", + "sha256:a55dc7a7f0b6198b07ec0cd445fbb98b05234e8b00c5ac4874a63372ba98d4ab", + "sha256:a62eb442011776e4036af5c8b1a00b706c5bc02dc15eb5344b0c750428c94219", + "sha256:a7d41d1612c1a82b64697e894b75db6758d4f21c3ec069d841e60ebe54b5b571", + "sha256:a98f6f20465e7618c83252c02041517bd2f7ea29be5378f09667a8f654a5918d", + "sha256:afe8fb968743d40435c3827632fd36c5fbde633b0423da7692e426529b1759b1", + "sha256:b0b227dcff8cdc3efbce66d4e50891f04d0a387cce282fe1e66199146a6a8fca", + "sha256:b30042fe92dbd79f1ba7f6898fada10bdaad1847c44f2dff9a16147e00a93661", + "sha256:b606b1aaf802e6468c2608c65ff7ece53eae1a6874b3765f69b8ceb20c5fa78e", + "sha256:b6207dc8fb3c8cb5668e885cef9ec7f70189bec4e276f0ff70d5aa078d32c88e", + "sha256:c2aed8fcf8abc3020d6a9ccb31dbc9e7d7819c56a348cc88fd44be269b37427e", + "sha256:cb24cca1968b21355cc6f3da1a20cd1cebd8a023e3c5b09b432444617949085a", + "sha256:cff210198bb4cae3f3c100444c5eaa573a823f05c253e7188e1362a5555235b3", + "sha256:d35717333b39d1b6bb8433fa758a55f1081543de527171543a2b710551d40881", + "sha256:df12a1f99b99f569a7c2ae59aa2d31724e8d835fc7f33e14f4792e3071d11221", + "sha256:e09d40edfdb4e260cb1567d8ae770ccf3b8b7e9f0d9b5c2a9992696b30ce2742", + "sha256:e12c6c1ce84628c52d6367863773f7c8c8241be554e8b79686e91a43f1733773", + "sha256:e2b8cd48a9942ed3f85b95ca4105c45758438c7ed28fff1e4ce3e57c3b589d8e", + "sha256:e500aba968a48e9019e42c0c199b7ec0696a97fa69037bea163b55398e390529", + "sha256:ebe5e59545401fbb1b24da76f006ab19734ae71e703cdb4a8b347e84a0cece67", + "sha256:f0dd071b95bbca244f4cb7f70b77d2ff3aaaba7fa16dc41f58d14854a6204e6c", + "sha256:f8c8b141ef9699ae777c6278b52c706b653bf15d135d302754f6b2e90eb30367" ], "markers": "python_version >= '3.10'", - "version": "==2.1.1" - }, - "ordered-set": { - "hashes": [ - "sha256:046e1132c71fcf3330438a539928932caf51ddbc582496833e23de611de14562", - "sha256:694a8e44c87657c59292ede72891eb91d34131f6531463aab3009191c77364a8" - ], - "markers": "python_version >= '3.7'", - "version": "==4.1.0" + "version": "==2.2.0" }, "overrides": { "hashes": [ @@ -1883,11 +1527,11 @@ }, "packaging": { "hashes": [ - "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002", - "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124" + "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", + "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f" ], "markers": "python_version >= '3.8'", - "version": "==24.1" + "version": "==24.2" }, "pandas": { "hashes": [ @@ -1933,13 +1577,6 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.5.1" }, - "parsedatetime": { - "hashes": [ - "sha256:4cb368fbb18a0b7231f4d76119165451c8d2e35951455dfee97c62a87b04d455", - "sha256:cb96edd7016872f58479e35879294258c71437195760746faffedb692aef000b" - ], - "version": "==2.6" - }, "parso": { "hashes": [ "sha256:a418670a20291dacd2dddc80c377c5c3791378ee1e8d12bffc35420643d43f18", @@ -1948,14 +1585,6 @@ "markers": "python_version >= '3.6'", "version": "==0.8.4" }, - "pathspec": { - "hashes": [ - "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", - "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712" - ], - "markers": "python_version >= '3.8'", - "version": "==0.12.1" - }, "pexpect": { "hashes": [ "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", @@ -1966,89 +1595,84 @@ }, "pillow": { "hashes": [ - "sha256:02a2be69f9c9b8c1e97cf2713e789d4e398c751ecfd9967c18d0ce304efbf885", - "sha256:030abdbe43ee02e0de642aee345efa443740aa4d828bfe8e2eb11922ea6a21ea", - "sha256:06b2f7898047ae93fad74467ec3d28fe84f7831370e3c258afa533f81ef7f3df", - "sha256:0755ffd4a0c6f267cccbae2e9903d95477ca2f77c4fcf3a3a09570001856c8a5", - "sha256:0a9ec697746f268507404647e531e92889890a087e03681a3606d9b920fbee3c", - "sha256:0ae24a547e8b711ccaaf99c9ae3cd975470e1a30caa80a6aaee9a2f19c05701d", - "sha256:134ace6dc392116566980ee7436477d844520a26a4b1bd4053f6f47d096997fd", - "sha256:166c1cd4d24309b30d61f79f4a9114b7b2313d7450912277855ff5dfd7cd4a06", - "sha256:1b5dea9831a90e9d0721ec417a80d4cbd7022093ac38a568db2dd78363b00908", - "sha256:1d846aea995ad352d4bdcc847535bd56e0fd88d36829d2c90be880ef1ee4668a", - "sha256:1ef61f5dd14c300786318482456481463b9d6b91ebe5ef12f405afbba77ed0be", - "sha256:297e388da6e248c98bc4a02e018966af0c5f92dfacf5a5ca22fa01cb3179bca0", - "sha256:298478fe4f77a4408895605f3482b6cc6222c018b2ce565c2b6b9c354ac3229b", - "sha256:29dbdc4207642ea6aad70fbde1a9338753d33fb23ed6956e706936706f52dd80", - "sha256:2db98790afc70118bd0255c2eeb465e9767ecf1f3c25f9a1abb8ffc8cfd1fe0a", - "sha256:32cda9e3d601a52baccb2856b8ea1fc213c90b340c542dcef77140dfa3278a9e", - "sha256:37fb69d905be665f68f28a8bba3c6d3223c8efe1edf14cc4cfa06c241f8c81d9", - "sha256:416d3a5d0e8cfe4f27f574362435bc9bae57f679a7158e0096ad2beb427b8696", - "sha256:43efea75eb06b95d1631cb784aa40156177bf9dd5b4b03ff38979e048258bc6b", - "sha256:4b35b21b819ac1dbd1233317adeecd63495f6babf21b7b2512d244ff6c6ce309", - "sha256:4d9667937cfa347525b319ae34375c37b9ee6b525440f3ef48542fcf66f2731e", - "sha256:5161eef006d335e46895297f642341111945e2c1c899eb406882a6c61a4357ab", - "sha256:543f3dc61c18dafb755773efc89aae60d06b6596a63914107f75459cf984164d", - "sha256:551d3fd6e9dc15e4c1eb6fc4ba2b39c0c7933fa113b220057a34f4bb3268a060", - "sha256:59291fb29317122398786c2d44427bbd1a6d7ff54017075b22be9d21aa59bd8d", - "sha256:5b001114dd152cfd6b23befeb28d7aee43553e2402c9f159807bf55f33af8a8d", - "sha256:5b4815f2e65b30f5fbae9dfffa8636d992d49705723fe86a3661806e069352d4", - "sha256:5dc6761a6efc781e6a1544206f22c80c3af4c8cf461206d46a1e6006e4429ff3", - "sha256:5e84b6cc6a4a3d76c153a6b19270b3526a5a8ed6b09501d3af891daa2a9de7d6", - "sha256:6209bb41dc692ddfee4942517c19ee81b86c864b626dbfca272ec0f7cff5d9fb", - "sha256:673655af3eadf4df6b5457033f086e90299fdd7a47983a13827acf7459c15d94", - "sha256:6c762a5b0997f5659a5ef2266abc1d8851ad7749ad9a6a5506eb23d314e4f46b", - "sha256:7086cc1d5eebb91ad24ded9f58bec6c688e9f0ed7eb3dbbf1e4800280a896496", - "sha256:73664fe514b34c8f02452ffb73b7a92c6774e39a647087f83d67f010eb9a0cf0", - "sha256:76a911dfe51a36041f2e756b00f96ed84677cdeb75d25c767f296c1c1eda1319", - "sha256:780c072c2e11c9b2c7ca37f9a2ee8ba66f44367ac3e5c7832afcfe5104fd6d1b", - "sha256:7928ecbf1ece13956b95d9cbcfc77137652b02763ba384d9ab508099a2eca856", - "sha256:7970285ab628a3779aecc35823296a7869f889b8329c16ad5a71e4901a3dc4ef", - "sha256:7a8d4bade9952ea9a77d0c3e49cbd8b2890a399422258a77f357b9cc9be8d680", - "sha256:7c1ee6f42250df403c5f103cbd2768a28fe1a0ea1f0f03fe151c8741e1469c8b", - "sha256:7dfecdbad5c301d7b5bde160150b4db4c659cee2b69589705b6f8a0c509d9f42", - "sha256:812f7342b0eee081eaec84d91423d1b4650bb9828eb53d8511bcef8ce5aecf1e", - "sha256:866b6942a92f56300012f5fbac71f2d610312ee65e22f1aa2609e491284e5597", - "sha256:86dcb5a1eb778d8b25659d5e4341269e8590ad6b4e8b44d9f4b07f8d136c414a", - "sha256:87dd88ded2e6d74d31e1e0a99a726a6765cda32d00ba72dc37f0651f306daaa8", - "sha256:8bc1a764ed8c957a2e9cacf97c8b2b053b70307cf2996aafd70e91a082e70df3", - "sha256:8d4d5063501b6dd4024b8ac2f04962d661222d120381272deea52e3fc52d3736", - "sha256:8f0aef4ef59694b12cadee839e2ba6afeab89c0f39a3adc02ed51d109117b8da", - "sha256:930044bb7679ab003b14023138b50181899da3f25de50e9dbee23b61b4de2126", - "sha256:950be4d8ba92aca4b2bb0741285a46bfae3ca699ef913ec8416c1b78eadd64cd", - "sha256:961a7293b2457b405967af9c77dcaa43cc1a8cd50d23c532e62d48ab6cdd56f5", - "sha256:9b885f89040bb8c4a1573566bbb2f44f5c505ef6e74cec7ab9068c900047f04b", - "sha256:9f4727572e2918acaa9077c919cbbeb73bd2b3ebcfe033b72f858fc9fbef0026", - "sha256:a02364621fe369e06200d4a16558e056fe2805d3468350df3aef21e00d26214b", - "sha256:a985e028fc183bf12a77a8bbf36318db4238a3ded7fa9df1b9a133f1cb79f8fc", - "sha256:ac1452d2fbe4978c2eec89fb5a23b8387aba707ac72810d9490118817d9c0b46", - "sha256:b15e02e9bb4c21e39876698abf233c8c579127986f8207200bc8a8f6bb27acf2", - "sha256:b2724fdb354a868ddf9a880cb84d102da914e99119211ef7ecbdc613b8c96b3c", - "sha256:bbc527b519bd3aa9d7f429d152fea69f9ad37c95f0b02aebddff592688998abe", - "sha256:bcd5e41a859bf2e84fdc42f4edb7d9aba0a13d29a2abadccafad99de3feff984", - "sha256:bd2880a07482090a3bcb01f4265f1936a903d70bc740bfcb1fd4e8a2ffe5cf5a", - "sha256:bee197b30783295d2eb680b311af15a20a8b24024a19c3a26431ff83eb8d1f70", - "sha256:bf2342ac639c4cf38799a44950bbc2dfcb685f052b9e262f446482afaf4bffca", - "sha256:c76e5786951e72ed3686e122d14c5d7012f16c8303a674d18cdcd6d89557fc5b", - "sha256:cbed61494057c0f83b83eb3a310f0bf774b09513307c434d4366ed64f4128a91", - "sha256:cfdd747216947628af7b259d274771d84db2268ca062dd5faf373639d00113a3", - "sha256:d7480af14364494365e89d6fddc510a13e5a2c3584cb19ef65415ca57252fb84", - "sha256:dbc6ae66518ab3c5847659e9988c3b60dc94ffb48ef9168656e0019a93dbf8a1", - "sha256:dc3e2db6ba09ffd7d02ae9141cfa0ae23393ee7687248d46a7507b75d610f4f5", - "sha256:dfe91cb65544a1321e631e696759491ae04a2ea11d36715eca01ce07284738be", - "sha256:e4d49b85c4348ea0b31ea63bc75a9f3857869174e2bf17e7aba02945cd218e6f", - "sha256:e4db64794ccdf6cb83a59d73405f63adbe2a1887012e308828596100a0b2f6cc", - "sha256:e553cad5179a66ba15bb18b353a19020e73a7921296a7979c4a2b7f6a5cd57f9", - "sha256:e88d5e6ad0d026fba7bdab8c3f225a69f063f116462c49892b0149e21b6c0a0e", - "sha256:ecd85a8d3e79cd7158dec1c9e5808e821feea088e2f69a974db5edf84dc53141", - "sha256:f5b92f4d70791b4a67157321c4e8225d60b119c5cc9aee8ecf153aace4aad4ef", - "sha256:f5f0c3e969c8f12dd2bb7e0b15d5c468b51e5017e01e2e867335c81903046a22", - "sha256:f7baece4ce06bade126fb84b8af1c33439a76d8a6fd818970215e0560ca28c27", - "sha256:ff25afb18123cea58a591ea0244b92eb1e61a1fd497bf6d6384f09bc3262ec3e", - "sha256:ff337c552345e95702c5fde3158acb0625111017d0e5f24bf3acdb9cc16b90d1" - ], - "markers": "python_version >= '3.8'", - "version": "==10.4.0" + "sha256:00177a63030d612148e659b55ba99527803288cea7c75fb05766ab7981a8c1b7", + "sha256:006bcdd307cc47ba43e924099a038cbf9591062e6c50e570819743f5607404f5", + "sha256:084a07ef0821cfe4858fe86652fffac8e187b6ae677e9906e192aafcc1b69903", + "sha256:0ae08bd8ffc41aebf578c2af2f9d8749d91f448b3bfd41d7d9ff573d74f2a6b2", + "sha256:0e038b0745997c7dcaae350d35859c9715c71e92ffb7e0f4a8e8a16732150f38", + "sha256:1187739620f2b365de756ce086fdb3604573337cc28a0d3ac4a01ab6b2d2a6d2", + "sha256:16095692a253047fe3ec028e951fa4221a1f3ed3d80c397e83541a3037ff67c9", + "sha256:1a61b54f87ab5786b8479f81c4b11f4d61702830354520837f8cc791ebba0f5f", + "sha256:1c1d72714f429a521d8d2d018badc42414c3077eb187a59579f28e4270b4b0fc", + "sha256:1e2688958a840c822279fda0086fec1fdab2f95bf2b717b66871c4ad9859d7e8", + "sha256:20ec184af98a121fb2da42642dea8a29ec80fc3efbaefb86d8fdd2606619045d", + "sha256:21a0d3b115009ebb8ac3d2ebec5c2982cc693da935f4ab7bb5c8ebe2f47d36f2", + "sha256:224aaa38177597bb179f3ec87eeefcce8e4f85e608025e9cfac60de237ba6316", + "sha256:2679d2258b7f1192b378e2893a8a0a0ca472234d4c2c0e6bdd3380e8dfa21b6a", + "sha256:27a7860107500d813fcd203b4ea19b04babe79448268403172782754870dac25", + "sha256:290f2cc809f9da7d6d622550bbf4c1e57518212da51b6a30fe8e0a270a5b78bd", + "sha256:2e46773dc9f35a1dd28bd6981332fd7f27bec001a918a72a79b4133cf5291dba", + "sha256:3107c66e43bda25359d5ef446f59c497de2b5ed4c7fdba0894f8d6cf3822dafc", + "sha256:375b8dd15a1f5d2feafff536d47e22f69625c1aa92f12b339ec0b2ca40263273", + "sha256:45c566eb10b8967d71bf1ab8e4a525e5a93519e29ea071459ce517f6b903d7fa", + "sha256:499c3a1b0d6fc8213519e193796eb1a86a1be4b1877d678b30f83fd979811d1a", + "sha256:4ad70c4214f67d7466bea6a08061eba35c01b1b89eaa098040a35272a8efb22b", + "sha256:4b60c9520f7207aaf2e1d94de026682fc227806c6e1f55bba7606d1c94dd623a", + "sha256:5178952973e588b3f1360868847334e9e3bf49d19e169bbbdfaf8398002419ae", + "sha256:52a2d8323a465f84faaba5236567d212c3668f2ab53e1c74c15583cf507a0291", + "sha256:598b4e238f13276e0008299bd2482003f48158e2b11826862b1eb2ad7c768b97", + "sha256:5bd2d3bdb846d757055910f0a59792d33b555800813c3b39ada1829c372ccb06", + "sha256:5c39ed17edea3bc69c743a8dd3e9853b7509625c2462532e62baa0732163a904", + "sha256:5d203af30149ae339ad1b4f710d9844ed8796e97fda23ffbc4cc472968a47d0b", + "sha256:5ddbfd761ee00c12ee1be86c9c0683ecf5bb14c9772ddbd782085779a63dd55b", + "sha256:607bbe123c74e272e381a8d1957083a9463401f7bd01287f50521ecb05a313f8", + "sha256:61b887f9ddba63ddf62fd02a3ba7add935d053b6dd7d58998c630e6dbade8527", + "sha256:6619654954dc4936fcff82db8eb6401d3159ec6be81e33c6000dfd76ae189947", + "sha256:674629ff60030d144b7bca2b8330225a9b11c482ed408813924619c6f302fdbb", + "sha256:6ec0d5af64f2e3d64a165f490d96368bb5dea8b8f9ad04487f9ab60dc4bb6003", + "sha256:6f4dba50cfa56f910241eb7f883c20f1e7b1d8f7d91c750cd0b318bad443f4d5", + "sha256:70fbbdacd1d271b77b7721fe3cdd2d537bbbd75d29e6300c672ec6bb38d9672f", + "sha256:72bacbaf24ac003fea9bff9837d1eedb6088758d41e100c1552930151f677739", + "sha256:7326a1787e3c7b0429659e0a944725e1b03eeaa10edd945a86dead1913383944", + "sha256:73853108f56df97baf2bb8b522f3578221e56f646ba345a372c78326710d3830", + "sha256:73e3a0200cdda995c7e43dd47436c1548f87a30bb27fb871f352a22ab8dcf45f", + "sha256:75acbbeb05b86bc53cbe7b7e6fe00fbcf82ad7c684b3ad82e3d711da9ba287d3", + "sha256:8069c5179902dcdce0be9bfc8235347fdbac249d23bd90514b7a47a72d9fecf4", + "sha256:846e193e103b41e984ac921b335df59195356ce3f71dcfd155aa79c603873b84", + "sha256:8594f42df584e5b4bb9281799698403f7af489fba84c34d53d1c4bfb71b7c4e7", + "sha256:86510e3f5eca0ab87429dd77fafc04693195eec7fd6a137c389c3eeb4cfb77c6", + "sha256:8853a3bf12afddfdf15f57c4b02d7ded92c7a75a5d7331d19f4f9572a89c17e6", + "sha256:88a58d8ac0cc0e7f3a014509f0455248a76629ca9b604eca7dc5927cc593c5e9", + "sha256:8ba470552b48e5835f1d23ecb936bb7f71d206f9dfeee64245f30c3270b994de", + "sha256:8c676b587da5673d3c75bd67dd2a8cdfeb282ca38a30f37950511766b26858c4", + "sha256:8ec4a89295cd6cd4d1058a5e6aec6bf51e0eaaf9714774e1bfac7cfc9051db47", + "sha256:94f3e1780abb45062287b4614a5bc0874519c86a777d4a7ad34978e86428b8dd", + "sha256:9a0f748eaa434a41fccf8e1ee7a3eed68af1b690e75328fd7a60af123c193b50", + "sha256:a5629742881bcbc1f42e840af185fd4d83a5edeb96475a575f4da50d6ede337c", + "sha256:a65149d8ada1055029fcb665452b2814fe7d7082fcb0c5bed6db851cb69b2086", + "sha256:b3c5ac4bed7519088103d9450a1107f76308ecf91d6dabc8a33a2fcfb18d0fba", + "sha256:b4fd7bd29610a83a8c9b564d457cf5bd92b4e11e79a4ee4716a63c959699b306", + "sha256:bcd1fb5bb7b07f64c15618c89efcc2cfa3e95f0e3bcdbaf4642509de1942a699", + "sha256:c12b5ae868897c7338519c03049a806af85b9b8c237b7d675b8c5e089e4a618e", + "sha256:c26845094b1af3c91852745ae78e3ea47abf3dbcd1cf962f16b9a5fbe3ee8488", + "sha256:c6a660307ca9d4867caa8d9ca2c2658ab685de83792d1876274991adec7b93fa", + "sha256:c809a70e43c7977c4a42aefd62f0131823ebf7dd73556fa5d5950f5b354087e2", + "sha256:c8b2351c85d855293a299038e1f89db92a2f35e8d2f783489c6f0b2b5f3fe8a3", + "sha256:cb929ca942d0ec4fac404cbf520ee6cac37bf35be479b970c4ffadf2b6a1cad9", + "sha256:d2c0a187a92a1cb5ef2c8ed5412dd8d4334272617f532d4ad4de31e0495bd923", + "sha256:d69bfd8ec3219ae71bcde1f942b728903cad25fafe3100ba2258b973bd2bc1b2", + "sha256:daffdf51ee5db69a82dd127eabecce20729e21f7a3680cf7cbb23f0829189790", + "sha256:e58876c91f97b0952eb766123bfef372792ab3f4e3e1f1a2267834c2ab131734", + "sha256:eda2616eb2313cbb3eebbe51f19362eb434b18e3bb599466a1ffa76a033fb916", + "sha256:ee217c198f2e41f184f3869f3e485557296d505b5195c513b2bfe0062dc537f1", + "sha256:f02541ef64077f22bf4924f225c0fd1248c168f86e4b7abdedd87d6ebaceab0f", + "sha256:f1b82c27e89fffc6da125d5eb0ca6e68017faf5efc078128cfaa42cf5cb38798", + "sha256:fba162b8872d30fea8c52b258a542c5dfd7b235fb5cb352240c8d63b414013eb", + "sha256:fbbcb7b57dc9c794843e3d1258c0fbf0f48656d46ffe9e09b63bbd6e8cd5d0a2", + "sha256:fcb4621042ac4b7865c179bb972ed0da0218a076dc1820ffc48b1d74c1e37fe9" + ], + "markers": "python_version >= '3.9'", + "version": "==11.0.0" }, "platformdirs": { "hashes": [ @@ -2058,14 +1682,6 @@ "markers": "python_version >= '3.8'", "version": "==4.3.6" }, - "pluggy": { - "hashes": [ - "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", - "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669" - ], - "markers": "python_version >= '3.8'", - "version": "==1.5.0" - }, "polyline": { "hashes": [ "sha256:10541e759c5fd51f746ee304e9af94744089a4055b6257b293b3afd1df64e369", @@ -2076,11 +1692,11 @@ }, "prometheus-client": { "hashes": [ - "sha256:4fa6b4dd0ac16d58bb587c04b1caae65b8c5043e85f778f42f5f632f6af2e166", - "sha256:96c83c606b71ff2b0a433c98889d275f51ffec6c5e267de37c7a2b5c9aa9233e" + "sha256:252505a722ac04b0456be05c05f75f45d760c2911ffc45f2a06bcaed9f3ae3fb", + "sha256:594b45c410d6f4f8888940fe80b5cc2521b305a1fafe1c58609ef715a001f301" ], "markers": "python_version >= '3.8'", - "version": "==0.21.0" + "version": "==0.21.1" }, "prompt-toolkit": { "hashes": [ @@ -2092,148 +1708,125 @@ }, "proto-plus": { "hashes": [ - "sha256:30b72a5ecafe4406b0d339db35b56c4059064e69227b8c3bda7462397f966445", - "sha256:402576830425e5f6ce4c2a6702400ac79897dab0b4343821aa5188b0fab81a12" + "sha256:c91fc4a65074ade8e458e95ef8bac34d4008daa7cce4a12d6707066fca648961", + "sha256:fbb17f57f7bd05a68b7707e745e26528b0b3c34e378db91eef93912c54982d91" ], "markers": "python_version >= '3.7'", - "version": "==1.24.0" + "version": "==1.25.0" }, "protobuf": { "hashes": [ - "sha256:0aebecb809cae990f8129ada5ca273d9d670b76d9bfc9b1809f0a9c02b7dbf41", - "sha256:4be0571adcbe712b282a330c6e89eae24281344429ae95c6d85e79e84780f5ea", - "sha256:5e61fd921603f58d2f5acb2806a929b4675f8874ff5f330b7d6f7e2e784bbcd8", - "sha256:7a183f592dc80aa7c8da7ad9e55091c4ffc9497b3054452d629bb85fa27c2a45", - "sha256:7f8249476b4a9473645db7f8ab42b02fe1488cbe5fb72fddd445e0665afd8584", - "sha256:919ad92d9b0310070f8356c24b855c98df2b8bd207ebc1c0c6fcc9ab1e007f3d", - "sha256:98d8d8aa50de6a2747efd9cceba361c9034050ecce3e09136f90de37ddba66e1", - "sha256:abe32aad8561aa7cc94fc7ba4fdef646e576983edb94a73381b03c53728a626f", - "sha256:b0234dd5a03049e4ddd94b93400b67803c823cfc405689688f59b34e0742381a", - "sha256:b2fde3d805354df675ea4c7c6338c1aecd254dfc9925e88c6d31a2bcb97eb173", - "sha256:fe14e16c22be926d3abfcb500e60cab068baf10b542b8c858fa27e098123e331" + "sha256:012ce28d862ff417fd629285aca5d9772807f15ceb1a0dbd15b88f58c776c98c", + "sha256:027fbcc48cea65a6b17028510fdd054147057fa78f4772eb547b9274e5219331", + "sha256:1fc55267f086dd4050d18ef839d7bd69300d0d08c2a53ca7df3920cc271a3c34", + "sha256:22c1f539024241ee545cbcb00ee160ad1877975690b16656ff87dde107b5f110", + "sha256:32600ddb9c2a53dedc25b8581ea0f1fd8ea04956373c0c07577ce58d312522e0", + "sha256:50879eb0eb1246e3a5eabbbe566b44b10348939b7cc1b267567e8c3d07213853", + "sha256:5a41deccfa5e745cef5c65a560c76ec0ed8e70908a67cc8f4da5fce588b50d57", + "sha256:683be02ca21a6ffe80db6dd02c0b5b2892322c59ca57fd6c872d652cb80549cb", + "sha256:8ee1461b3af56145aca2800e6a3e2f928108c749ba8feccc6f5dd0062c410c0d", + "sha256:b5ba1d0e4c8a40ae0496d0e2ecfdbb82e1776928a205106d14ad6985a09ec155", + "sha256:d473655e29c0c4bbf8b69e9a8fb54645bc289dead6d753b952e7aa660254ae18" ], "markers": "python_version >= '3.8'", - "version": "==4.25.5" + "version": "==5.29.1" }, "psutil": { "hashes": [ - "sha256:02b69001f44cc73c1c5279d02b30a817e339ceb258ad75997325e0e6169d8b35", - "sha256:1287c2b95f1c0a364d23bc6f2ea2365a8d4d9b726a3be7294296ff7ba97c17f0", - "sha256:1e7c870afcb7d91fdea2b37c24aeb08f98b6d67257a5cb0a8bc3ac68d0f1a68c", - "sha256:21f1fb635deccd510f69f485b87433460a603919b45e2a324ad65b0cc74f8fb1", - "sha256:33ea5e1c975250a720b3a6609c490db40dae5d83a4eb315170c4fe0d8b1f34b3", - "sha256:34859b8d8f423b86e4385ff3665d3f4d94be3cdf48221fbe476e883514fdb71c", - "sha256:5fd9a97c8e94059b0ef54a7d4baf13b405011176c3b6ff257c247cae0d560ecd", - "sha256:6ec7588fb3ddaec7344a825afe298db83fe01bfaaab39155fa84cf1c0d6b13c3", - "sha256:6ed2440ada7ef7d0d608f20ad89a04ec47d2d3ab7190896cd62ca5fc4fe08bf0", - "sha256:8faae4f310b6d969fa26ca0545338b21f73c6b15db7c4a8d934a5482faa818f2", - "sha256:a021da3e881cd935e64a3d0a20983bda0bb4cf80e4f74fa9bfcb1bc5785360c6", - "sha256:a495580d6bae27291324fe60cea0b5a7c23fa36a7cd35035a16d93bdcf076b9d", - "sha256:a9a3dbfb4de4f18174528d87cc352d1f788b7496991cca33c6996f40c9e3c92c", - "sha256:c588a7e9b1173b6e866756dde596fd4cad94f9399daf99ad8c3258b3cb2b47a0", - "sha256:e2e8d0054fc88153ca0544f5c4d554d42e33df2e009c4ff42284ac9ebdef4132", - "sha256:fc8c9510cde0146432bbdb433322861ee8c3efbf8589865c8bf8d21cb30c4d14", - "sha256:ffe7fc9b6b36beadc8c322f84e1caff51e8703b88eee1da46d1e3a6ae11b4fd0" + "sha256:000d1d1ebd634b4efb383f4034437384e44a6d455260aaee2eca1e9c1b55f047", + "sha256:045f00a43c737f960d273a83973b2511430d61f283a44c96bf13a6e829ba8fdc", + "sha256:0895b8414afafc526712c498bd9de2b063deaac4021a3b3c34566283464aff8e", + "sha256:1209036fbd0421afde505a4879dee3b2fd7b1e14fee81c0069807adcbbcca747", + "sha256:1ad45a1f5d0b608253b11508f80940985d1d0c8f6111b5cb637533a0e6ddc13e", + "sha256:353815f59a7f64cdaca1c0307ee13558a0512f6db064e92fe833784f08539c7a", + "sha256:498c6979f9c6637ebc3a73b3f87f9eb1ec24e1ce53a7c5173b8508981614a90b", + "sha256:5cd2bcdc75b452ba2e10f0e8ecc0b57b827dd5d7aaffbc6821b2a9a242823a76", + "sha256:6d3fbbc8d23fcdcb500d2c9f94e07b1342df8ed71b948a2649b5cb060a7c94ca", + "sha256:6e2dcd475ce8b80522e51d923d10c7871e45f20918e027ab682f94f1c6351688", + "sha256:9118f27452b70bb1d9ab3198c1f626c2499384935aaf55388211ad982611407e", + "sha256:9dcbfce5d89f1d1f2546a2090f4fcf87c7f669d1d90aacb7d7582addece9fb38", + "sha256:a8506f6119cff7015678e2bce904a4da21025cc70ad283a53b099e7620061d85", + "sha256:a8fb3752b491d246034fa4d279ff076501588ce8cbcdbb62c32fd7a377d996be", + "sha256:c0e0c00aa18ca2d3b2b991643b799a15fc8f0563d2ebb6040f64ce8dc027b942", + "sha256:d905186d647b16755a800e7263d43df08b790d709d575105d419f8b6ef65423a", + "sha256:ff34df86226c0227c52f38b919213157588a678d049688eded74c76c8ba4a5d0" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==6.0.0" - }, - "psycopg2": { - "hashes": [ - "sha256:121081ea2e76729acfb0673ff33755e8703d45e926e416cb59bae3a86c6a4981", - "sha256:38a8dcc6856f569068b47de286b472b7c473ac7977243593a288ebce0dc89516", - "sha256:426f9f29bde126913a20a96ff8ce7d73fd8a216cfb323b1f04da402d452853c3", - "sha256:5e0d98cade4f0e0304d7d6f25bbfbc5bd186e07b38eac65379309c4ca3193efa", - "sha256:7e2dacf8b009a1c1e843b5213a87f7c544b2b042476ed7755be813eaf4e8347a", - "sha256:a7653d00b732afb6fc597e29c50ad28087dcb4fbfb28e86092277a559ae4e693", - "sha256:ade01303ccf7ae12c356a5e10911c9e1c51136003a9a1d92f7aa9d010fb98372", - "sha256:bac58c024c9922c23550af2a581998624d6e02350f4ae9c5f0bc642c633a2d5e", - "sha256:c92811b2d4c9b6ea0285942b2e7cac98a59e166d59c588fe5cfe1eda58e72d59", - "sha256:d1454bde93fb1e224166811694d600e746430c006fbb031ea06ecc2ea41bf156", - "sha256:d735786acc7dd25815e89cc4ad529a43af779db2e25aa7c626de864127e5a024", - "sha256:de80739447af31525feddeb8effd640782cf5998e1a4e9192ebdf829717e3913", - "sha256:ff432630e510709564c01dafdbe996cb552e0b9f3f065eb89bdce5bd31fabf4c" - ], - "version": "==2.9.9" + "version": "==6.1.0" }, "psycopg2-binary": { "hashes": [ - "sha256:03ef7df18daf2c4c07e2695e8cfd5ee7f748a1d54d802330985a78d2a5a6dca9", - "sha256:0a602ea5aff39bb9fac6308e9c9d82b9a35c2bf288e184a816002c9fae930b77", - "sha256:0c009475ee389757e6e34611d75f6e4f05f0cf5ebb76c6037508318e1a1e0d7e", - "sha256:0ef4854e82c09e84cc63084a9e4ccd6d9b154f1dbdd283efb92ecd0b5e2b8c84", - "sha256:1236ed0952fbd919c100bc839eaa4a39ebc397ed1c08a97fc45fee2a595aa1b3", - "sha256:143072318f793f53819048fdfe30c321890af0c3ec7cb1dfc9cc87aa88241de2", - "sha256:15208be1c50b99203fe88d15695f22a5bed95ab3f84354c494bcb1d08557df67", - "sha256:1873aade94b74715be2246321c8650cabf5a0d098a95bab81145ffffa4c13876", - "sha256:18d0ef97766055fec15b5de2c06dd8e7654705ce3e5e5eed3b6651a1d2a9a152", - "sha256:1ea665f8ce695bcc37a90ee52de7a7980be5161375d42a0b6c6abedbf0d81f0f", - "sha256:2293b001e319ab0d869d660a704942c9e2cce19745262a8aba2115ef41a0a42a", - "sha256:246b123cc54bb5361588acc54218c8c9fb73068bf227a4a531d8ed56fa3ca7d6", - "sha256:275ff571376626195ab95a746e6a04c7df8ea34638b99fc11160de91f2fef503", - "sha256:281309265596e388ef483250db3640e5f414168c5a67e9c665cafce9492eda2f", - "sha256:2d423c8d8a3c82d08fe8af900ad5b613ce3632a1249fd6a223941d0735fce493", - "sha256:2e5afae772c00980525f6d6ecf7cbca55676296b580c0e6abb407f15f3706996", - "sha256:30dcc86377618a4c8f3b72418df92e77be4254d8f89f14b8e8f57d6d43603c0f", - "sha256:31a34c508c003a4347d389a9e6fcc2307cc2150eb516462a7a17512130de109e", - "sha256:323ba25b92454adb36fa425dc5cf6f8f19f78948cbad2e7bc6cdf7b0d7982e59", - "sha256:34eccd14566f8fe14b2b95bb13b11572f7c7d5c36da61caf414d23b91fcc5d94", - "sha256:3a58c98a7e9c021f357348867f537017057c2ed7f77337fd914d0bedb35dace7", - "sha256:3f78fd71c4f43a13d342be74ebbc0666fe1f555b8837eb113cb7416856c79682", - "sha256:4154ad09dac630a0f13f37b583eae260c6aa885d67dfbccb5b02c33f31a6d420", - "sha256:420f9bbf47a02616e8554e825208cb947969451978dceb77f95ad09c37791dae", - "sha256:4686818798f9194d03c9129a4d9a702d9e113a89cb03bffe08c6cf799e053291", - "sha256:57fede879f08d23c85140a360c6a77709113efd1c993923c59fde17aa27599fe", - "sha256:60989127da422b74a04345096c10d416c2b41bd7bf2a380eb541059e4e999980", - "sha256:64cf30263844fa208851ebb13b0732ce674d8ec6a0c86a4e160495d299ba3c93", - "sha256:68fc1f1ba168724771e38bee37d940d2865cb0f562380a1fb1ffb428b75cb692", - "sha256:6e6f98446430fdf41bd36d4faa6cb409f5140c1c2cf58ce0bbdaf16af7d3f119", - "sha256:729177eaf0aefca0994ce4cffe96ad3c75e377c7b6f4efa59ebf003b6d398716", - "sha256:72dffbd8b4194858d0941062a9766f8297e8868e1dd07a7b36212aaa90f49472", - "sha256:75723c3c0fbbf34350b46a3199eb50638ab22a0228f93fb472ef4d9becc2382b", - "sha256:77853062a2c45be16fd6b8d6de2a99278ee1d985a7bd8b103e97e41c034006d2", - "sha256:78151aa3ec21dccd5cdef6c74c3e73386dcdfaf19bced944169697d7ac7482fc", - "sha256:7f01846810177d829c7692f1f5ada8096762d9172af1b1a28d4ab5b77c923c1c", - "sha256:804d99b24ad523a1fe18cc707bf741670332f7c7412e9d49cb5eab67e886b9b5", - "sha256:81ff62668af011f9a48787564ab7eded4e9fb17a4a6a74af5ffa6a457400d2ab", - "sha256:8359bf4791968c5a78c56103702000105501adb557f3cf772b2c207284273984", - "sha256:83791a65b51ad6ee6cf0845634859d69a038ea9b03d7b26e703f94c7e93dbcf9", - "sha256:8532fd6e6e2dc57bcb3bc90b079c60de896d2128c5d9d6f24a63875a95a088cf", - "sha256:876801744b0dee379e4e3c38b76fc89f88834bb15bf92ee07d94acd06ec890a0", - "sha256:8dbf6d1bc73f1d04ec1734bae3b4fb0ee3cb2a493d35ede9badbeb901fb40f6f", - "sha256:8f8544b092a29a6ddd72f3556a9fcf249ec412e10ad28be6a0c0d948924f2212", - "sha256:911dda9c487075abd54e644ccdf5e5c16773470a6a5d3826fda76699410066fb", - "sha256:977646e05232579d2e7b9c59e21dbe5261f403a88417f6a6512e70d3f8a046be", - "sha256:9dba73be7305b399924709b91682299794887cbbd88e38226ed9f6712eabee90", - "sha256:a148c5d507bb9b4f2030a2025c545fccb0e1ef317393eaba42e7eabd28eb6041", - "sha256:a6cdcc3ede532f4a4b96000b6362099591ab4a3e913d70bcbac2b56c872446f7", - "sha256:ac05fb791acf5e1a3e39402641827780fe44d27e72567a000412c648a85ba860", - "sha256:b0605eaed3eb239e87df0d5e3c6489daae3f7388d455d0c0b4df899519c6a38d", - "sha256:b58b4710c7f4161b5e9dcbe73bb7c62d65670a87df7bcce9e1faaad43e715245", - "sha256:b6356793b84728d9d50ead16ab43c187673831e9d4019013f1402c41b1db9b27", - "sha256:b76bedd166805480ab069612119ea636f5ab8f8771e640ae103e05a4aae3e417", - "sha256:bc7bb56d04601d443f24094e9e31ae6deec9ccb23581f75343feebaf30423359", - "sha256:c2470da5418b76232f02a2fcd2229537bb2d5a7096674ce61859c3229f2eb202", - "sha256:c332c8d69fb64979ebf76613c66b985414927a40f8defa16cf1bc028b7b0a7b0", - "sha256:c6af2a6d4b7ee9615cbb162b0738f6e1fd1f5c3eda7e5da17861eacf4c717ea7", - "sha256:c77e3d1862452565875eb31bdb45ac62502feabbd53429fdc39a1cc341d681ba", - "sha256:ca08decd2697fdea0aea364b370b1249d47336aec935f87b8bbfd7da5b2ee9c1", - "sha256:ca49a8119c6cbd77375ae303b0cfd8c11f011abbbd64601167ecca18a87e7cdd", - "sha256:cb16c65dcb648d0a43a2521f2f0a2300f40639f6f8c1ecbc662141e4e3e1ee07", - "sha256:d2997c458c690ec2bc6b0b7ecbafd02b029b7b4283078d3b32a852a7ce3ddd98", - "sha256:d3f82c171b4ccd83bbaf35aa05e44e690113bd4f3b7b6cc54d2219b132f3ae55", - "sha256:dc4926288b2a3e9fd7b50dc6a1909a13bbdadfc67d93f3374d984e56f885579d", - "sha256:ead20f7913a9c1e894aebe47cccf9dc834e1618b7aa96155d2091a626e59c972", - "sha256:ebdc36bea43063116f0486869652cb2ed7032dbc59fbcb4445c4862b5c1ecf7f", - "sha256:ed1184ab8f113e8d660ce49a56390ca181f2981066acc27cf637d5c1e10ce46e", - "sha256:ee825e70b1a209475622f7f7b776785bd68f34af6e7a46e2e42f27b659b5bc26", - "sha256:f7ae5d65ccfbebdfa761585228eb4d0df3a8b15cfb53bd953e713e09fbb12957", - "sha256:f7fc5a5acafb7d6ccca13bfa8c90f8c51f13d8fb87d95656d3950f0158d3ce53", - "sha256:f9b5571d33660d5009a8b3c25dc1db560206e2d2f89d3df1cb32d72c0d117d52" + "sha256:04392983d0bb89a8717772a193cfaac58871321e3ec69514e1c4e0d4957b5aff", + "sha256:056470c3dc57904bbf63d6f534988bafc4e970ffd50f6271fc4ee7daad9498a5", + "sha256:0ea8e3d0ae83564f2fc554955d327fa081d065c8ca5cc6d2abb643e2c9c1200f", + "sha256:155e69561d54d02b3c3209545fb08938e27889ff5a10c19de8d23eb5a41be8a5", + "sha256:18c5ee682b9c6dd3696dad6e54cc7ff3a1a9020df6a5c0f861ef8bfd338c3ca0", + "sha256:19721ac03892001ee8fdd11507e6a2e01f4e37014def96379411ca99d78aeb2c", + "sha256:1a6784f0ce3fec4edc64e985865c17778514325074adf5ad8f80636cd029ef7c", + "sha256:2286791ececda3a723d1910441c793be44625d86d1a4e79942751197f4d30341", + "sha256:230eeae2d71594103cd5b93fd29d1ace6420d0b86f4778739cb1a5a32f607d1f", + "sha256:245159e7ab20a71d989da00f280ca57da7641fa2cdcf71749c193cea540a74f7", + "sha256:26540d4a9a4e2b096f1ff9cce51253d0504dca5a85872c7f7be23be5a53eb18d", + "sha256:270934a475a0e4b6925b5f804e3809dd5f90f8613621d062848dd82f9cd62007", + "sha256:2ad26b467a405c798aaa1458ba09d7e2b6e5f96b1ce0ac15d82fd9f95dc38a92", + "sha256:2b3d2491d4d78b6b14f76881905c7a8a8abcf974aad4a8a0b065273a0ed7a2cb", + "sha256:2ce3e21dc3437b1d960521eca599d57408a695a0d3c26797ea0f72e834c7ffe5", + "sha256:30e34c4e97964805f715206c7b789d54a78b70f3ff19fbe590104b71c45600e5", + "sha256:3216ccf953b3f267691c90c6fe742e45d890d8272326b4a8b20850a03d05b7b8", + "sha256:32581b3020c72d7a421009ee1c6bf4a131ef5f0a968fab2e2de0c9d2bb4577f1", + "sha256:35958ec9e46432d9076286dda67942ed6d968b9c3a6a2fd62b48939d1d78bf68", + "sha256:3abb691ff9e57d4a93355f60d4f4c1dd2d68326c968e7db17ea96df3c023ef73", + "sha256:3c18f74eb4386bf35e92ab2354a12c17e5eb4d9798e4c0ad3a00783eae7cd9f1", + "sha256:3c4745a90b78e51d9ba06e2088a2fe0c693ae19cc8cb051ccda44e8df8a6eb53", + "sha256:3c4ded1a24b20021ebe677b7b08ad10bf09aac197d6943bfe6fec70ac4e4690d", + "sha256:3e9c76f0ac6f92ecfc79516a8034a544926430f7b080ec5a0537bca389ee0906", + "sha256:48b338f08d93e7be4ab2b5f1dbe69dc5e9ef07170fe1f86514422076d9c010d0", + "sha256:4b3df0e6990aa98acda57d983942eff13d824135fe2250e6522edaa782a06de2", + "sha256:512d29bb12608891e349af6a0cccedce51677725a921c07dba6342beaf576f9a", + "sha256:5a507320c58903967ef7384355a4da7ff3f28132d679aeb23572753cbf2ec10b", + "sha256:5c370b1e4975df846b0277b4deba86419ca77dbc25047f535b0bb03d1a544d44", + "sha256:6b269105e59ac96aba877c1707c600ae55711d9dcd3fc4b5012e4af68e30c648", + "sha256:6d4fa1079cab9018f4d0bd2db307beaa612b0d13ba73b5c6304b9fe2fb441ff7", + "sha256:6dc08420625b5a20b53551c50deae6e231e6371194fa0651dbe0fb206452ae1f", + "sha256:73aa0e31fa4bb82578f3a6c74a73c273367727de397a7a0f07bd83cbea696baa", + "sha256:7559bce4b505762d737172556a4e6ea8a9998ecac1e39b5233465093e8cee697", + "sha256:79625966e176dc97ddabc142351e0409e28acf4660b88d1cf6adb876d20c490d", + "sha256:7a813c8bdbaaaab1f078014b9b0b13f5de757e2b5d9be6403639b298a04d218b", + "sha256:7b2c956c028ea5de47ff3a8d6b3cc3330ab45cf0b7c3da35a2d6ff8420896526", + "sha256:7f4152f8f76d2023aac16285576a9ecd2b11a9895373a1f10fd9db54b3ff06b4", + "sha256:7f5d859928e635fa3ce3477704acee0f667b3a3d3e4bb109f2b18d4005f38287", + "sha256:851485a42dbb0bdc1edcdabdb8557c09c9655dfa2ca0460ff210522e073e319e", + "sha256:8608c078134f0b3cbd9f89b34bd60a943b23fd33cc5f065e8d5f840061bd0673", + "sha256:880845dfe1f85d9d5f7c412efea7a08946a46894537e4e5d091732eb1d34d9a0", + "sha256:8aabf1c1a04584c168984ac678a668094d831f152859d06e055288fa515e4d30", + "sha256:8aecc5e80c63f7459a1a2ab2c64df952051df196294d9f739933a9f6687e86b3", + "sha256:8cd9b4f2cfab88ed4a9106192de509464b75a906462fb846b936eabe45c2063e", + "sha256:8de718c0e1c4b982a54b41779667242bc630b2197948405b7bd8ce16bcecac92", + "sha256:9440fa522a79356aaa482aa4ba500b65f28e5d0e63b801abf6aa152a29bd842a", + "sha256:b5f86c56eeb91dc3135b3fd8a95dc7ae14c538a2f3ad77a19645cf55bab1799c", + "sha256:b73d6d7f0ccdad7bc43e6d34273f70d587ef62f824d7261c4ae9b8b1b6af90e8", + "sha256:bb89f0a835bcfc1d42ccd5f41f04870c1b936d8507c6df12b7737febc40f0909", + "sha256:c3cc28a6fd5a4a26224007712e79b81dbaee2ffb90ff406256158ec4d7b52b47", + "sha256:ce5ab4bf46a211a8e924d307c1b1fcda82368586a19d0a24f8ae166f5c784864", + "sha256:d00924255d7fc916ef66e4bf22f354a940c67179ad3fd7067d7a0a9c84d2fbfc", + "sha256:d7cd730dfa7c36dbe8724426bf5612798734bff2d3c3857f36f2733f5bfc7c00", + "sha256:e217ce4d37667df0bc1c397fdcd8de5e81018ef305aed9415c3b093faaeb10fb", + "sha256:e3923c1d9870c49a2d44f795df0c889a22380d36ef92440ff618ec315757e539", + "sha256:e5720a5d25e3b99cd0dc5c8a440570469ff82659bb09431c1439b92caf184d3b", + "sha256:e8b58f0a96e7a1e341fc894f62c1177a7c83febebb5ff9123b579418fdc8a481", + "sha256:e984839e75e0b60cfe75e351db53d6db750b00de45644c5d1f7ee5d1f34a1ce5", + "sha256:eb09aa7f9cecb45027683bb55aebaaf45a0df8bf6de68801a6afdc7947bb09d4", + "sha256:ec8a77f521a17506a24a5f626cb2aee7850f9b69a0afe704586f63a464f3cd64", + "sha256:ecced182e935529727401b24d76634a357c71c9275b356efafd8a2a91ec07392", + "sha256:ee0e8c683a7ff25d23b55b11161c2663d4b099770f6085ff0a20d4505778d6b4", + "sha256:f0c2d907a1e102526dd2986df638343388b94c33860ff3bbe1384130828714b1", + "sha256:f758ed67cab30b9a8d2833609513ce4d3bd027641673d4ebc9c067e4d208eec1", + "sha256:f8157bed2f51db683f31306aa497311b560f2265998122abe1dce6428bd86567", + "sha256:ffe8ed017e4ed70f68b7b371d84b7d4a790368db9203dfc2d222febd3a9c8863" ], "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==2.9.9" + "markers": "python_version >= '3.8'", + "version": "==2.9.10" }, "ptyprocess": { "hashes": [ @@ -2251,46 +1844,52 @@ }, "pyarrow": { "hashes": [ - "sha256:0071ce35788c6f9077ff9ecba4858108eebe2ea5a3f7cf2cf55ebc1dbc6ee24a", - "sha256:02dae06ce212d8b3244dd3e7d12d9c4d3046945a5933d28026598e9dbbda1fca", - "sha256:0b72e87fe3e1db343995562f7fff8aee354b55ee83d13afba65400c178ab2597", - "sha256:0cdb0e627c86c373205a2f94a510ac4376fdc523f8bb36beab2e7f204416163c", - "sha256:13d7a460b412f31e4c0efa1148e1d29bdf18ad1411eb6757d38f8fbdcc8645fb", - "sha256:1c8856e2ef09eb87ecf937104aacfa0708f22dfeb039c363ec99735190ffb977", - "sha256:2e19f569567efcbbd42084e87f948778eb371d308e137a0f97afe19bb860ccb3", - "sha256:32503827abbc5aadedfa235f5ece8c4f8f8b0a3cf01066bc8d29de7539532687", - "sha256:392bc9feabc647338e6c89267635e111d71edad5fcffba204425a7c8d13610d7", - "sha256:42bf93249a083aca230ba7e2786c5f673507fa97bbd9725a1e2754715151a204", - "sha256:4beca9521ed2c0921c1023e68d097d0299b62c362639ea315572a58f3f50fd28", - "sha256:5984f416552eea15fd9cee03da53542bf4cddaef5afecefb9aa8d1010c335087", - "sha256:6b244dc8e08a23b3e352899a006a26ae7b4d0da7bb636872fa8f5884e70acf15", - "sha256:757074882f844411fcca735e39aae74248a1531367a7c80799b4266390ae51cc", - "sha256:75c06d4624c0ad6674364bb46ef38c3132768139ddec1c56582dbac54f2663e2", - "sha256:7c7916bff914ac5d4a8fe25b7a25e432ff921e72f6f2b7547d1e325c1ad9d155", - "sha256:9b564a51fbccfab5a04a80453e5ac6c9954a9c5ef2890d1bcf63741909c3f8df", - "sha256:9b8a823cea605221e61f34859dcc03207e52e409ccf6354634143e23af7c8d22", - "sha256:9ba11c4f16976e89146781a83833df7f82077cdab7dc6232c897789343f7891a", - "sha256:a155acc7f154b9ffcc85497509bcd0d43efb80d6f733b0dc3bb14e281f131c8b", - "sha256:a27532c38f3de9eb3e90ecab63dfda948a8ca859a66e3a47f5f42d1e403c4d03", - "sha256:a48ddf5c3c6a6c505904545c25a4ae13646ae1f8ba703c4df4a1bfe4f4006bda", - "sha256:a5c8b238d47e48812ee577ee20c9a2779e6a5904f1708ae240f53ecbee7c9f07", - "sha256:af5ff82a04b2171415f1410cff7ebb79861afc5dae50be73ce06d6e870615204", - "sha256:b0c6ac301093b42d34410b187bba560b17c0330f64907bfa4f7f7f2444b0cf9b", - "sha256:d7d192305d9d8bc9082d10f361fc70a73590a4c65cf31c3e6926cd72b76bc35c", - "sha256:da1e060b3876faa11cee287839f9cc7cdc00649f475714b8680a05fd9071d545", - "sha256:db023dc4c6cae1015de9e198d41250688383c3f9af8f565370ab2b4cb5f62655", - "sha256:dc5c31c37409dfbc5d014047817cb4ccd8c1ea25d19576acf1a001fe07f5b420", - "sha256:dec8d129254d0188a49f8a1fc99e0560dc1b85f60af729f47de4046015f9b0a5", - "sha256:e3343cb1e88bc2ea605986d4b94948716edc7a8d14afd4e2c097232f729758b4", - "sha256:edca18eaca89cd6382dfbcff3dd2d87633433043650c07375d095cd3517561d8", - "sha256:f1e70de6cb5790a50b01d2b686d54aaf73da01266850b05e3af2a1bc89e16053", - "sha256:f553ca691b9e94b202ff741bdd40f6ccb70cdd5fbf65c187af132f1317de6145", - "sha256:f7ae2de664e0b158d1607699a16a488de3d008ba99b3a7aa5de1cbc13574d047", - "sha256:fa3c246cc58cb5a4a5cb407a18f193354ea47dd0648194e6265bd24177982fe8" + "sha256:01c034b576ce0eef554f7c3d8c341714954be9b3f5d5bc7117006b85fcf302fe", + "sha256:05a5636ec3eb5cc2a36c6edb534a38ef57b2ab127292a716d00eabb887835f1e", + "sha256:0743e503c55be0fdb5c08e7d44853da27f19dc854531c0570f9f394ec9671d54", + "sha256:0ad4892617e1a6c7a551cfc827e072a633eaff758fa09f21c4ee548c30bcaf99", + "sha256:0b331e477e40f07238adc7ba7469c36b908f07c89b95dd4bd3a0ec84a3d1e21e", + "sha256:11b676cd410cf162d3f6a70b43fb9e1e40affbc542a1e9ed3681895f2962d3d9", + "sha256:25dbacab8c5952df0ca6ca0af28f50d45bd31c1ff6fcf79e2d120b4a65ee7181", + "sha256:2c4dd0c9010a25ba03e198fe743b1cc03cd33c08190afff371749c52ccbbaf76", + "sha256:36ac22d7782554754a3b50201b607d553a8d71b78cdf03b33c1125be4b52397c", + "sha256:3b2e2239339c538f3464308fd345113f886ad031ef8266c6f004d49769bb074c", + "sha256:3c35813c11a059056a22a3bef520461310f2f7eea5c8a11ef9de7062a23f8d56", + "sha256:4a4813cb8ecf1809871fd2d64a8eff740a1bd3691bbe55f01a3cf6c5ec869754", + "sha256:4f443122c8e31f4c9199cb23dca29ab9427cef990f283f80fe15b8e124bcc49b", + "sha256:4f97b31b4c4e21ff58c6f330235ff893cc81e23da081b1a4b1c982075e0ed4e9", + "sha256:543ad8459bc438efc46d29a759e1079436290bd583141384c6f7a1068ed6f992", + "sha256:6a276190309aba7bc9d5bd2933230458b3521a4317acfefe69a354f2fe59f2bc", + "sha256:73eeed32e724ea3568bb06161cad5fa7751e45bc2228e33dcb10c614044165c7", + "sha256:74de649d1d2ccb778f7c3afff6085bd5092aed4c23df9feeb45dd6b16f3811aa", + "sha256:84e314d22231357d473eabec709d0ba285fa706a72377f9cc8e1cb3c8013813b", + "sha256:9386d3ca9c145b5539a1cfc75df07757dff870168c959b473a0bccbc3abc8c73", + "sha256:9736ba3c85129d72aefa21b4f3bd715bc4190fe4426715abfff90481e7d00812", + "sha256:9f3a76670b263dc41d0ae877f09124ab96ce10e4e48f3e3e4257273cee61ad0d", + "sha256:a1880dd6772b685e803011a6b43a230c23b566859a6e0c9a276c1e0faf4f4052", + "sha256:acb7564204d3c40babf93a05624fc6a8ec1ab1def295c363afc40b0c9e66c191", + "sha256:ad514dbfcffe30124ce655d72771ae070f30bf850b48bc4d9d3b25993ee0e386", + "sha256:aebc13a11ed3032d8dd6e7171eb6e86d40d67a5639d96c35142bd568b9299324", + "sha256:b516dad76f258a702f7ca0250885fc93d1fa5ac13ad51258e39d402bd9e2e1e4", + "sha256:b76130d835261b38f14fc41fdfb39ad8d672afb84c447126b84d5472244cfaba", + "sha256:ba17845efe3aa358ec266cf9cc2800fa73038211fb27968bfa88acd09261a470", + "sha256:c0a03da7f2758645d17b7b4f83c8bffeae5bbb7f974523fe901f36288d2eab71", + "sha256:c52f81aa6f6575058d8e2c782bf79d4f9fdc89887f16825ec3a66607a5dd8e30", + "sha256:d4b3d2a34780645bed6414e22dda55a92e0fcd1b8a637fba86800ad737057e33", + "sha256:d4f13eee18433f99adefaeb7e01d83b59f73360c231d4782d9ddfaf1c3fbde0a", + "sha256:d6cf5c05f3cee251d80e98726b5c7cc9f21bab9e9783673bac58e6dfab57ecc8", + "sha256:da31fbca07c435be88a0c321402c4e31a2ba61593ec7473630769de8346b54ee", + "sha256:e21488d5cfd3d8b500b3238a6c4b075efabc18f0f6d80b29239737ebd69caa6c", + "sha256:e31e9417ba9c42627574bdbfeada7217ad8a4cbbe45b9d6bdd4b62abbca4c6f6", + "sha256:eaeabf638408de2772ce3d7793b2668d4bb93807deed1725413b70e3156a7854", + "sha256:f266a2c0fc31995a06ebd30bcfdb7f615d7278035ec5b1cd71c48d56daaf30b0", + "sha256:f39a2e0ed32a0970e4e46c262753417a60c43a3246972cfc2d3eb85aedd01b21", + "sha256:f591704ac05dfd0477bb8f8e0bd4b5dc52c1cadf50503858dce3a15db6e46ff2", + "sha256:f96bd502cb11abb08efea6dab09c003305161cb6c9eafd432e35e76e7fa9b90c" ], "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==17.0.0" + "markers": "python_version >= '3.9'", + "version": "==18.1.0" }, "pyasn1": { "hashes": [ @@ -2428,15 +2027,6 @@ "markers": "python_version >= '3.8'", "version": "==2.18.0" }, - "pylint": { - "hashes": [ - "sha256:2f846a466dd023513240bc140ad2dd73bfc080a5d85a710afdb728c420a5a2b9", - "sha256:9f3dcc87b1203e612b78d91a896407787e708b3f189b5fa0b307712d49ff0c6e" - ], - "index": "pypi", - "markers": "python_full_version >= '3.9.0'", - "version": "==3.3.1" - }, "pyogrio": { "hashes": [ "sha256:02e54bcfb305af75f829044b0045f74de31b77c2d6546f7aaf96822066147848", @@ -2476,11 +2066,11 @@ }, "pyparsing": { "hashes": [ - "sha256:a6a7ee4235a3f944aa1fa2249307708f893fe5717dc603503c6c7969c070fb7c", - "sha256:f86ec8d1a83f11977c9a6ea7598e8c27fc5cddfa5b07ea2241edbbde1d7bc032" + "sha256:93d9577b88da0bbea8cc8334ee8b918ed014968fd2ec383e868fb8afb1ccef84", + "sha256:cbf74e27246d595d9a74b186b810f6fbb86726dbf3b9532efb343f6d7294fe9c" ], - "markers": "python_full_version >= '3.6.8'", - "version": "==3.1.4" + "markers": "python_version >= '3.9'", + "version": "==3.2.0" }, "pyproj": { "hashes": [ @@ -2488,6 +2078,7 @@ "sha256:0dd31b0740ee010934234f848d2d092c66146cb8d0ba009a64e41d192caa7686", "sha256:10a8dc6ec61af97c89ff032647d743f8dc023645773da42ef43f7ae1125b3509", "sha256:24fa4e9e0abba875f9524808410cc520067eaf38fd5549ed0ef7c43ac39923c9", + "sha256:2c54e9bdda7ab9c4a5af50f9d6e6ee7704e05fafd504896b96ed1208c7aea098", "sha256:34e1bbb3f89c68d4a6835c40b2da8b27680eec60e8cc7cdb08c09bcc725b2b62", "sha256:38cba7c4c5679e40242dd959133e95b908d3b912dd66291094fd13510e8517ff", "sha256:448958c46bd3fe2da91c89ba551ac5835e63073ca861422c6eb1af89979dfab1", @@ -2512,15 +2103,6 @@ "markers": "python_version >= '3.10'", "version": "==3.7.0" }, - "pytest": { - "hashes": [ - "sha256:70b98107bd648308a7952b06e6ca9a50bc660be218d53c257cc1fc94fda10181", - "sha256:a6853c7375b2663155079443d2e45de913a911a11d669df02a50814944db57b2" - ], - "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==8.3.3" - }, "python-dateutil": { "hashes": [ "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", @@ -2537,21 +2119,6 @@ "markers": "python_version >= '3.6'", "version": "==2.0.7" }, - "python-slugify": { - "hashes": [ - "sha256:276540b79961052b66b7d116620b36518847f52d5fd9e3a70164fc8c50faa6b8", - "sha256:59202371d1d05b54a9e7720c5e038f928f45daaffe41dd10822f3907b937c856" - ], - "markers": "python_version >= '3.7'", - "version": "==8.0.4" - }, - "pytimeparse": { - "hashes": [ - "sha256:04b7be6cc8bd9f5647a6325444926c3ac34ee6bc7e69da4367ba282f076036bd", - "sha256:e86136477be924d7e670646a98561957e8ca7308d44841e21f5ddea757556a0a" - ], - "version": "==1.1.8" - }, "pytz": { "hashes": [ "sha256:2aa355083c50a0f93fa581709deac0c9ad65cca8a9e9beac660adcbd493c798a", @@ -2733,41 +2300,33 @@ "markers": "python_version >= '3.7'", "version": "==26.2.0" }, - "radon": { - "hashes": [ - "sha256:632cc032364a6f8bb1010a2f6a12d0f14bc7e5ede76585ef29dc0cecf4cd8859", - "sha256:d1ac0053943a893878940fedc8b19ace70386fc9c9bf0a09229a44125ebf45b5" - ], - "index": "pypi", - "version": "==6.0.1" - }, "rasterio": { "hashes": [ - "sha256:03f0d04ed18b4311ec807fe00fff85a9c744cb340e494dbb208ed1dc39dc1cd4", - "sha256:0c2f21b4fe0f6f8af0a902c4f86fb51d25a058fabf4dada1d453f8dce6be7938", - "sha256:0f0903fe08724e99b7404c8069474eab08c50b06c3436476f8fa63e381bd0a36", - "sha256:22a7222911e96a77a5b09f1ea444cff574cc704872801b685edf5057e016ff47", - "sha256:2466adf39009115de23209a4b343fdb0db9a3aef97119d826d09df26b7f3beb9", - "sha256:2e6a8fbdf31053a3ff95c138976ffc8ee547f361326cb42bd26fcee26422e157", - "sha256:33be7bc0e50c1a27a47faaac934d98275aea68db14d045130303bbd32db34d96", - "sha256:5037188f3582cdb21a308d6cacfc6a4f2828cd05d382f8b848c8bb405a0778b4", - "sha256:50fb06c2895cc055f34d813600cb5dc56b170d02c216913fdf80b573d771e972", - "sha256:5b85f7d912b868ce8dd25e01d5042c8426cc9208a715f093081503ee864e870b", - "sha256:6832fb300615e5d8649346b4aad557330cfae23bc5499832d106c415cc85173b", - "sha256:919c3fada8825b385fa057f9dfdde502b10f65b940ce3299cc932252edb1c57b", - "sha256:a0cd7b8eda7d3b0097a42a047d2604c3f1f27b4093e2a4e1e7ad379c9f639f65", - "sha256:af5e0559eb8da3cf0adbc1d26ce86e505ea82814012b7b6711855674ae3f6425", - "sha256:b30c4f60951e70f25cca03c8c127ff1cb6eb4670c551d8561cb71f8cd5aaad3f", - "sha256:b53e08a86d9180d87021b0ea0d5d4eacf7c252adfaa85dafcd8f1e3daf89dbf9", - "sha256:c5b626dc0c08dc593e462265ce974266f3e1dd90fa67807bef33704d88c62a28", - "sha256:d750362bb792d2311f94803ff309baec48486ecba75c9b905ea9b1f5eb06ef9f", - "sha256:e4a89441afa7cbd2f5df4e822b0d9f581971f805fa6884b59911cf238bdfa9b6", - "sha256:e7b18c4ccf0d813b6056704af86e14d6841e29ec558a9bce2f1ce2e3ee2ef70d", - "sha256:ef99c77d2c52bd95b42ff862c252a74c7528ec3b3cbb665c783e3948ebd811cf" + "sha256:1839960e2f3057a6daa323ccf67b330f8f2f0dbd4a50cc7031e88e649301c5c0", + "sha256:1a6e6ca9ec361599b48c9918ce25adb1a9203b8c8ca9b34ad78dccb3aef7945a", + "sha256:201f05dbc7c4739dacb2c78a1cf4e09c0b7265b0a4d16ccbd1753ce4f2af350a", + "sha256:38a126f8dbf405cd3450b5bd10c6cc493a2e1be4cf83442d26f5e4f412372d36", + "sha256:3f411a6a5bcb81ab6dc9128a8bccd13d3822cfa4a50c239e3a0528751a1ad5fc", + "sha256:4009f7ce4e0883d8e5b400970daa3f1ca309caac8916d955722ee4486654d452", + "sha256:54eef32d20a0dfbba59a8bb9828e562c3e9e97e2355b8dfe4a5274117976059f", + "sha256:597f8dcf494d0ca4254434496e83b1723fec206d23d64da5751a582a2b01e1d3", + "sha256:5b8a4311582274de2346450e5361d092b80b8b5c7b02fda6203402ba101ffabf", + "sha256:5d4fcb635379b3d7b2f5e944c153849e3d27e93f35ad73ad4d3f0b8a580f0c8e", + "sha256:80f994b92e5dda78f13291710bd5c43efcfd164f69a8a2c20489115df9d178c8", + "sha256:812c854e7177064aeb58def2d59752887ad6b3d39ff3f858ed9df3f2ddc95613", + "sha256:8e90c2c300294265c16becc9822337ded0f01fb8664500b4d77890d633d8cd0e", + "sha256:98a9c89eade8c779e8ac1e525269faaa18c6b9818fc3c72cfc4627df71c66d0d", + "sha256:9c30114d95ebba4ff49f078b3c193d29ff56d832588649400a3fa78f1dda1c96", + "sha256:a702e21712ba237e34515d829847f9f5f06d8e665e864a7bb0a3d4d8f6dec10d", + "sha256:a962ad4c29feaf38b1d7a94389313127de3646a5b9b734fbf9a04e16051a27ff", + "sha256:af04f788f6f814569184bd9da6c5d9889512212385ab58c52720dfb1f972671d", + "sha256:d9bab1a0bb22b8bed1db34b5258db93d790ed4e61ef21ac055a7c6933c8d5e84", + "sha256:e703e4b2c74c678786d5d110a3f30e26f3acfd65f09ccf35f69683a532f7a772", + "sha256:e79847a5a0e01399457a1e02d8c92040cb56729d054fe7796f0c17b246b18bf0" ], "index": "pypi", "markers": "python_version >= '3.9'", - "version": "==1.4.1" + "version": "==1.4.3" }, "referencing": { "hashes": [ @@ -2802,122 +2361,114 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", "version": "==0.1.1" }, - "rich": { - "hashes": [ - "sha256:097cffdf85db1babe30cc7deba5ab3a29e1b9885047dab24c57e9a7f8a9c1466", - "sha256:b340e739f30aa58921dc477b8adaa9ecdb7cecc217be01d93730ee1bc8aa83be" - ], - "markers": "python_full_version >= '3.8.0'", - "version": "==13.9.1" - }, "rpds-py": { "hashes": [ - "sha256:06db23d43f26478303e954c34c75182356ca9aa7797d22c5345b16871ab9c45c", - "sha256:0e13e6952ef264c40587d510ad676a988df19adea20444c2b295e536457bc585", - "sha256:11ef6ce74616342888b69878d45e9f779b95d4bd48b382a229fe624a409b72c5", - "sha256:1259c7b3705ac0a0bd38197565a5d603218591d3f6cee6e614e380b6ba61c6f6", - "sha256:18d7585c463087bddcfa74c2ba267339f14f2515158ac4db30b1f9cbdb62c8ef", - "sha256:1e0f80b739e5a8f54837be5d5c924483996b603d5502bfff79bf33da06164ee2", - "sha256:1e5f3cd7397c8f86c8cc72d5a791071431c108edd79872cdd96e00abd8497d29", - "sha256:220002c1b846db9afd83371d08d239fdc865e8f8c5795bbaec20916a76db3318", - "sha256:22e6c9976e38f4d8c4a63bd8a8edac5307dffd3ee7e6026d97f3cc3a2dc02a0b", - "sha256:238a2d5b1cad28cdc6ed15faf93a998336eb041c4e440dd7f902528b8891b399", - "sha256:2580b0c34583b85efec8c5c5ec9edf2dfe817330cc882ee972ae650e7b5ef739", - "sha256:28527c685f237c05445efec62426d285e47a58fb05ba0090a4340b73ecda6dee", - "sha256:2cf126d33a91ee6eedc7f3197b53e87a2acdac63602c0f03a02dd69e4b138174", - "sha256:338ca4539aad4ce70a656e5187a3a31c5204f261aef9f6ab50e50bcdffaf050a", - "sha256:39ed0d010457a78f54090fafb5d108501b5aa5604cc22408fc1c0c77eac14344", - "sha256:3ad0fda1635f8439cde85c700f964b23ed5fc2d28016b32b9ee5fe30da5c84e2", - "sha256:3d2b1ad682a3dfda2a4e8ad8572f3100f95fad98cb99faf37ff0ddfe9cbf9d03", - "sha256:3d61339e9f84a3f0767b1995adfb171a0d00a1185192718a17af6e124728e0f5", - "sha256:3fde368e9140312b6e8b6c09fb9f8c8c2f00999d1823403ae90cc00480221b22", - "sha256:40ce74fc86ee4645d0a225498d091d8bc61f39b709ebef8204cb8b5a464d3c0e", - "sha256:49a8063ea4296b3a7e81a5dfb8f7b2d73f0b1c20c2af401fb0cdf22e14711a96", - "sha256:4a1f1d51eccb7e6c32ae89243cb352389228ea62f89cd80823ea7dd1b98e0b91", - "sha256:4b16aa0107ecb512b568244ef461f27697164d9a68d8b35090e9b0c1c8b27752", - "sha256:4f1ed4749a08379555cebf4650453f14452eaa9c43d0a95c49db50c18b7da075", - "sha256:4fe84294c7019456e56d93e8ababdad5a329cd25975be749c3f5f558abb48253", - "sha256:50eccbf054e62a7b2209b28dc7a22d6254860209d6753e6b78cfaeb0075d7bee", - "sha256:514b3293b64187172bc77c8fb0cdae26981618021053b30d8371c3a902d4d5ad", - "sha256:54b43a2b07db18314669092bb2de584524d1ef414588780261e31e85846c26a5", - "sha256:55fea87029cded5df854ca7e192ec7bdb7ecd1d9a3f63d5c4eb09148acf4a7ce", - "sha256:569b3ea770c2717b730b61998b6c54996adee3cef69fc28d444f3e7920313cf7", - "sha256:56e27147a5a4c2c21633ff8475d185734c0e4befd1c989b5b95a5d0db699b21b", - "sha256:57eb94a8c16ab08fef6404301c38318e2c5a32216bf5de453e2714c964c125c8", - "sha256:5a35df9f5548fd79cb2f52d27182108c3e6641a4feb0f39067911bf2adaa3e57", - "sha256:5a8c94dad2e45324fc74dce25e1645d4d14df9a4e54a30fa0ae8bad9a63928e3", - "sha256:5b4f105deeffa28bbcdff6c49b34e74903139afa690e35d2d9e3c2c2fba18cec", - "sha256:5c1dc0f53856b9cc9a0ccca0a7cc61d3d20a7088201c0937f3f4048c1718a209", - "sha256:614fdafe9f5f19c63ea02817fa4861c606a59a604a77c8cdef5aa01d28b97921", - "sha256:617c7357272c67696fd052811e352ac54ed1d9b49ab370261a80d3b6ce385045", - "sha256:65794e4048ee837494aea3c21a28ad5fc080994dfba5b036cf84de37f7ad5074", - "sha256:6632f2d04f15d1bd6fe0eedd3b86d9061b836ddca4c03d5cf5c7e9e6b7c14580", - "sha256:6c8ef2ebf76df43f5750b46851ed1cdf8f109d7787ca40035fe19fbdc1acc5a7", - "sha256:758406267907b3781beee0f0edfe4a179fbd97c0be2e9b1154d7f0a1279cf8e5", - "sha256:7e60cb630f674a31f0368ed32b2a6b4331b8350d67de53c0359992444b116dd3", - "sha256:89c19a494bf3ad08c1da49445cc5d13d8fefc265f48ee7e7556839acdacf69d0", - "sha256:8a86a9b96070674fc88b6f9f71a97d2c1d3e5165574615d1f9168ecba4cecb24", - "sha256:8bc7690f7caee50b04a79bf017a8d020c1f48c2a1077ffe172abec59870f1139", - "sha256:8d7919548df3f25374a1f5d01fbcd38dacab338ef5f33e044744b5c36729c8db", - "sha256:9426133526f69fcaba6e42146b4e12d6bc6c839b8b555097020e2b78ce908dcc", - "sha256:9824fb430c9cf9af743cf7aaf6707bf14323fb51ee74425c380f4c846ea70789", - "sha256:9bb4a0d90fdb03437c109a17eade42dfbf6190408f29b2744114d11586611d6f", - "sha256:9bc2d153989e3216b0559251b0c260cfd168ec78b1fac33dd485750a228db5a2", - "sha256:9d35cef91e59ebbeaa45214861874bc6f19eb35de96db73e467a8358d701a96c", - "sha256:a1862d2d7ce1674cffa6d186d53ca95c6e17ed2b06b3f4c476173565c862d232", - "sha256:a84ab91cbe7aab97f7446652d0ed37d35b68a465aeef8fc41932a9d7eee2c1a6", - "sha256:aa7f429242aae2947246587d2964fad750b79e8c233a2367f71b554e9447949c", - "sha256:aa9a0521aeca7d4941499a73ad7d4f8ffa3d1affc50b9ea11d992cd7eff18a29", - "sha256:ac2f4f7a98934c2ed6505aead07b979e6f999389f16b714448fb39bbaa86a489", - "sha256:ae94bd0b2f02c28e199e9bc51485d0c5601f58780636185660f86bf80c89af94", - "sha256:af0fc424a5842a11e28956e69395fbbeab2c97c42253169d87e90aac2886d751", - "sha256:b2a5db5397d82fa847e4c624b0c98fe59d2d9b7cf0ce6de09e4d2e80f8f5b3f2", - "sha256:b4c29cbbba378759ac5786730d1c3cb4ec6f8ababf5c42a9ce303dc4b3d08cda", - "sha256:b74b25f024b421d5859d156750ea9a65651793d51b76a2e9238c05c9d5f203a9", - "sha256:b7f19250ceef892adf27f0399b9e5afad019288e9be756d6919cb58892129f51", - "sha256:b80d4a7900cf6b66bb9cee5c352b2d708e29e5a37fe9bf784fa97fc11504bf6c", - "sha256:b8c00a3b1e70c1d3891f0db1b05292747f0dbcfb49c43f9244d04c70fbc40eb8", - "sha256:bb273176be34a746bdac0b0d7e4e2c467323d13640b736c4c477881a3220a989", - "sha256:c3c20f0ddeb6e29126d45f89206b8291352b8c5b44384e78a6499d68b52ae511", - "sha256:c3e130fd0ec56cb76eb49ef52faead8ff09d13f4527e9b0c400307ff72b408e1", - "sha256:c52d3f2f82b763a24ef52f5d24358553e8403ce05f893b5347098014f2d9eff2", - "sha256:c6377e647bbfd0a0b159fe557f2c6c602c159fc752fa316572f012fc0bf67150", - "sha256:c638144ce971df84650d3ed0096e2ae7af8e62ecbbb7b201c8935c370df00a2c", - "sha256:ce9845054c13696f7af7f2b353e6b4f676dab1b4b215d7fe5e05c6f8bb06f965", - "sha256:cf258ede5bc22a45c8e726b29835b9303c285ab46fc7c3a4cc770736b5304c9f", - "sha256:d0a26ffe9d4dd35e4dfdd1e71f46401cff0181c75ac174711ccff0459135fa58", - "sha256:d0b67d87bb45ed1cd020e8fbf2307d449b68abc45402fe1a4ac9e46c3c8b192b", - "sha256:d20277fd62e1b992a50c43f13fbe13277a31f8c9f70d59759c88f644d66c619f", - "sha256:d454b8749b4bd70dd0a79f428731ee263fa6995f83ccb8bada706e8d1d3ff89d", - "sha256:d4c7d1a051eeb39f5c9547e82ea27cbcc28338482242e3e0b7768033cb083821", - "sha256:d72278a30111e5b5525c1dd96120d9e958464316f55adb030433ea905866f4de", - "sha256:d72a210824facfdaf8768cf2d7ca25a042c30320b3020de2fa04640920d4e121", - "sha256:d807dc2051abe041b6649681dce568f8e10668e3c1c6543ebae58f2d7e617855", - "sha256:dbe982f38565bb50cb7fb061ebf762c2f254ca3d8c20d4006878766e84266272", - "sha256:dcedf0b42bcb4cfff4101d7771a10532415a6106062f005ab97d1d0ab5681c60", - "sha256:deb62214c42a261cb3eb04d474f7155279c1a8a8c30ac89b7dcb1721d92c3c02", - "sha256:def7400461c3a3f26e49078302e1c1b38f6752342c77e3cf72ce91ca69fb1bc1", - "sha256:df3de6b7726b52966edf29663e57306b23ef775faf0ac01a3e9f4012a24a4140", - "sha256:e1940dae14e715e2e02dfd5b0f64a52e8374a517a1e531ad9412319dc3ac7879", - "sha256:e4df1e3b3bec320790f699890d41c59d250f6beda159ea3c44c3f5bac1976940", - "sha256:e6900ecdd50ce0facf703f7a00df12374b74bbc8ad9fe0f6559947fb20f82364", - "sha256:ea438162a9fcbee3ecf36c23e6c68237479f89f962f82dae83dc15feeceb37e4", - "sha256:eb851b7df9dda52dc1415ebee12362047ce771fc36914586b2e9fcbd7d293b3e", - "sha256:ec31a99ca63bf3cd7f1a5ac9fe95c5e2d060d3c768a09bc1d16e235840861420", - "sha256:f0475242f447cc6cb8a9dd486d68b2ef7fbee84427124c232bff5f63b1fe11e5", - "sha256:f2fbf7db2012d4876fb0d66b5b9ba6591197b0f165db8d99371d976546472a24", - "sha256:f60012a73aa396be721558caa3a6fd49b3dd0033d1675c6d59c4502e870fcf0c", - "sha256:f8e604fe73ba048c06085beaf51147eaec7df856824bfe7b98657cf436623daf", - "sha256:f90a4cd061914a60bd51c68bcb4357086991bd0bb93d8aa66a6da7701370708f", - "sha256:f918a1a130a6dfe1d7fe0f105064141342e7dd1611f2e6a21cd2f5c8cb1cfb3e", - "sha256:fa518bcd7600c584bf42e6617ee8132869e877db2f76bcdc281ec6a4113a53ab", - "sha256:faefcc78f53a88f3076b7f8be0a8f8d35133a3ecf7f3770895c25f8813460f08", - "sha256:fcaeb7b57f1a1e071ebd748984359fef83ecb026325b9d4ca847c95bc7311c92", - "sha256:fd2d84f40633bc475ef2d5490b9c19543fbf18596dcb1b291e3a12ea5d722f7a", - "sha256:fdfc3a892927458d98f3d55428ae46b921d1f7543b89382fdb483f5640daaec8" - ], - "markers": "python_version >= '3.8'", - "version": "==0.20.0" + "sha256:009de23c9c9ee54bf11303a966edf4d9087cd43a6003672e6aa7def643d06518", + "sha256:02fbb9c288ae08bcb34fb41d516d5eeb0455ac35b5512d03181d755d80810059", + "sha256:0a0461200769ab3b9ab7e513f6013b7a97fdeee41c29b9db343f3c5a8e2b9e61", + "sha256:0b09865a9abc0ddff4e50b5ef65467cd94176bf1e0004184eb915cbc10fc05c5", + "sha256:0b8db6b5b2d4491ad5b6bdc2bc7c017eec108acbf4e6785f42a9eb0ba234f4c9", + "sha256:0c150c7a61ed4a4f4955a96626574e9baf1adf772c2fb61ef6a5027e52803543", + "sha256:0f3cec041684de9a4684b1572fe28c7267410e02450f4561700ca5a3bc6695a2", + "sha256:1352ae4f7c717ae8cba93421a63373e582d19d55d2ee2cbb184344c82d2ae55a", + "sha256:177c7c0fce2855833819c98e43c262007f42ce86651ffbb84f37883308cb0e7d", + "sha256:1978d0021e943aae58b9b0b196fb4895a25cc53d3956b8e35e0b7682eefb6d56", + "sha256:1a60bce91f81ddaac922a40bbb571a12c1070cb20ebd6d49c48e0b101d87300d", + "sha256:1aef18820ef3e4587ebe8b3bc9ba6e55892a6d7b93bac6d29d9f631a3b4befbd", + "sha256:1e9663daaf7a63ceccbbb8e3808fe90415b0757e2abddbfc2e06c857bf8c5e2b", + "sha256:20070c65396f7373f5df4005862fa162db5d25d56150bddd0b3e8214e8ef45b4", + "sha256:214b7a953d73b5e87f0ebece4a32a5bd83c60a3ecc9d4ec8f1dca968a2d91e99", + "sha256:22bebe05a9ffc70ebfa127efbc429bc26ec9e9b4ee4d15a740033efda515cf3d", + "sha256:24e8abb5878e250f2eb0d7859a8e561846f98910326d06c0d51381fed59357bd", + "sha256:26fd7cac7dd51011a245f29a2cc6489c4608b5a8ce8d75661bb4a1066c52dfbe", + "sha256:27b1d3b3915a99208fee9ab092b8184c420f2905b7d7feb4aeb5e4a9c509b8a1", + "sha256:27e98004595899949bd7a7b34e91fa7c44d7a97c40fcaf1d874168bb652ec67e", + "sha256:2b8f60e1b739a74bab7e01fcbe3dddd4657ec685caa04681df9d562ef15b625f", + "sha256:2de29005e11637e7a2361fa151f780ff8eb2543a0da1413bb951e9f14b699ef3", + "sha256:2e8b55d8517a2fda8d95cb45d62a5a8bbf9dd0ad39c5b25c8833efea07b880ca", + "sha256:2fa4331c200c2521512595253f5bb70858b90f750d39b8cbfd67465f8d1b596d", + "sha256:3445e07bf2e8ecfeef6ef67ac83de670358abf2996916039b16a218e3d95e97e", + "sha256:3453e8d41fe5f17d1f8e9c383a7473cd46a63661628ec58e07777c2fff7196dc", + "sha256:378753b4a4de2a7b34063d6f95ae81bfa7b15f2c1a04a9518e8644e81807ebea", + "sha256:3af6e48651c4e0d2d166dc1b033b7042ea3f871504b6805ba5f4fe31581d8d38", + "sha256:3dfcbc95bd7992b16f3f7ba05af8a64ca694331bd24f9157b49dadeeb287493b", + "sha256:3f21f0495edea7fdbaaa87e633a8689cd285f8f4af5c869f27bc8074638ad69c", + "sha256:4041711832360a9b75cfb11b25a6a97c8fb49c07b8bd43d0d02b45d0b499a4ff", + "sha256:44d61b4b7d0c2c9ac019c314e52d7cbda0ae31078aabd0f22e583af3e0d79723", + "sha256:4617e1915a539a0d9a9567795023de41a87106522ff83fbfaf1f6baf8e85437e", + "sha256:4b232061ca880db21fa14defe219840ad9b74b6158adb52ddf0e87bead9e8493", + "sha256:5246b14ca64a8675e0a7161f7af68fe3e910e6b90542b4bfb5439ba752191df6", + "sha256:5725dd9cc02068996d4438d397e255dcb1df776b7ceea3b9cb972bdb11260a83", + "sha256:583f6a1993ca3369e0f80ba99d796d8e6b1a3a2a442dd4e1a79e652116413091", + "sha256:59259dc58e57b10e7e18ce02c311804c10c5a793e6568f8af4dead03264584d1", + "sha256:593eba61ba0c3baae5bc9be2f5232430453fb4432048de28399ca7376de9c627", + "sha256:59f4a79c19232a5774aee369a0c296712ad0e77f24e62cad53160312b1c1eaa1", + "sha256:5f0e260eaf54380380ac3808aa4ebe2d8ca28b9087cf411649f96bad6900c728", + "sha256:62d9cfcf4948683a18a9aff0ab7e1474d407b7bab2ca03116109f8464698ab16", + "sha256:64607d4cbf1b7e3c3c8a14948b99345eda0e161b852e122c6bb71aab6d1d798c", + "sha256:655ca44a831ecb238d124e0402d98f6212ac527a0ba6c55ca26f616604e60a45", + "sha256:666ecce376999bf619756a24ce15bb14c5bfaf04bf00abc7e663ce17c3f34fe7", + "sha256:68049202f67380ff9aa52f12e92b1c30115f32e6895cd7198fa2a7961621fc5a", + "sha256:69803198097467ee7282750acb507fba35ca22cc3b85f16cf45fb01cb9097730", + "sha256:6c7b99ca52c2c1752b544e310101b98a659b720b21db00e65edca34483259967", + "sha256:6dd9412824c4ce1aca56c47b0991e65bebb7ac3f4edccfd3f156150c96a7bf25", + "sha256:70eb60b3ae9245ddea20f8a4190bd79c705a22f8028aaf8bbdebe4716c3fab24", + "sha256:70fb28128acbfd264eda9bf47015537ba3fe86e40d046eb2963d75024be4d055", + "sha256:7b2513ba235829860b13faa931f3b6846548021846ac808455301c23a101689d", + "sha256:7ef9d9da710be50ff6809fed8f1963fecdfecc8b86656cadfca3bc24289414b0", + "sha256:81e69b0a0e2537f26d73b4e43ad7bc8c8efb39621639b4434b76a3de50c6966e", + "sha256:8633e471c6207a039eff6aa116e35f69f3156b3989ea3e2d755f7bc41754a4a7", + "sha256:8bd7c8cfc0b8247c8799080fbff54e0b9619e17cdfeb0478ba7295d43f635d7c", + "sha256:9253fc214112405f0afa7db88739294295f0e08466987f1d70e29930262b4c8f", + "sha256:99b37292234e61325e7a5bb9689e55e48c3f5f603af88b1642666277a81f1fbd", + "sha256:9bd7228827ec7bb817089e2eb301d907c0d9827a9e558f22f762bb690b131652", + "sha256:9beeb01d8c190d7581a4d59522cd3d4b6887040dcfc744af99aa59fef3e041a8", + "sha256:a63cbdd98acef6570c62b92a1e43266f9e8b21e699c363c0fef13bd530799c11", + "sha256:a76e42402542b1fae59798fab64432b2d015ab9d0c8c47ba7addddbaf7952333", + "sha256:ac0a03221cdb5058ce0167ecc92a8c89e8d0decdc9e99a2ec23380793c4dcb96", + "sha256:b0b4136a252cadfa1adb705bb81524eee47d9f6aab4f2ee4fa1e9d3cd4581f64", + "sha256:b25bc607423935079e05619d7de556c91fb6adeae9d5f80868dde3468657994b", + "sha256:b3d504047aba448d70cf6fa22e06cb09f7cbd761939fdd47604f5e007675c24e", + "sha256:bb47271f60660803ad11f4c61b42242b8c1312a31c98c578f79ef9387bbde21c", + "sha256:bbb232860e3d03d544bc03ac57855cd82ddf19c7a07651a7c0fdb95e9efea8b9", + "sha256:bc27863442d388870c1809a87507727b799c8460573cfbb6dc0eeaef5a11b5ec", + "sha256:bc51abd01f08117283c5ebf64844a35144a0843ff7b2983e0648e4d3d9f10dbb", + "sha256:be2eb3f2495ba669d2a985f9b426c1797b7d48d6963899276d22f23e33d47e37", + "sha256:bf9db5488121b596dbfc6718c76092fda77b703c1f7533a226a5a9f65248f8ad", + "sha256:c58e2339def52ef6b71b8f36d13c3688ea23fa093353f3a4fee2556e62086ec9", + "sha256:cfbc454a2880389dbb9b5b398e50d439e2e58669160f27b60e5eca11f68ae17c", + "sha256:cff63a0272fcd259dcc3be1657b07c929c466b067ceb1c20060e8d10af56f5bf", + "sha256:d115bffdd417c6d806ea9069237a4ae02f513b778e3789a359bc5856e0404cc4", + "sha256:d20cfb4e099748ea39e6f7b16c91ab057989712d31761d3300d43134e26e165f", + "sha256:d48424e39c2611ee1b84ad0f44fb3b2b53d473e65de061e3f460fc0be5f1939d", + "sha256:e0fa2d4ec53dc51cf7d3bb22e0aa0143966119f42a0c3e4998293a3dd2856b09", + "sha256:e32fee8ab45d3c2db6da19a5323bc3362237c8b653c70194414b892fd06a080d", + "sha256:e35ba67d65d49080e8e5a1dd40101fccdd9798adb9b050ff670b7d74fa41c566", + "sha256:e3fb866d9932a3d7d0c82da76d816996d1667c44891bd861a0f97ba27e84fc74", + "sha256:e61b02c3f7a1e0b75e20c3978f7135fd13cb6cf551bf4a6d29b999a88830a338", + "sha256:e67ba3c290821343c192f7eae1d8fd5999ca2dc99994114643e2f2d3e6138b15", + "sha256:e79dd39f1e8c3504be0607e5fc6e86bb60fe3584bec8b782578c3b0fde8d932c", + "sha256:e89391e6d60251560f0a8f4bd32137b077a80d9b7dbe6d5cab1cd80d2746f648", + "sha256:ea7433ce7e4bfc3a85654aeb6747babe3f66eaf9a1d0c1e7a4435bbdf27fea84", + "sha256:eaf16ae9ae519a0e237a0f528fd9f0197b9bb70f40263ee57ae53c2b8d48aeb3", + "sha256:eb0c341fa71df5a4595f9501df4ac5abfb5a09580081dffbd1ddd4654e6e9123", + "sha256:f276b245347e6e36526cbd4a266a417796fc531ddf391e43574cf6466c492520", + "sha256:f47ad3d5f3258bd7058d2d506852217865afefe6153a36eb4b6928758041d831", + "sha256:f56a6b404f74ab372da986d240e2e002769a7d7102cc73eb238a4f72eec5284e", + "sha256:f5cf2a0c2bdadf3791b5c205d55a37a54025c6e18a71c71f82bb536cf9a454bf", + "sha256:f5d36399a1b96e1a5fdc91e0522544580dbebeb1f77f27b2b0ab25559e103b8b", + "sha256:f60bd8423be1d9d833f230fdbccf8f57af322d96bcad6599e5a771b151398eb2", + "sha256:f612463ac081803f243ff13cccc648578e2279295048f2a8d5eb430af2bae6e3", + "sha256:f73d3fef726b3243a811121de45193c0ca75f6407fe66f3f4e183c983573e130", + "sha256:f82a116a1d03628a8ace4859556fb39fd1424c933341a08ea3ed6de1edb0283b", + "sha256:fb0ba113b4983beac1a2eb16faffd76cb41e176bf58c4afe3e14b9c681f702de", + "sha256:fb4f868f712b2dd4bcc538b0a0c1f63a2b1d584c925e69a224d759e7070a12d5", + "sha256:fb6116dfb8d1925cbdb52595560584db42a7f664617a1f7d7f6e32f138cdf37d", + "sha256:fda7cb070f442bf80b642cd56483b5548e43d366fe3f39b98e67cce780cded00", + "sha256:feea821ee2a9273771bae61194004ee2fc33f8ec7db08117ef9147d4bbcbca8e" + ], + "markers": "python_version >= '3.9'", + "version": "==0.22.3" }, "rsa": { "hashes": [ @@ -2929,39 +2480,48 @@ }, "s3transfer": { "hashes": [ - "sha256:0711534e9356d3cc692fdde846b4a1e4b0cb6519971860796e6bc4c7aea00ef6", - "sha256:eca1c20de70a39daee580aef4986996620f365c4e0fda6a86100231d62f1bf69" + "sha256:244a76a24355363a68164241438de1b72f8781664920260c48465896b712a41e", + "sha256:29edc09801743c21eb5ecbc617a152df41d3c287f67b615f73e5f750583666a7" ], "markers": "python_version >= '3.8'", - "version": "==0.10.2" + "version": "==0.10.4" }, "scikit-learn": { "hashes": [ - "sha256:03b6158efa3faaf1feea3faa884c840ebd61b6484167c711548fce208ea09445", - "sha256:1ff45e26928d3b4eb767a8f14a9a6efbf1cbff7c05d1fb0f95f211a89fd4f5de", - "sha256:299406827fb9a4f862626d0fe6c122f5f87f8910b86fe5daa4c32dcd742139b6", - "sha256:2d4cad1119c77930b235579ad0dc25e65c917e756fe80cab96aa3b9428bd3fb0", - "sha256:394397841449853c2290a32050382edaec3da89e35b3e03d6cc966aebc6a8ae6", - "sha256:3a686885a4b3818d9e62904d91b57fa757fc2bed3e465c8b177be652f4dd37c8", - "sha256:3b923d119d65b7bd555c73be5423bf06c0105678ce7e1f558cb4b40b0a5502b1", - "sha256:3bed4909ba187aca80580fe2ef370d9180dcf18e621a27c4cf2ef10d279a7efe", - "sha256:52788f48b5d8bca5c0736c175fa6bdaab2ef00a8f536cda698db61bd89c551c1", - "sha256:57cc1786cfd6bd118220a92ede80270132aa353647684efa385a74244a41e3b1", - "sha256:643964678f4b5fbdc95cbf8aec638acc7aa70f5f79ee2cdad1eec3df4ba6ead8", - "sha256:6c16d84a0d45e4894832b3c4d0bf73050939e21b99b01b6fd59cbb0cf39163b6", - "sha256:757c7d514ddb00ae249832fe87100d9c73c6ea91423802872d9e74970a0e40b9", - "sha256:8c412ccc2ad9bf3755915e3908e677b367ebc8d010acbb3f182814524f2e5540", - "sha256:b4237ed7b3fdd0a4882792e68ef2545d5baa50aca3bb45aa7df468138ad8f94d", - "sha256:c15b1ca23d7c5f33cc2cb0a0d6aaacf893792271cddff0edbd6a40e8319bc113", - "sha256:ca64b3089a6d9b9363cd3546f8978229dcbb737aceb2c12144ee3f70f95684b7", - "sha256:f60021ec1574e56632be2a36b946f8143bf4e5e6af4a06d85281adc22938e0dd", - "sha256:f763897fe92d0e903aa4847b0aec0e68cadfff77e8a0687cabd946c89d17e675", - "sha256:f8b0ccd4a902836493e026c03256e8b206656f91fbcc4fde28c57a5b752561f1", - "sha256:f932a02c3f4956dfb981391ab24bda1dbd90fe3d628e4b42caef3e041c67707a" + "sha256:04a5ba45c12a5ff81518aa4f1604e826a45d20e53da47b15871526cda4ff5174", + "sha256:0baa91eeb8c32632628874a5c91885eaedd23b71504d24227925080da075837a", + "sha256:1dad624cffe3062276a0881d4e441bc9e3b19d02d17757cd6ae79a9d192a0027", + "sha256:1f50b4f24cf12a81c3c09958ae3b864d7534934ca66ded3822de4996d25d7285", + "sha256:21fadfc2ad7a1ce8bd1d90f23d17875b84ec765eecbbfc924ff11fb73db582ce", + "sha256:2fce7950a3fad85e0a61dc403df0f9345b53432ac0e47c50da210d22c60b6d85", + "sha256:30f34bb5fde90e020653bb84dcb38b6c83f90c70680dbd8c38bd9becbad7a127", + "sha256:34e20bfac8ff0ebe0ff20fb16a4d6df5dc4cc9ce383e00c2ab67a526a3c67b18", + "sha256:366fb3fa47dce90afed3d6106183f4978d6f24cfd595c2373424171b915ee718", + "sha256:3c716d13ba0a2f8762d96ff78d3e0cde90bc9c9b5c13d6ab6bb9b2d6ca6705fd", + "sha256:59cd96a8d9f8dfd546f5d6e9787e1b989e981388d7803abbc9efdcde61e47460", + "sha256:5be4577769c5dde6e1b53de8e6520f9b664ab5861dd57acee47ad119fd7405d6", + "sha256:5c3fa7d3dd5a0ec2d0baba0d644916fa2ab180ee37850c5d536245df916946bd", + "sha256:5fe11794236fb83bead2af26a87ced5d26e3370b8487430818b915dafab1724e", + "sha256:61fe3dcec0d82ae280877a818ab652f4988371e32dd5451e75251bece79668b1", + "sha256:66b1cf721a9f07f518eb545098226796c399c64abdcbf91c2b95d625068363da", + "sha256:7b35b60cf4cd6564b636e4a40516b3c61a4fa7a8b1f7a3ce80c38ebe04750bc3", + "sha256:98717d3c152f6842d36a70f21e1468fb2f1a2f8f2624d9a3f382211798516426", + "sha256:9aafd94bafc841b626681e626be27bf1233d5a0f20f0a6fdb4bee1a1963c6643", + "sha256:9d58481f9f7499dff4196927aedd4285a0baec8caa3790efbe205f13de37dd6e", + "sha256:a17860a562bac54384454d40b3f6155200c1c737c9399e6a97962c63fce503ac", + "sha256:a46d3ca0f11a540b8eaddaf5e38172d8cd65a86cb3e3632161ec96c0cffb774c", + "sha256:a73b1c2038c93bc7f4bf21f6c9828d5116c5d2268f7a20cfbbd41d3074d52083", + "sha256:b44e3a51e181933bdf9a4953cc69c6025b40d2b49e238233f149b98849beb4bf", + "sha256:b6916d1cec1ff163c7d281e699d7a6a709da2f2c5ec7b10547e08cc788ddd3ae", + "sha256:df778486a32518cda33818b7e3ce48c78cef1d5f640a6bc9d97c6d2e71449a51", + "sha256:e5453b2e87ef8accedc5a8a4e6709f887ca01896cd7cc8a174fe39bd4bb00aef", + "sha256:eb9ae21f387826da14b0b9cb1034f5048ddb9182da429c689f5f4a87dc96930b", + "sha256:eba06d75815406091419e06dd650b91ebd1c5f836392a0d833ff36447c2b1bfa", + "sha256:efa7a579606c73a0b3d210e33ea410ea9e1af7933fe324cb7e6fbafae4ea5948" ], "index": "pypi", "markers": "python_version >= '3.9'", - "version": "==1.5.2" + "version": "==1.6.0" }, "scipy": { "hashes": [ @@ -3002,14 +2562,6 @@ "markers": "python_version >= '3.10'", "version": "==1.14.1" }, - "secretstorage": { - "hashes": [ - "sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77", - "sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99" - ], - "markers": "sys_platform == 'linux'", - "version": "==3.3.3" - }, "send2trash": { "hashes": [ "sha256:0c31227e0bd08961c7665474a3d1ef7193929fedda4233843689baa056be46c9", @@ -3020,11 +2572,11 @@ }, "setuptools": { "hashes": [ - "sha256:35ab7fd3bcd95e6b7fd704e4a1539513edad446c097797f2985e0e4b960772f2", - "sha256:d59a21b17a275fb872a9c3dae73963160ae079f1049ed956880cd7c09b120538" + "sha256:8199222558df7c86216af4f84c30e9b34a61d8ba19366cc914424cdbd28252f6", + "sha256:ce74b49e8f7110f9bf04883b730f4765b774ef3ef28f722cce7c273d253aaf7d" ], - "markers": "python_version >= '3.8'", - "version": "==75.1.0" + "markers": "python_version >= '3.9'", + "version": "==75.6.0" }, "shapely": { "hashes": [ @@ -3077,20 +2629,20 @@ }, "six": { "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" + "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", + "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.16.0" + "version": "==1.17.0" }, "slack-sdk": { "hashes": [ - "sha256:e328bb661d95db5f66b993b1d64288ac7c72201a745b4c7cf8848dafb7b74e40", - "sha256:ef93beec3ce9c8f64da02fd487598a05ec4bc9c92ceed58f122dbe632691cbe2" + "sha256:a5e74c00c99dc844ad93e501ab764a20d86fa8184bbc9432af217496f632c4ee", + "sha256:b8cccadfa3d4005a5e6529f52000d25c583f46173fda8e9136fdd2bc58923ff6" ], "index": "pypi", "markers": "python_version >= '3.6'", - "version": "==3.33.1" + "version": "==3.33.5" }, "sniffio": { "hashes": [ @@ -3110,67 +2662,67 @@ }, "sqlalchemy": { "hashes": [ - "sha256:016b2e665f778f13d3c438651dd4de244214b527a275e0acf1d44c05bc6026a9", - "sha256:032d979ce77a6c2432653322ba4cbeabf5a6837f704d16fa38b5a05d8e21fa00", - "sha256:0375a141e1c0878103eb3d719eb6d5aa444b490c96f3fedab8471c7f6ffe70ee", - "sha256:042622a5306c23b972192283f4e22372da3b8ddf5f7aac1cc5d9c9b222ab3ff6", - "sha256:05c3f58cf91683102f2f0265c0db3bd3892e9eedabe059720492dbaa4f922da1", - "sha256:0630774b0977804fba4b6bbea6852ab56c14965a2b0c7fc7282c5f7d90a1ae72", - "sha256:0f9f3f9a3763b9c4deb8c5d09c4cc52ffe49f9876af41cc1b2ad0138878453cf", - "sha256:1b56961e2d31389aaadf4906d453859f35302b4eb818d34a26fab72596076bb8", - "sha256:22b83aed390e3099584b839b93f80a0f4a95ee7f48270c97c90acd40ee646f0b", - "sha256:25b0f63e7fcc2a6290cb5f7f5b4fc4047843504983a28856ce9b35d8f7de03cc", - "sha256:2a275a806f73e849e1c309ac11108ea1a14cd7058577aba962cd7190e27c9e3c", - "sha256:2ab3f0336c0387662ce6221ad30ab3a5e6499aab01b9790879b6578fd9b8faa1", - "sha256:2e795c2f7d7249b75bb5f479b432a51b59041580d20599d4e112b5f2046437a3", - "sha256:3655af10ebcc0f1e4e06c5900bb33e080d6a1fa4228f502121f28a3b1753cde5", - "sha256:4668bd8faf7e5b71c0319407b608f278f279668f358857dbfd10ef1954ac9f90", - "sha256:4c31943b61ed8fdd63dfd12ccc919f2bf95eefca133767db6fbbd15da62078ec", - "sha256:4fdcd72a789c1c31ed242fd8c1bcd9ea186a98ee8e5408a50e610edfef980d71", - "sha256:627dee0c280eea91aed87b20a1f849e9ae2fe719d52cbf847c0e0ea34464b3f7", - "sha256:67219632be22f14750f0d1c70e62f204ba69d28f62fd6432ba05ab295853de9b", - "sha256:6921ee01caf375363be5e9ae70d08ce7ca9d7e0e8983183080211a062d299468", - "sha256:69683e02e8a9de37f17985905a5eca18ad651bf592314b4d3d799029797d0eb3", - "sha256:6a93c5a0dfe8d34951e8a6f499a9479ffb9258123551fa007fc708ae2ac2bc5e", - "sha256:732e026240cdd1c1b2e3ac515c7a23820430ed94292ce33806a95869c46bd139", - "sha256:7befc148de64b6060937231cbff8d01ccf0bfd75aa26383ffdf8d82b12ec04ff", - "sha256:890da8cd1941fa3dab28c5bac3b9da8502e7e366f895b3b8e500896f12f94d11", - "sha256:89b64cd8898a3a6f642db4eb7b26d1b28a497d4022eccd7717ca066823e9fb01", - "sha256:8a6219108a15fc6d24de499d0d515c7235c617b2540d97116b663dade1a54d62", - "sha256:8cdf1a0dbe5ced887a9b127da4ffd7354e9c1a3b9bb330dce84df6b70ccb3a8d", - "sha256:8d625eddf7efeba2abfd9c014a22c0f6b3796e0ffb48f5d5ab106568ef01ff5a", - "sha256:93a71c8601e823236ac0e5d087e4f397874a421017b3318fd92c0b14acf2b6db", - "sha256:9509c4123491d0e63fb5e16199e09f8e262066e58903e84615c301dde8fa2e87", - "sha256:a29762cd3d116585278ffb2e5b8cc311fb095ea278b96feef28d0b423154858e", - "sha256:a62dd5d7cc8626a3634208df458c5fe4f21200d96a74d122c83bc2015b333bc1", - "sha256:ada603db10bb865bbe591939de854faf2c60f43c9b763e90f653224138f910d9", - "sha256:aee110e4ef3c528f3abbc3c2018c121e708938adeeff9006428dd7c8555e9b3f", - "sha256:b76d63495b0508ab9fc23f8152bac63205d2a704cd009a2b0722f4c8e0cba8e0", - "sha256:c0d8326269dbf944b9201911b0d9f3dc524d64779a07518199a58384c3d37a44", - "sha256:c41411e192f8d3ea39ea70e0fae48762cd11a2244e03751a98bd3c0ca9a4e936", - "sha256:c68fe3fcde03920c46697585620135b4ecfdfc1ed23e75cc2c2ae9f8502c10b8", - "sha256:cb8bea573863762bbf45d1e13f87c2d2fd32cee2dbd50d050f83f87429c9e1ea", - "sha256:cc32b2990fc34380ec2f6195f33a76b6cdaa9eecf09f0c9404b74fc120aef36f", - "sha256:ccae5de2a0140d8be6838c331604f91d6fafd0735dbdcee1ac78fc8fbaba76b4", - "sha256:d299797d75cd747e7797b1b41817111406b8b10a4f88b6e8fe5b5e59598b43b0", - "sha256:e04b622bb8a88f10e439084486f2f6349bf4d50605ac3e445869c7ea5cf0fa8c", - "sha256:e11d7ea4d24f0a262bccf9a7cd6284c976c5369dac21db237cff59586045ab9f", - "sha256:e21f66748ab725ade40fa7af8ec8b5019c68ab00b929f6643e1b1af461eddb60", - "sha256:eb60b026d8ad0c97917cb81d3662d0b39b8ff1335e3fabb24984c6acd0c900a2", - "sha256:f021d334f2ca692523aaf7bbf7592ceff70c8594fad853416a81d66b35e3abf9", - "sha256:f552023710d4b93d8fb29a91fadf97de89c5926c6bd758897875435f2a939f33" + "sha256:03e08af7a5f9386a43919eda9de33ffda16b44eb11f3b313e6822243770e9763", + "sha256:0572f4bd6f94752167adfd7c1bed84f4b240ee6203a95e05d1e208d488d0d436", + "sha256:07b441f7d03b9a66299ce7ccf3ef2900abc81c0db434f42a5694a37bd73870f2", + "sha256:1bc330d9d29c7f06f003ab10e1eaced295e87940405afe1b110f2eb93a233588", + "sha256:1e0d612a17581b6616ff03c8e3d5eff7452f34655c901f75d62bd86449d9750e", + "sha256:23623166bfefe1487d81b698c423f8678e80df8b54614c2bf4b4cfcd7c711959", + "sha256:2519f3a5d0517fc159afab1015e54bb81b4406c278749779be57a569d8d1bb0d", + "sha256:28120ef39c92c2dd60f2721af9328479516844c6b550b077ca450c7d7dc68575", + "sha256:37350015056a553e442ff672c2d20e6f4b6d0b2495691fa239d8aa18bb3bc908", + "sha256:39769a115f730d683b0eb7b694db9789267bcd027326cccc3125e862eb03bfd8", + "sha256:3c01117dd36800f2ecaa238c65365b7b16497adc1522bf84906e5710ee9ba0e8", + "sha256:3d6718667da04294d7df1670d70eeddd414f313738d20a6f1d1f379e3139a545", + "sha256:3dbb986bad3ed5ceaf090200eba750b5245150bd97d3e67343a3cfed06feecf7", + "sha256:4557e1f11c5f653ebfdd924f3f9d5ebfc718283b0b9beebaa5dd6b77ec290971", + "sha256:46331b00096a6db1fdc052d55b101dbbfc99155a548e20a0e4a8e5e4d1362855", + "sha256:4a121d62ebe7d26fec9155f83f8be5189ef1405f5973ea4874a26fab9f1e262c", + "sha256:4f5e9cd989b45b73bd359f693b935364f7e1f79486e29015813c338450aa5a71", + "sha256:50aae840ebbd6cdd41af1c14590e5741665e5272d2fee999306673a1bb1fdb4d", + "sha256:59b1ee96617135f6e1d6f275bbe988f419c5178016f3d41d3c0abb0c819f75bb", + "sha256:59b8f3adb3971929a3e660337f5dacc5942c2cdb760afcabb2614ffbda9f9f72", + "sha256:66bffbad8d6271bb1cc2f9a4ea4f86f80fe5e2e3e501a5ae2a3dc6a76e604e6f", + "sha256:69f93723edbca7342624d09f6704e7126b152eaed3cdbb634cb657a54332a3c5", + "sha256:6a440293d802d3011028e14e4226da1434b373cbaf4a4bbb63f845761a708346", + "sha256:72c28b84b174ce8af8504ca28ae9347d317f9dba3999e5981a3cd441f3712e24", + "sha256:79d2e78abc26d871875b419e1fd3c0bca31a1cb0043277d0d850014599626c2e", + "sha256:7f2767680b6d2398aea7082e45a774b2b0767b5c8d8ffb9c8b683088ea9b29c5", + "sha256:8318f4776c85abc3f40ab185e388bee7a6ea99e7fa3a30686580b209eaa35c08", + "sha256:8958b10490125124463095bbdadda5aa22ec799f91958e410438ad6c97a7b793", + "sha256:8c78ac40bde930c60e0f78b3cd184c580f89456dd87fc08f9e3ee3ce8765ce88", + "sha256:90812a8933df713fdf748b355527e3af257a11e415b613dd794512461eb8a686", + "sha256:9bc633f4ee4b4c46e7adcb3a9b5ec083bf1d9a97c1d3854b92749d935de40b9b", + "sha256:9e46ed38affdfc95d2c958de328d037d87801cfcbea6d421000859e9789e61c2", + "sha256:9fe53b404f24789b5ea9003fc25b9a3988feddebd7e7b369c8fac27ad6f52f28", + "sha256:a4e46a888b54be23d03a89be510f24a7652fe6ff660787b96cd0e57a4ebcb46d", + "sha256:a86bfab2ef46d63300c0f06936bd6e6c0105faa11d509083ba8f2f9d237fb5b5", + "sha256:ac9dfa18ff2a67b09b372d5db8743c27966abf0e5344c555d86cc7199f7ad83a", + "sha256:af148a33ff0349f53512a049c6406923e4e02bf2f26c5fb285f143faf4f0e46a", + "sha256:b11d0cfdd2b095e7b0686cf5fabeb9c67fae5b06d265d8180715b8cfa86522e3", + "sha256:b2985c0b06e989c043f1dc09d4fe89e1616aadd35392aea2844f0458a989eacf", + "sha256:b544ad1935a8541d177cb402948b94e871067656b3a0b9e91dbec136b06a2ff5", + "sha256:b5cc79df7f4bc3d11e4b542596c03826063092611e481fcf1c9dfee3c94355ef", + "sha256:b817d41d692bf286abc181f8af476c4fbef3fd05e798777492618378448ee689", + "sha256:b81ee3d84803fd42d0b154cb6892ae57ea6b7c55d8359a02379965706c7efe6c", + "sha256:be9812b766cad94a25bc63bec11f88c4ad3629a0cec1cd5d4ba48dc23860486b", + "sha256:c245b1fbade9c35e5bd3b64270ab49ce990369018289ecfde3f9c318411aaa07", + "sha256:c3f3631693003d8e585d4200730616b78fafd5a01ef8b698f6967da5c605b3fa", + "sha256:c4ae3005ed83f5967f961fd091f2f8c5329161f69ce8480aa8168b2d7fe37f06", + "sha256:c54a1e53a0c308a8e8a7dffb59097bff7facda27c70c286f005327f21b2bd6b1", + "sha256:d0ddd9db6e59c44875211bc4c7953a9f6638b937b0a88ae6d09eb46cced54eff", + "sha256:dc022184d3e5cacc9579e41805a681187650e170eb2fd70e28b86192a479dcaa", + "sha256:e32092c47011d113dc01ab3e1d3ce9f006a47223b18422c5c0d150af13a00687", + "sha256:f7b64e6ec3f02c35647be6b4851008b26cff592a95ecb13b6788a54ef80bbdd4", + "sha256:f942a799516184c855e1a32fbc7b29d7e571b52612647866d4ec1c3242578fcb", + "sha256:f9511d8dd4a6e9271d07d150fb2f81874a3c8c95e11ff9af3a2dfc35fe42ee44", + "sha256:fd3a55deef00f689ce931d4d1b23fa9f04c880a48ee97af488fd215cf24e2a6c", + "sha256:fddbe92b4760c6f5d48162aef14824add991aeda8ddadb3c31d56eb15ca69f8e", + "sha256:fdf3386a801ea5aba17c6410dd1dc8d39cf454ca2565541b5ac42a84e1e28f53" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==2.0.35" - }, - "sqlparse": { - "hashes": [ - "sha256:773dcbf9a5ab44a090f3441e2180efe2560220203dc2f8c0b0fa141e18b505e4", - "sha256:bb6b4df465655ef332548e24f08e205afc81b9ab86cb1c45657a7ff173a3a00e" - ], - "markers": "python_version >= '3.8'", - "version": "==0.5.1" + "version": "==2.0.36" }, "stack-data": { "hashes": [ @@ -3179,14 +2731,6 @@ ], "version": "==0.6.3" }, - "tabulate": { - "hashes": [ - "sha256:0095b12bf5966de529c0feb1fa08671671b3368eec77d7ef7ab114be2c068b3c", - "sha256:024ca478df22e9340661486f85298cff5f6dcdba14f3813e8830015b9ed1948f" - ], - "markers": "python_version >= '3.7'", - "version": "==0.9.0" - }, "terminado": { "hashes": [ "sha256:a4468e1b37bb318f8a86514f65814e1afc977cf29b3992a4500d9dd305dcceb0", @@ -3195,13 +2739,6 @@ "markers": "python_version >= '3.8'", "version": "==0.18.1" }, - "text-unidecode": { - "hashes": [ - "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8", - "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93" - ], - "version": "==1.3" - }, "threadpoolctl": { "hashes": [ "sha256:082433502dd922bf738de0d8bcc4fdcbf0979ff44c42bd40f5af8a282f6fa107", @@ -3212,53 +2749,37 @@ }, "tinycss2": { "hashes": [ - "sha256:152f9acabd296a8375fbca5b84c961ff95971fcfc32e79550c8df8e29118c54d", - "sha256:54a8dbdffb334d536851be0226030e9505965bb2f30f21a4a82c55fb2a80fae7" - ], - "markers": "python_version >= '3.8'", - "version": "==1.3.0" - }, - "toml": { - "hashes": [ - "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", - "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" - ], - "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==0.10.2" - }, - "tomlkit": { - "hashes": [ - "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde", - "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79" + "sha256:10c0972f6fc0fbee87c3edb76549357415e94548c1ae10ebccdea16fb404a9b7", + "sha256:3a49cf47b7675da0b15d0c6e1df8df4ebd96e9394bb905a5775adb0d884c5289" ], "markers": "python_version >= '3.8'", - "version": "==0.13.2" + "version": "==1.4.0" }, "tornado": { "hashes": [ - "sha256:163b0aafc8e23d8cdc3c9dfb24c5368af84a81e3364745ccb4427669bf84aec8", - "sha256:25486eb223babe3eed4b8aecbac33b37e3dd6d776bc730ca14e1bf93888b979f", - "sha256:454db8a7ecfcf2ff6042dde58404164d969b6f5d58b926da15e6b23817950fc4", - "sha256:613bf4ddf5c7a95509218b149b555621497a6cc0d46ac341b30bd9ec19eac7f3", - "sha256:6d5ce3437e18a2b66fbadb183c1d3364fb03f2be71299e7d10dbeeb69f4b2a14", - "sha256:8ae50a504a740365267b2a8d1a90c9fbc86b780a39170feca9bcc1787ff80842", - "sha256:92d3ab53183d8c50f8204a51e6f91d18a15d5ef261e84d452800d4ff6fc504e9", - "sha256:a02a08cc7a9314b006f653ce40483b9b3c12cda222d6a46d4ac63bb6c9057698", - "sha256:b24b8982ed444378d7f21d563f4180a2de31ced9d8d84443907a0a64da2072e7", - "sha256:d9a566c40b89757c9aa8e6f032bcdb8ca8795d7c1a9762910c722b1635c9de4d", - "sha256:e2e20b9113cd7293f164dc46fffb13535266e713cdb87bd2d15ddb336e96cfc4" + "sha256:072ce12ada169c5b00b7d92a99ba089447ccc993ea2143c9ede887e0937aa803", + "sha256:1a017d239bd1bb0919f72af256a970624241f070496635784d9bf0db640d3fec", + "sha256:2876cef82e6c5978fde1e0d5b1f919d756968d5b4282418f3146b79b58556482", + "sha256:304463bd0772442ff4d0f5149c6f1c2135a1fae045adf070821c6cdc76980634", + "sha256:908b71bf3ff37d81073356a5fadcc660eb10c1476ee6e2725588626ce7e5ca38", + "sha256:92bad5b4746e9879fd7bf1eb21dce4e3fc5128d71601f80005afa39237ad620b", + "sha256:932d195ca9015956fa502c6b56af9eb06106140d844a335590c1ec7f5277d10c", + "sha256:bca9eb02196e789c9cb5c3c7c0f04fb447dc2adffd95265b2c7223a8a615ccbf", + "sha256:c36e62ce8f63409301537222faffcef7dfc5284f27eec227389f2ad11b09d946", + "sha256:c82c46813ba483a385ab2a99caeaedf92585a1f90defb5693351fa7e4ea0bf73", + "sha256:e828cce1123e9e44ae2a50a9de3055497ab1d0aeb440c5ac23064d9e44880da1" ], "markers": "python_version >= '3.8'", - "version": "==6.4.1" + "version": "==6.4.2" }, "tqdm": { "hashes": [ - "sha256:90279a3770753eafc9194a0364852159802111925aa30eb3f9d85b0e805ac7cd", - "sha256:e1020aef2e5096702d8a025ac7d16b1577279c9d63f8375b63083e9a5f0fcbad" + "sha256:26445eca388f82e72884e0d580d5464cd801a3ea01e63e5601bdff9ba6a48de2", + "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==4.66.5" + "version": "==4.67.1" }, "traitlets": { "hashes": [ @@ -3270,11 +2791,11 @@ }, "types-python-dateutil": { "hashes": [ - "sha256:27c8cc2d058ccb14946eebcaaa503088f4f6dbc4fb6093d3d456a49aef2753f6", - "sha256:9706c3b68284c25adffc47319ecc7947e5bb86b3773f843c73906fd598bc176e" + "sha256:18f493414c26ffba692a72369fea7a154c502646301ebfe3d56a04b3767284cb", + "sha256:e248a4bc70a486d3e3ec84d0dc30eec3a5f979d6e7ee4123ae043eedbb987f53" ], "markers": "python_version >= '3.8'", - "version": "==2.9.0.20240906" + "version": "==2.9.0.20241206" }, "typing-extensions": { "hashes": [ @@ -3297,6 +2818,7 @@ "sha256:0e00f8eb65e18c7de20d595a14336e9f337ead580c70934141624b6d1ffdacc7", "sha256:a44a133ea12d44a0c0f06d7d42a52d71282e77e2f937d8abd5655b8d56fc1363" ], + "markers": "python_version >= '3.7'", "version": "==1.3.0" }, "uritemplate": { @@ -3309,20 +2831,11 @@ }, "urllib3": { "hashes": [ - "sha256:0ed14ccfbf1c30a9072c7ca157e4319b70d65f623e91e7b32fadb2853431016e", - "sha256:40c2dc0c681e47eb8f90e7e27bf6ff7df2e677421fd46756da1161c39ca70d32" + "sha256:ca899ca043dcb1bafa3e262d73aa25c465bfb49e0bd9dd5d59f1d0acba2f8fac", + "sha256:e7d814a81dad81e6caf2ec9fdedb284ecc9c73076b62654547cc64ccdcae26e9" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==1.26.20" - }, - "vulture": { - "hashes": [ - "sha256:68ee4c4ce0128bb504dc7c2df4244c97ef5a2e29af42f27a976a6e30906e993a", - "sha256:c35e98e992eb84b01cdadbfeb0aca2d44363e7dfe6c19416f65001ae69360ccc" - ], - "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==2.12" + "version": "==2.2.3" }, "wcwidth": { "hashes": [ @@ -3333,10 +2846,11 @@ }, "webcolors": { "hashes": [ - "sha256:08b07af286a01bcd30d583a7acadf629583d1f79bfef27dd2c2c5c263817277d", - "sha256:fc4c3b59358ada164552084a8ebee637c221e4059267d0f8325b3b560f6c7f0a" + "sha256:515291393b4cdf0eb19c155749a096f779f7d909f7cceea072791cb9095b92e9", + "sha256:ecb3d768f32202af770477b8b65f318fa4f566c22948673a977b00d589dd80f6" ], - "version": "==24.8.0" + "markers": "python_version >= '3.9'", + "version": "==24.11.1" }, "webencodings": { "hashes": [ @@ -3360,15 +2874,216 @@ ], "markers": "python_version >= '3.7'", "version": "==4.0.13" + } + }, + "develop": { + "astroid": { + "hashes": [ + "sha256:6aaea045f938c735ead292204afdb977a36e989522b7833ef6fea94de743f442", + "sha256:db676dc4f3ae6bfe31cda227dc60e03438378d7a896aec57422c95634e8d722f" + ], + "markers": "python_full_version >= '3.9.0'", + "version": "==3.3.6" }, - "zipp": { + "black": { + "hashes": [ + "sha256:14b3502784f09ce2443830e3133dacf2c0110d45191ed470ecb04d0f5f6fcb0f", + "sha256:17374989640fbca88b6a448129cd1745c5eb8d9547b464f281b251dd00155ccd", + "sha256:1c536fcf674217e87b8cc3657b81809d3c085d7bf3ef262ead700da345bfa6ea", + "sha256:1cbacacb19e922a1d75ef2b6ccaefcd6e93a2c05ede32f06a21386a04cedb981", + "sha256:1f93102e0c5bb3907451063e08b9876dbeac810e7da5a8bfb7aeb5a9ef89066b", + "sha256:2cd9c95431d94adc56600710f8813ee27eea544dd118d45896bb734e9d7a0dc7", + "sha256:30d2c30dc5139211dda799758559d1b049f7f14c580c409d6ad925b74a4208a8", + "sha256:394d4ddc64782e51153eadcaaca95144ac4c35e27ef9b0a42e121ae7e57a9175", + "sha256:3bb2b7a1f7b685f85b11fed1ef10f8a9148bceb49853e47a294a3dd963c1dd7d", + "sha256:4007b1393d902b48b36958a216c20c4482f601569d19ed1df294a496eb366392", + "sha256:5a2221696a8224e335c28816a9d331a6c2ae15a2ee34ec857dcf3e45dbfa99ad", + "sha256:63f626344343083322233f175aaf372d326de8436f5928c042639a4afbbf1d3f", + "sha256:649fff99a20bd06c6f727d2a27f401331dc0cc861fb69cde910fe95b01b5928f", + "sha256:680359d932801c76d2e9c9068d05c6b107f2584b2a5b88831c83962eb9984c1b", + "sha256:846ea64c97afe3bc677b761787993be4991810ecc7a4a937816dd6bddedc4875", + "sha256:b5e39e0fae001df40f95bd8cc36b9165c5e2ea88900167bddf258bacef9bbdc3", + "sha256:ccfa1d0cb6200857f1923b602f978386a3a2758a65b52e0950299ea014be6800", + "sha256:d37d422772111794b26757c5b55a3eade028aa3fde43121ab7b673d050949d65", + "sha256:ddacb691cdcdf77b96f549cf9591701d8db36b2f19519373d60d31746068dbf2", + "sha256:e6668650ea4b685440857138e5fe40cde4d652633b1bdffc62933d0db4ed9812", + "sha256:f9da3333530dbcecc1be13e69c250ed8dfa67f43c4005fb537bb426e19200d50", + "sha256:fe4d6476887de70546212c99ac9bd803d90b42fc4767f058a0baa895013fbb3e" + ], + "index": "pypi", + "markers": "python_version >= '3.9'", + "version": "==24.10.0" + }, + "click": { + "hashes": [ + "sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28", + "sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de" + ], + "markers": "python_version >= '3.7'", + "version": "==8.1.7" + }, + "colorama": { + "hashes": [ + "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", + "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6" + ], + "markers": "python_version >= '3.5'", + "version": "==0.4.6" + }, + "dill": { "hashes": [ - "sha256:a817ac80d6cf4b23bf7f2828b7cabf326f15a001bea8b1f9b49631780ba28350", - "sha256:bc9eb26f4506fda01b81bcde0ca78103b6e62f991b381fec825435c836edbc29" + "sha256:468dff3b89520b474c0397703366b7b95eebe6303f108adf9b19da1f702be87a", + "sha256:81aa267dddf68cbfe8029c42ca9ec6a4ab3b22371d1c450abc54422577b4512c" + ], + "markers": "python_version >= '3.11'", + "version": "==0.3.9" + }, + "iniconfig": { + "hashes": [ + "sha256:2d91e135bf72d31a410b17c16da610a82cb55f6b0477d1a902134b24a455b8b3", + "sha256:b6a85871a79d2e3b22d2d1b94ac2824226a63c6b741c88f7ae975f18b6778374" + ], + "markers": "python_version >= '3.7'", + "version": "==2.0.0" + }, + "isort": { + "hashes": [ + "sha256:48fdfcb9face5d58a4f6dde2e72a1fb8dcaf8ab26f95ab49fab84c2ddefb0109", + "sha256:8ca5e72a8d85860d5a3fa69b8745237f2939afe12dbf656afbcb47fe72d947a6" + ], + "markers": "python_full_version >= '3.8.0'", + "version": "==5.13.2" + }, + "mando": { + "hashes": [ + "sha256:18baa999b4b613faefb00eac4efadcf14f510b59b924b66e08289aa1de8c3500", + "sha256:26ef1d70928b6057ee3ca12583d73c63e05c49de8972d620c278a7b206581a8a" + ], + "version": "==0.7.1" + }, + "mccabe": { + "hashes": [ + "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", + "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e" + ], + "markers": "python_version >= '3.6'", + "version": "==0.7.0" + }, + "mypy-extensions": { + "hashes": [ + "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d", + "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782" + ], + "markers": "python_version >= '3.5'", + "version": "==1.0.0" + }, + "packaging": { + "hashes": [ + "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", + "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f" ], "markers": "python_version >= '3.8'", - "version": "==3.20.2" + "version": "==24.2" + }, + "pathspec": { + "hashes": [ + "sha256:a0d503e138a4c123b27490a4f7beda6a01c6f288df0e4a8b79c7eb0dc7b4cc08", + "sha256:a482d51503a1ab33b1c67a6c3813a26953dbdc71c31dacaef9a838c4e29f5712" + ], + "markers": "python_version >= '3.8'", + "version": "==0.12.1" + }, + "platformdirs": { + "hashes": [ + "sha256:357fb2acbc885b0419afd3ce3ed34564c13c9b95c89360cd9563f73aa5e2b907", + "sha256:73e575e1408ab8103900836b97580d5307456908a03e92031bab39e4554cc3fb" + ], + "markers": "python_version >= '3.8'", + "version": "==4.3.6" + }, + "pluggy": { + "hashes": [ + "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", + "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669" + ], + "markers": "python_version >= '3.8'", + "version": "==1.5.0" + }, + "pylint": { + "hashes": [ + "sha256:77f068c287d49b8683cd7c6e624243c74f92890f767f106ffa1ddf3c0a54cb7a", + "sha256:9ec054ec992cd05ad30a6df1676229739a73f8feeabf3912c995d17601052b01" + ], + "index": "pypi", + "markers": "python_full_version >= '3.9.0'", + "version": "==3.3.2" + }, + "pytest": { + "hashes": [ + "sha256:50e16d954148559c9a74109af1eaf0c945ba2d8f30f0a3d3335edde19788b6f6", + "sha256:965370d062bce11e73868e0335abac31b4d3de0e82f4007408d242b4f8610761" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==8.3.4" + }, + "radon": { + "hashes": [ + "sha256:632cc032364a6f8bb1010a2f6a12d0f14bc7e5ede76585ef29dc0cecf4cd8859", + "sha256:d1ac0053943a893878940fedc8b19ace70386fc9c9bf0a09229a44125ebf45b5" + ], + "index": "pypi", + "version": "==6.0.1" + }, + "ruff": { + "hashes": [ + "sha256:1ca4e3a87496dc07d2427b7dd7ffa88a1e597c28dad65ae6433ecb9f2e4f022f", + "sha256:2aae99ec70abf43372612a838d97bfe77d45146254568d94926e8ed5bbb409ea", + "sha256:32096b41aaf7a5cc095fa45b4167b890e4c8d3fd217603f3634c92a541de7248", + "sha256:5fe716592ae8a376c2673fdfc1f5c0c193a6d0411f90a496863c99cd9e2ae25d", + "sha256:60f578c11feb1d3d257b2fb043ddb47501ab4816e7e221fbb0077f0d5d4e7b6f", + "sha256:705832cd7d85605cb7858d8a13d75993c8f3ef1397b0831289109e953d833d29", + "sha256:729850feed82ef2440aa27946ab39c18cb4a8889c1128a6d589ffa028ddcfc22", + "sha256:81c148825277e737493242b44c5388a300584d73d5774defa9245aaef55448b0", + "sha256:ac42caaa0411d6a7d9594363294416e0e48fc1279e1b0e948391695db2b3d5b1", + "sha256:b402ddee3d777683de60ff76da801fa7e5e8a71038f57ee53e903afbcefdaa58", + "sha256:b84f4f414dda8ac7f75075c1fa0b905ac0ff25361f42e6d5da681a465e0f78e5", + "sha256:c49ab4da37e7c457105aadfd2725e24305ff9bc908487a9bf8d548c6dad8bb3d", + "sha256:cbd5cf9b0ae8f30eebc7b360171bd50f59ab29d39f06a670b3e4501a36ba5897", + "sha256:d261d7850c8367704874847d95febc698a950bf061c9475d4a8b7689adc4f7fa", + "sha256:e769083da9439508833cfc7c23e351e1809e67f47c50248250ce1ac52c21fb93", + "sha256:ec016beb69ac16be416c435828be702ee694c0d722505f9c1f35e1b9c0cc1bf5", + "sha256:f05cdf8d050b30e2ba55c9b09330b51f9f97d36d4673213679b965d25a785f3c", + "sha256:fb88e2a506b70cfbc2de6fae6681c4f944f7dd5f2fe87233a7233d888bad73e8" + ], + "index": "pypi", + "markers": "python_version >= '3.7'", + "version": "==0.8.2" + }, + "six": { + "hashes": [ + "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", + "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.17.0" + }, + "tomlkit": { + "hashes": [ + "sha256:7a974427f6e119197f670fbbbeae7bef749a6c14e793db934baefc1b5f03efde", + "sha256:fff5fe59a87295b278abd31bec92c15d9bc4a06885ab12bcea52c71119392e79" + ], + "markers": "python_version >= '3.8'", + "version": "==0.13.2" + }, + "vulture": { + "hashes": [ + "sha256:cb8277902a1138deeab796ec5bef7076a6e0248ca3607a3f3dee0b6d9e9b8415", + "sha256:d9a90dba89607489548a49d557f8bac8112bd25d3cbc8aeef23e860811bd5ed9" + ], + "index": "pypi", + "markers": "python_version >= '3.8'", + "version": "==2.14" } - }, - "develop": {} + } } diff --git a/data/src/classes/featurelayer.py b/data/src/classes/featurelayer.py new file mode 100644 index 00000000..18bcd245 --- /dev/null +++ b/data/src/classes/featurelayer.py @@ -0,0 +1,408 @@ +import logging as log +import os +import subprocess +import traceback + +import geopandas as gpd +import pandas as pd +import requests +import sqlalchemy as sa +from config.config import ( + FORCE_RELOAD, + USE_CRS, + log_level, + min_tiles_file_size_in_bytes, + write_production_tiles_file, +) +from config.psql import conn, local_engine +from esridump.dumper import EsriDumper +from google.cloud import storage +from google.cloud.storage.bucket import Bucket +from shapely import Point, wkb + +log.basicConfig(level=log_level) + + +def google_cloud_bucket() -> Bucket: + """Build the google cloud bucket with name configured in your environ or default of cleanandgreenphl + + Returns: + Bucket: the gcp bucket + """ + credentials_path = os.path.expanduser("/app/service-account-key.json") + + if not os.path.exists(credentials_path): + raise FileNotFoundError(f"Credentials file not found at {credentials_path}") + + os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = credentials_path + bucket_name = os.getenv("GOOGLE_CLOUD_BUCKET_NAME", "cleanandgreenphl") + project_name = os.getenv("GOOGLE_CLOUD_PROJECT", "clean-and-green-philly") + + storage_client = storage.Client(project=project_name) + return storage_client.bucket(bucket_name) + + +bucket = google_cloud_bucket() + + +class FeatureLayer: + """ + FeatureLayer is a class to represent a GIS dataset. It can be initialized with a URL to an Esri Feature Service, a SQL query to Carto, or a GeoDataFrame. + """ + + def __init__( + self, + name, + esri_rest_urls=None, + carto_sql_queries=None, + gdf=None, + crs=USE_CRS, + force_reload=FORCE_RELOAD, + from_xy=False, + use_wkb_geom_field=None, + cols: list[str] = None, + ): + self.name = name + self.esri_rest_urls = ( + [esri_rest_urls] if isinstance(esri_rest_urls, str) else esri_rest_urls + ) + self.carto_sql_queries = ( + [carto_sql_queries] + if isinstance(carto_sql_queries, str) + else carto_sql_queries + ) + self.gdf = gdf + self.crs = crs + self.cols = cols + self.psql_table = name.lower().replace(" ", "_") + self.input_crs = "EPSG:4326" if not from_xy else USE_CRS + self.use_wkb_geom_field = use_wkb_geom_field + + inputs = [self.esri_rest_urls, self.carto_sql_queries, self.gdf] + non_none_inputs = [i for i in inputs if i is not None] + + if len(non_none_inputs) > 0: + if self.esri_rest_urls is not None: + self.type = "esri" + elif self.carto_sql_queries is not None: + self.type = "carto" + elif self.gdf is not None: + self.type = "gdf" + + if force_reload: + self.load_data() + else: + psql_exists = self.check_psql() + if not psql_exists: + self.load_data() + else: + print(f"Initialized FeatureLayer {self.name} with no data.") + + def check_psql(self): + try: + if not sa.inspect(local_engine).has_table(self.psql_table): + print(f"Table {self.psql_table} does not exist") + return False + psql_table = gpd.read_postgis( + f"SELECT * FROM {self.psql_table}", conn, geom_col="geometry" + ) + if len(psql_table) == 0: + return False + else: + print(f"Loading data for {self.name} from psql...") + self.gdf = psql_table + return True + except Exception as e: + print(f"Error loading data for {self.name}: {e}") + return False + + def load_data(self): + print(f"Loading data for {self.name} from {self.type}...") + if self.type == "gdf": + pass + else: + try: + if self.type == "esri": + if self.esri_rest_urls is None: + raise ValueError("Must provide a URL to load data from Esri") + + gdfs = [] + for url in self.esri_rest_urls: + parcel_type = ( + "Land" + if "Vacant_Indicators_Land" in url + else "Building" + if "Vacant_Indicators_Bldg" in url + else None + ) + self.dumper = EsriDumper(url) + features = [feature for feature in self.dumper] + + geojson_features = { + "type": "FeatureCollection", + "features": features, + } + + this_gdf = gpd.GeoDataFrame.from_features( + geojson_features, crs=self.input_crs + ) + + # Check if 'X' and 'Y' columns exist and create geometry if necessary + if "X" in this_gdf.columns and "Y" in this_gdf.columns: + this_gdf["geometry"] = this_gdf.apply( + lambda row: Point(row["X"], row["Y"]), axis=1 + ) + elif "geometry" not in this_gdf.columns: + raise ValueError( + "No geometry information found in the data." + ) + + this_gdf = this_gdf.to_crs(USE_CRS) + + # Assign the parcel_type to the GeoDataFrame + if parcel_type: + this_gdf["parcel_type"] = parcel_type + + gdfs.append(this_gdf) + + self.gdf = pd.concat(gdfs, ignore_index=True) + + elif self.type == "carto": + if self.carto_sql_queries is None: + raise ValueError( + "Must provide a SQL query to load data from Carto" + ) + + gdfs = [] + for sql_query in self.carto_sql_queries: + response = requests.get( + "https://phl.carto.com/api/v2/sql", params={"q": sql_query} + ) + + data = response.json()["rows"] + df = pd.DataFrame(data) + geometry = ( + wkb.loads(df[self.use_wkb_geom_field], hex=True) + if self.use_wkb_geom_field + else gpd.points_from_xy(df.x, df.y) + ) + + gdf = gpd.GeoDataFrame( + df, + geometry=geometry, + crs=self.input_crs, + ) + gdf = gdf.to_crs(USE_CRS) + + gdfs.append(gdf) + self.gdf = pd.concat(gdfs, ignore_index=True) + + # Drop columns + if self.cols: + self.cols.append("geometry") + self.gdf = self.gdf[self.cols] + + # save self.gdf to psql + # rename columns to lowercase for table creation in postgres + if self.cols: + self.gdf = self.gdf.rename( + columns={x: x.lower() for x in self.cols} + ) + self.gdf.to_postgis( + name=self.psql_table, + con=conn, + if_exists="replace", + chunksize=1000, + ) + except Exception as e: + print(f"Error loading data for {self.name}: {e}") + traceback.print_exc() + self.gdf = None + + def spatial_join(self, other_layer, how="left", predicate="intersects"): + """ + Spatial joins in this script are generally left intersect joins. + They also can create duplicates, so we drop duplicates after the join. + Note: We may want to revisit the duplicates. + """ + + # If other_layer.gdf isn't a geodataframe, try to make it one + if not isinstance(other_layer.gdf, gpd.GeoDataFrame): + try: + other_layer.rebuild_gdf() + + except Exception as e: + print(f"Error converting other_layer to GeoDataFrame: {e}") + return + + self.gdf = gpd.sjoin(self.gdf, other_layer.gdf, how=how, predicate=predicate) + self.gdf.drop(columns=["index_right"], inplace=True) + self.gdf.drop_duplicates(inplace=True) + + # Coerce opa_id to integer and drop rows where opa_id is null or non-numeric + self.gdf.loc[:, "opa_id"] = pd.to_numeric(self.gdf["opa_id"], errors="coerce") + self.gdf = self.gdf.dropna(subset=["opa_id"]) + + def opa_join(self, other_df, opa_column): + """ + Join 2 dataframes based on opa_id and keep the 'geometry' column from the left dataframe if it exists in both. + """ + + # Coerce opa_column to integer and drop rows where opa_column is null or non-numeric + other_df.loc[:, opa_column] = pd.to_numeric( + other_df[opa_column], errors="coerce" + ) + other_df = other_df.dropna(subset=[opa_column]) + + # Coerce opa_id to integer and drop rows where opa_id is null or non-numeric + self.gdf.loc[:, "opa_id"] = pd.to_numeric(self.gdf["opa_id"], errors="coerce") + self.gdf = self.gdf.dropna(subset=["opa_id"]) + + # Perform the merge + joined = self.gdf.merge( + other_df, how="left", right_on=opa_column, left_on="opa_id" + ) + + # Check if 'geometry' column exists in both dataframes and clean up + if "geometry_x" in joined.columns and "geometry_y" in joined.columns: + joined = joined.drop(columns=["geometry_y"]).copy() # Ensure a full copy + joined = joined.rename(columns={"geometry_x": "geometry"}) + + if opa_column != "opa_id": + joined = joined.drop(columns=[opa_column]) + + # Assign the joined DataFrame to self.gdf as a full copy + self.gdf = joined.copy() + self.rebuild_gdf() + + def rebuild_gdf(self): + self.gdf = gpd.GeoDataFrame(self.gdf, geometry="geometry", crs=self.crs) + + def create_centroid_gdf(self): + """ + Convert the geometry of the GeoDataFrame to centroids. + """ + self.centroid_gdf = self.gdf.copy() + self.centroid_gdf.loc[:, "geometry"] = self.gdf["geometry"].centroid + + def build_and_publish(self, tiles_file_id_prefix: str) -> None: + """ + Builds PMTiles and a Parquet file from a GeoDataFrame and publishes them to Google Cloud Storage. + + Args: + tiles_file_id_prefix (str): The ID prefix used for naming the PMTiles and Parquet files, coming from config. + + Raises: + ValueError: Raised if the generated PMTiles file is smaller than the minimum allowed size, indicating a potential corruption or incomplete file. + """ + zoom_threshold: int = 13 + + # Export the GeoDataFrame to a temporary GeoJSON file + temp_geojson_points: str = f"tmp/temp_{tiles_file_id_prefix}_points.geojson" + temp_geojson_polygons: str = f"tmp/temp_{tiles_file_id_prefix}_polygons.geojson" + temp_pmtiles_points: str = f"tmp/temp_{tiles_file_id_prefix}_points.pmtiles" + temp_pmtiles_polygons: str = f"tmp/temp_{tiles_file_id_prefix}_polygons.pmtiles" + temp_merged_pmtiles: str = f"tmp/temp_{tiles_file_id_prefix}_merged.pmtiles" + temp_parquet: str = f"tmp/{tiles_file_id_prefix}.parquet" + + # Reproject + gdf_wm = self.gdf.to_crs(epsg=4326) + gdf_wm.to_file(temp_geojson_polygons, driver="GeoJSON") + + # Create points dataset + self.centroid_gdf = self.gdf.copy() + self.centroid_gdf["geometry"] = self.centroid_gdf["geometry"].centroid + self.centroid_gdf = self.centroid_gdf.to_crs(epsg=4326) + self.centroid_gdf.to_file(temp_geojson_points, driver="GeoJSON") + + # Load the GeoJSON from the polygons, drop geometry, and save as Parquet + gdf_polygons = gpd.read_file(temp_geojson_polygons) + df_no_geom = gdf_polygons.drop(columns=["geometry"]) + + # Check if the DataFrame has fewer than 25,000 rows + num_rows, num_cols = df_no_geom.shape + if num_rows < 25000: + print( + f"Parquet file has {num_rows} rows, which is fewer than 25,000. Skipping upload." + ) + return + + # Save the DataFrame as Parquet + df_no_geom.to_parquet(temp_parquet) + + # Upload Parquet to Google Cloud Storage + blob_parquet = bucket.blob(f"{tiles_file_id_prefix}.parquet") + try: + blob_parquet.upload_from_filename(temp_parquet) + parquet_size = os.stat(temp_parquet).st_size + parquet_size_mb = parquet_size / (1024 * 1024) + print( + f"Parquet upload successful! Size: {parquet_size} bytes ({parquet_size_mb:.2f} MB), Dimensions: {num_rows} rows, {num_cols} columns." + ) + except Exception as e: + print(f"Parquet upload failed: {e}") + return + + # Command for generating PMTiles for points up to zoom level zoom_threshold + points_command: list[str] = [ + "tippecanoe", + f"--output={temp_pmtiles_points}", + f"--maximum-zoom={zoom_threshold}", + "--minimum-zoom=10", + "-zg", + "-aC", + "-r0", + temp_geojson_points, + "-l", + "vacant_properties_tiles_points", + "--force", + ] + + # Command for generating PMTiles for polygons from zoom level zoom_threshold + polygons_command: list[str] = [ + "tippecanoe", + f"--output={temp_pmtiles_polygons}", + f"--minimum-zoom={zoom_threshold}", + "--maximum-zoom=16", + "-zg", + "--no-tile-size-limit", + temp_geojson_polygons, + "-l", + "vacant_properties_tiles_polygons", + "--force", + ] + + # Command for merging the two PMTiles files into a single output file + merge_command: list[str] = [ + "tile-join", + f"--output={temp_merged_pmtiles}", + "--no-tile-size-limit", + temp_pmtiles_polygons, + temp_pmtiles_points, + "--force", + ] + + # Run the commands + for command in [points_command, polygons_command, merge_command]: + subprocess.run(command) + + write_files: list[str] = [f"{tiles_file_id_prefix}_staging.pmtiles"] + + if write_production_tiles_file: + write_files.append(f"{tiles_file_id_prefix}.pmtiles") + + # Check whether the temp saved tiles files is big enough. + file_size: int = os.stat(temp_merged_pmtiles).st_size + if file_size < min_tiles_file_size_in_bytes: + raise ValueError( + f"{temp_merged_pmtiles} is {file_size} bytes in size but should be at least {min_tiles_file_size_in_bytes}. Therefore, we are not uploading any files to the GCP bucket. The file may be corrupt or incomplete." + ) + + # Upload PMTiles to Google Cloud Storage + for file in write_files: + blob = bucket.blob(file) + try: + blob.upload_from_filename(temp_merged_pmtiles) + print(f"PMTiles upload successful for {file}!") + except Exception as e: + print(f"PMTiles upload failed for {file}: {e}") \ No newline at end of file diff --git a/data/src/constants/services.py b/data/src/constants/services.py new file mode 100644 index 00000000..dcc4daac --- /dev/null +++ b/data/src/constants/services.py @@ -0,0 +1,78 @@ +import datetime + +VACANT_PROPS_LAYERS_TO_LOAD = [ + "https://services.arcgis.com/fLeGjb7u4uXqeF9q/ArcGIS/rest/services/Vacant_Indicators_Land/FeatureServer/0/", + "https://services.arcgis.com/fLeGjb7u4uXqeF9q/ArcGIS/rest/services/Vacant_Indicators_Bldg/FeatureServer/0/", +] + +COUNCIL_DISTRICTS_TO_LOAD = [ + "https://services.arcgis.com/fLeGjb7u4uXqeF9q/arcgis/rest/services/Council_Districts_2024/FeatureServer/0/" +] + +CITY_OWNED_PROPERTIES_TO_LOAD = [ + "https://services.arcgis.com/fLeGjb7u4uXqeF9q/ArcGIS/rest/services/LAMAAssets/FeatureServer/0/" +] + +PHS_LAYERS_TO_LOAD = [ + "https://services.arcgis.com/fLeGjb7u4uXqeF9q/ArcGIS/rest/services/phs_landcare/FeatureServer/0", +] + +RCOS_LAYERS_TO_LOAD = [ + "https://services.arcgis.com/fLeGjb7u4uXqeF9q/ArcGIS/rest/services/Zoning_RCO/FeatureServer/0/" +] + +COMMUNITY_GARDENS_TO_LOAD = [ + "https://services2.arcgis.com/qjOOiLCYeUtwT7x7/arcgis/rest/services/PHS_NGT_Supported_Current_view/FeatureServer/0/" +] + +PPR_PROPERTIES_TO_LOAD = [ + "https://services.arcgis.com/fLeGjb7u4uXqeF9q/ArcGIS/rest/services/PPR_Properties/FeatureServer/0" +] + +one_year_ago = (datetime.datetime.now() - datetime.timedelta(days=365)).strftime( + "%Y-%m-%d" +) + +# Load data for complaints from L&I +COMPLAINTS_SQL_QUERY = f"SELECT address, service_request_id, subject, status, service_name, service_code, lat AS y, lon AS x FROM public_cases_fc WHERE requested_datetime >= '{one_year_ago}' AND lat IS NOT NULL" + +VIOLATIONS_SQL_QUERY = f"SELECT parcel_id_num, casenumber, casecreateddate, casetype, casestatus, violationnumber, violationcodetitle, violationstatus, opa_account_num, address, opa_owner, geocode_x AS x, geocode_y AS y FROM violations WHERE violationdate >= '{one_year_ago}' AND geocode_x IS NOT NULL" + +GUNCRIME_SQL_QUERY = f"SELECT text_general_code, dispatch_date, point_x AS x, point_y AS y FROM incidents_part1_part2 WHERE dispatch_date_time >= '{one_year_ago}' AND text_general_code IN ('Aggravated Assault Firearm', 'Robbery Firearm') AND point_x IS NOT NULL" + +DRUGCRIME_SQL_QUERY = f"SELECT text_general_code, dispatch_date, point_x AS x, point_y AS y FROM incidents_part1_part2 WHERE dispatch_date_time >= '{one_year_ago}' AND text_general_code IN ('Narcotic / Drug Law Violations') AND point_x IS NOT NULL" + +DELINQUENCIES_QUERY = "SELECT * FROM real_estate_tax_delinquencies" + +OPA_PROPERTIES_QUERY = "SELECT building_code_description, market_value, sale_date, sale_price, parcel_number, owner_1, owner_2, mailing_address_1, mailing_address_2, mailing_care_of, mailing_street, mailing_zip, mailing_city_state, zip_code, zoning, the_geom FROM opa_properties_public" + +PWD_PARCELS_QUERY = "SELECT *, the_geom FROM pwd_parcels" + +UNSAFE_BUILDINGS_QUERY = "SELECT * FROM unsafe" + +IMMINENT_DANGER_BUILDINGS_QUERY = "SELECT * FROM imm_dang" + +PERMITS_QUERY = f""" + SELECT + address, + addressobjectid, + approvedscopeofwork, + commercialorresidential, + opa_account_num, + permittype, + status, + unit_num, + unit_type, + permitissuedate, + typeofwork, + the_geom, + ST_AsGeoJSON(the_geom)::json AS the_geom_geojson + FROM permits + WHERE permitissuedate >= '{one_year_ago}' + """ + +NBHOODS_URL = "https://raw.githubusercontent.com/opendataphilly/open-geo-data/master/philadelphia-neighborhoods/philadelphia-neighborhoods.geojson" + +CENSUS_BGS_URL = ( + "https://opendata.arcgis.com/datasets/2f982bada233478ea0100528227febce_0.geojson" +) \ No newline at end of file diff --git a/data/src/main.py b/data/src/main.py index e0131edd..66a30287 100644 --- a/data/src/main.py +++ b/data/src/main.py @@ -1,155 +1,107 @@ import sys - -from new_etl.data_utils.access_process import access_process -from new_etl.data_utils.contig_neighbors import contig_neighbors -from new_etl.data_utils.dev_probability import dev_probability -from new_etl.data_utils.negligent_devs import negligent_devs -from new_etl.data_utils.opa_properties import opa_properties -from new_etl.data_utils.priority_level import priority_level -from new_etl.data_utils.vacant_properties import vacant_properties -from new_etl.data_utils.pwd_parcels import pwd_parcels -from new_etl.data_utils.city_owned_properties import city_owned_properties -from new_etl.data_utils.phs_properties import phs_properties -from new_etl.data_utils.li_violations import li_violations -from new_etl.data_utils.li_complaints import li_complaints -from new_etl.data_utils.rco_geoms import rco_geoms -from new_etl.data_utils.council_dists import council_dists -from new_etl.data_utils.tree_canopy import tree_canopy -from new_etl.data_utils.nbhoods import nbhoods -from new_etl.data_utils.gun_crimes import gun_crimes -from new_etl.data_utils.drug_crimes import drug_crimes -from new_etl.data_utils.delinquencies import delinquencies -from new_etl.data_utils.unsafe_buildings import unsafe_buildings -from new_etl.data_utils.imm_dang_buildings import imm_dang_buildings -from new_etl.data_utils.tactical_urbanism import tactical_urbanism -from new_etl.data_utils.conservatorship import conservatorship -from new_etl.data_utils.owner_type import owner_type -from new_etl.data_utils.community_gardens import community_gardens -from new_etl.data_utils.park_priority import park_priority -from new_etl.data_utils.ppr_properties import ppr_properties - import pandas as pd +import traceback +from config.psql import conn +from config.config import tiles_file_id_prefix + +from new_etl.classes.slack_reporters import send_dataframe_profile_to_slack, send_pg_stats_to_slack, send_error_to_slack +from new_etl.classes.data_diff import DiffReport +from new_etl.data_utils import * +from new_etl.database import to_postgis_with_schema # Ensure the directory containing awkde is in the Python path awkde_path = "/usr/src/app" if awkde_path not in sys.path: sys.path.append(awkde_path) -services = [ - # vacant designation - vacant_properties, # needs to run early so that other utils can make use of the `vacant` designation - # geometries/areas - pwd_parcels, - council_dists, - nbhoods, - rco_geoms, - # ownership - city_owned_properties, - phs_properties, - community_gardens, - ppr_properties, - owner_type, - # quality of life - li_violations, - li_complaints, - tree_canopy, - gun_crimes, - drug_crimes, - delinquencies, - unsafe_buildings, - imm_dang_buildings, - # development - contig_neighbors, - dev_probability, - negligent_devs, - # access/interventions - tactical_urbanism, - conservatorship, - park_priority, -] - -dataset = opa_properties() - -print("Initial Dataset:") -print("Shape:", dataset.gdf.shape) -print("Head:\n", dataset.gdf.head()) -print("NA Counts:\n", dataset.gdf.isna().sum()) - -for service in services: - dataset = service(dataset) - print(f"After {service.__name__}:") - print("Dataset type:", type(dataset.gdf).__name__) - print("Shape:", dataset.gdf.shape) - print("Head:\n", dataset.gdf.head()) - print("NA Counts:\n", dataset.gdf.isna().sum()) - -before_drop = dataset.gdf.shape[0] -dataset.gdf = dataset.gdf.drop_duplicates(subset="opa_id") -after_drop = dataset.gdf.shape[0] -print( - f"Duplicate dataset rows dropped after initial services: {before_drop - after_drop}" -) - -# Add Priority Level -dataset = priority_level(dataset) - -# Print the distribution of "priority_level" -distribution = dataset.gdf["priority_level"].value_counts() -print("Distribution of priority level:") -print(distribution) - -# Add Access Process -dataset = access_process(dataset) - -# Print the distribution of "access_process" -distribution = dataset.gdf["access_process"].value_counts() -print("Distribution of access process:") -print(distribution) - -before_drop = dataset.gdf.shape[0] -dataset.gdf = dataset.gdf.drop_duplicates(subset="opa_id") -after_drop = dataset.gdf.shape[0] -print(f"Duplicate final dataset rows droppeds: {before_drop - after_drop}") - -# Convert problematic columns to numeric -numeric_columns = [ - "market_value", - "sale_price", - "total_assessment", - "total_due", - "num_years_owed", - "permit_count", -] -for col in numeric_columns: - dataset.gdf[col] = pd.to_numeric(dataset.gdf[col], errors="coerce") - -dataset.gdf["most_recent_year_owed"] = dataset.gdf["most_recent_year_owed"].astype(str) - -print("Column data types before exporting to Parquet:") -print(dataset.gdf.dtypes) - -# Quick dataset profiling -print("\nQuick dataset profile:") - -# 1) Number of NA values per column -print("\nNumber of NA values per column:") -print(dataset.gdf.isna().sum()) - -# 2) Mean, median, and std of numeric columns -print("\nMean, Median, and Standard Deviation of numeric columns:") -numeric_columns = dataset.gdf.select_dtypes(include=["float", "int"]).columns - -for column in numeric_columns: - mean = dataset.gdf[column].mean() - median = dataset.gdf[column].median() - std = dataset.gdf[column].std() - print(f"{column}:\n Mean: {mean:.2f}\n Median: {median:.2f}\n Std: {std:.2f}") - -# 3) Number of unique values in string columns -print("\nNumber of unique values in string columns:") -string_columns = dataset.gdf.select_dtypes(include=["object", "string"]).columns -unique_values = dataset.gdf[string_columns].nunique() -print(unique_values) - -dataset.gdf.to_parquet("tmp/test_output.parquet") + +try: + + print("Starting ETL process.") + + services = [ + vacant_properties, # Run early for other utils to use the `vacant` designation + pwd_parcels, + council_dists, + nbhoods, + rco_geoms, + city_owned_properties, + phs_properties, + community_gardens, + ppr_properties, + owner_type, + li_violations, + li_complaints, + tree_canopy, + gun_crimes, + drug_crimes, + delinquencies, + unsafe_buildings, + imm_dang_buildings, + contig_neighbors, + dev_probability, + negligent_devs, + tactical_urbanism, + conservatorship, + park_priority, + ] + + print("Loading OPA properties dataset.") + dataset = opa_properties() + + for service in services: + print(f"Running service: {service.__name__}") + dataset = service(dataset) + + print("Applying final dataset transformations.") + dataset = priority_level(dataset) + dataset = access_process(dataset) + + # Drop duplicates + before_drop = dataset.gdf.shape[0] + dataset.gdf = dataset.gdf.drop_duplicates(subset="opa_id") + print(f"Duplicate rows dropped: {before_drop - dataset.gdf.shape[0]}") + + # Convert columns to numeric where necessary + numeric_columns = [ + "market_value", + "sale_price", + "total_assessment", + "total_due", + "num_years_owed", + "permit_count", + ] + dataset.gdf[numeric_columns] = dataset.gdf[numeric_columns].apply(pd.to_numeric, errors="coerce") + dataset.gdf["most_recent_year_owed"] = dataset.gdf["most_recent_year_owed"].astype(str) + + # Dataset profiling + send_dataframe_profile_to_slack(dataset.gdf, "all_properties_end") + + # Save dataset to PostgreSQL + to_postgis_with_schema(dataset.gdf, "all_properties_end", conn) + + # Generate and send diff report + diff_report = DiffReport() + diff_report.run() + + send_pg_stats_to_slack(conn) # Send PostgreSQL stats to Slack + + # Save local Parquet file + parquet_path = "tmp/test_output.parquet" + dataset.gdf.to_parquet(parquet_path) + print(f"Dataset saved to Parquet: {parquet_path}") + + # Publish only vacant properties + dataset.gdf = dataset.gdf[dataset.gdf["vacant"]] + dataset.build_and_publish(tiles_file_id_prefix) + + # Finalize + conn.commit() + conn.close() + print("ETL process completed successfully.") + +except Exception as e: + error_message = f"Error in backend job: {str(e)}\n\n{traceback.format_exc()}" + send_error_to_slack(error_message) + raise # Optionally re-raise the exception diff --git a/data/src/new_etl/classes/data_diff.py b/data/src/new_etl/classes/data_diff.py new file mode 100644 index 00000000..3dc40bfb --- /dev/null +++ b/data/src/new_etl/classes/data_diff.py @@ -0,0 +1,152 @@ +from slack_sdk import WebClient +import pandas as pd +from sqlalchemy import text +import os + +from config.psql import conn + + +class DiffReport: + def __init__( + self, conn=conn, table_name="all_properties_end", unique_id_col="opa_id" + ): + """ + Initialize the DiffReport. + + Args: + conn: SQLAlchemy connection to the database. + table_name (str): The name of the table to analyze. + unique_id_col (str): Column used as a unique identifier. + """ + self.conn = conn + self.table_name = table_name + self.unique_id_col = unique_id_col + self.latest_timestamp = None + self.previous_timestamp = None + self.summary_text = "" + + def generate_diff(self): + """ + Generate the data diff and summarize changes. + """ + # Step 1: Retrieve the two most recent timestamps + query_timestamps = text(f""" + SELECT DISTINCT create_date + FROM {self.table_name} + ORDER BY create_date DESC + LIMIT 2; + """) + + timestamps = pd.read_sql(query_timestamps, self.conn)["create_date"].tolist() + + if len(timestamps) < 2: + print( + f"Table '{self.table_name}' has less than two timestamps. Cannot perform comparison." + ) + return + + self.latest_timestamp, self.previous_timestamp = timestamps[0], timestamps[1] + + print("Last two timestamps are:") + print(self.latest_timestamp) + print(self.previous_timestamp) + + # Step 2: Load data for the two timestamps into DataFrames + query_latest = text(f""" + SELECT * FROM {self.table_name} WHERE create_date = '{self.latest_timestamp}'; + """) + query_previous = text(f""" + SELECT * FROM {self.table_name} WHERE create_date = '{self.previous_timestamp}'; + """) + + df_latest = pd.read_sql(query_latest, self.conn) + df_previous = pd.read_sql(query_previous, self.conn) + + # Step 3: Ensure DataFrames are aligned on the same index and columns + common_columns = [ + col for col in df_latest.columns if col in df_previous.columns + ] + df_latest = df_latest[common_columns] + df_previous = df_previous[common_columns] + + # Align indexes to include all rows from both DataFrames + df_latest = df_latest.set_index(self.unique_id_col).reindex( + df_previous.index.union(df_latest.index) + ) + df_previous = df_previous.set_index(self.unique_id_col).reindex( + df_previous.index.union(df_latest.index) + ) + + # Ensure columns are in the same order + df_latest = df_latest[sorted(df_latest.columns)] + df_previous = df_previous[sorted(df_previous.columns)] + + # Step 4: Perform the comparison + diff = df_latest.compare( + df_previous, align_axis=1, keep_shape=False, keep_equal=False + ) + + if diff.empty: + print("No changes detected between the two timestamps.") + self.summary_text = "No changes detected between the two timestamps." + return + + # Step 5: Calculate percentage changes + print("Calculating percentages...") + total_rows = len(df_latest) + changes_by_column = { + col: (diff.xs(col, level=0, axis=1).notna().sum().sum() / total_rows) * 100 + for col in diff.columns.get_level_values(0).unique() + } + + # Step 6: Create plain text summary + summary_lines = [ + f"Diff Report for {self.table_name}", + f"Latest timestamp: {self.latest_timestamp}", + f"Previous timestamp: {self.previous_timestamp}", + "", + "Comparison Summary (% of rows with changes per column):", + ] + + for col, pct_change in sorted( + changes_by_column.items(), key=lambda x: x[1], reverse=True + ): + summary_lines.append(f" - {col}: {pct_change:.2f}%") + + self.summary_text = "\n".join(summary_lines) + + def send_to_slack(self, channel="clean-and-green-philly-pipeline"): + """ + Sends the diff summary to a Slack channel. + + Args: + channel (str): The Slack channel to post the message to. + """ + token = os.getenv("CAGP_SLACK_API_TOKEN") + if token: + client = WebClient(token=token) + try: + client.chat_postMessage( + channel=channel, + text=f"*Data Difference Report*\n\n{self.summary_text}", + username="Diff Reporter", + ) + print("Diff report sent to Slack successfully.") + except Exception as e: + print(f"Failed to send diff report to Slack: {e}") + else: + raise ValueError("Slack API token not found in environment variables.") + + def run(self, send_to_slack=True, slack_channel="clean-and-green-philly-pipeline"): + """ + Orchestrates the diff generation and optional Slack notification. + + Args: + send_to_slack (bool): Whether to send the diff summary to Slack. + slack_channel (str): The Slack channel to post the message to. + """ + self.generate_diff() + + if send_to_slack and self.summary_text: + print(f"Sending report to Slack channel: {slack_channel}...") + self.send_to_slack(channel=slack_channel) diff --git a/data/src/new_etl/classes/featurelayer.py b/data/src/new_etl/classes/featurelayer.py index fce57fbf..9e5fd08b 100644 --- a/data/src/new_etl/classes/featurelayer.py +++ b/data/src/new_etl/classes/featurelayer.py @@ -10,22 +10,21 @@ from config.config import ( FORCE_RELOAD, USE_CRS, + log_level, min_tiles_file_size_in_bytes, write_production_tiles_file, ) from config.psql import conn, local_engine -from esridump.dumper import EsriDumper from google.cloud import storage from google.cloud.storage.bucket import Bucket from shapely import wkb - from concurrent.futures import ThreadPoolExecutor, as_completed -from tqdm.auto import tqdm - from tqdm import tqdm +from new_etl.loaders import load_esri_data, load_carto_data +from new_etl.database import to_postgis_with_schema -log.basicConfig(level=log.INFO) +log.basicConfig(level=log_level) def google_cloud_bucket() -> Bucket: @@ -62,7 +61,7 @@ def __init__( from_xy=False, use_wkb_geom_field=None, cols: list[str] = None, - max_workers=16, + max_workers=os.cpu_count(), chunk_size=100000, ): self.name = name @@ -118,75 +117,43 @@ def check_psql(self): def load_data(self): log.info(f"Loading data for {self.name} from {self.type}...") - if self.type == "gdf": - pass - else: - try: - if self.type == "esri": - if not self.esri_rest_urls: - raise ValueError("Must provide a URL to load data from Esri") - - gdfs = [] - for url in self.esri_rest_urls: - print(f"Processing URL: {url}") # Debugging: Print the URL - - # Use EsriDumper to get features - dumper = EsriDumper(url) - features = [feature for feature in dumper] - - if not features: - log.error(f"No features returned for URL: {url}") - continue - - geojson_features = { - "type": "FeatureCollection", - "features": features, - } - gdf = gpd.GeoDataFrame.from_features( - geojson_features, crs=self.input_crs - ) - gdf = gdf.to_crs(self.crs) - - # Add parcel_type based on the URL - if "Vacant_Indicators_Land" in url: - gdf["parcel_type"] = "Land" - elif "Vacant_Indicators_Bldg" in url: - gdf["parcel_type"] = "Building" - - gdfs.append(gdf) - # Concatenate all dataframes - self.gdf = pd.concat(gdfs, ignore_index=True) + if self.type == "gdf": + return # Skip processing for gdf type - elif self.type == "carto": - self._load_carto_data() + try: + # Load data based on the source type + if self.type == "esri": + self.gdf = load_esri_data(self.esri_rest_urls, self.input_crs, self.crs) + elif self.type == "carto": + self.gdf = load_carto_data( + self.carto_sql_queries, + self.max_workers, + self.chunk_size, + self.use_wkb_geom_field, + self.input_crs, + self.crs, + ) - # Convert all column names to lowercase - if not self.gdf.empty: - self.gdf.columns = [col.lower() for col in self.gdf.columns] + # Standardize column names + if not self.gdf.empty: + self.gdf.columns = [col.lower() for col in self.gdf.columns] - # Drop columns not in self.cols, if specified + # Filter columns if specified if self.cols: - self.cols = [ - col.lower() for col in self.cols - ] # Ensure self.cols is lowercase + self.cols = [col.lower() for col in self.cols] self.cols.append("geometry") self.gdf = self.gdf[ [col for col in self.cols if col in self.gdf.columns] ] - # Save to PostGIS - self.gdf.to_postgis( - name=self.psql_table, - con=conn, - if_exists="replace", - chunksize=1000, - ) + # Save GeoDataFrame to PostgreSQL and configure it as a hypertable + to_postgis_with_schema(self.gdf, self.psql_table, conn) - except Exception as e: - log.error(f"Error loading data for {self.name}: {e}") - traceback.print_exc() - self.gdf = gpd.GeoDataFrame() + except Exception as e: + log.error(f"Error loading data for {self.name}: {e}") + traceback.print_exc() + self.gdf = gpd.GeoDataFrame() # Reset to an empty GeoDataFrame def _load_carto_data(self): if not self.carto_sql_queries: @@ -324,7 +291,6 @@ def build_and_publish(self, tiles_file_id_prefix: str) -> None: temp_pmtiles_points: str = f"tmp/temp_{tiles_file_id_prefix}_points.pmtiles" temp_pmtiles_polygons: str = f"tmp/temp_{tiles_file_id_prefix}_polygons.pmtiles" temp_merged_pmtiles: str = f"tmp/temp_{tiles_file_id_prefix}_merged.pmtiles" - temp_parquet: str = f"tmp/{tiles_file_id_prefix}.parquet" # Reproject gdf_wm = self.gdf.to_crs(epsg=4326) diff --git a/data/src/new_etl/classes/slack_reporters.py b/data/src/new_etl/classes/slack_reporters.py new file mode 100644 index 00000000..6ae08517 --- /dev/null +++ b/data/src/new_etl/classes/slack_reporters.py @@ -0,0 +1,177 @@ +from sqlalchemy import text +import os +from slack_sdk import WebClient + +import pandas as pd + + +def send_dataframe_profile_to_slack( + df: pd.DataFrame, df_name: str, channel="clean-and-green-philly-pipeline" +): + """ + Profiles a DataFrame and sends the QC profile summary to a Slack channel. + + Args: + df (pd.DataFrame): The DataFrame to profile. + df_name (str): The name of the DataFrame being profiled. + channel (str): The Slack channel to post the message to. + """ + # Step 1: Profile the DataFrame + profile_summary = {} + + # Count NA values + profile_summary["na_counts"] = df.isna().sum().to_dict() + + # Numeric stats + numeric_columns = df.select_dtypes(include=["number"]).columns + profile_summary["numeric_stats"] = {} + for column in numeric_columns: + profile_summary["numeric_stats"][column] = { + "mean": df[column].mean(), + "median": df[column].median(), + "std": df[column].std(), + } + + # Unique values in string columns + string_columns = df.select_dtypes(include=["object", "string"]).columns + profile_summary["unique_values"] = df[string_columns].nunique().to_dict() + + # Step 2: Format the message + message = f"*Dataset QC Summary: `{df_name}`*\n" + + # Missing Values + message += "*Missing Values:*\n" + for col, count in profile_summary["na_counts"].items(): + message += f" - `{col}`: {count} missing\n" + + # Numeric Summary + message += "\n*Numeric Summary:*\n" + for col, stats in profile_summary["numeric_stats"].items(): + message += ( + f" - `{col}`: Mean: {stats['mean']:.2f}, " + f"Median: {stats['median']:.2f}, Std: {stats['std']:.2f}\n" + ) + + # Unique Values + message += "\n*Unique Values in String Columns:*\n" + for col, unique_count in profile_summary["unique_values"].items(): + message += f" - `{col}`: {unique_count} unique values\n" + + # Step 3: Send to Slack + token = os.getenv("CAGP_SLACK_API_TOKEN") + if token: + client = WebClient(token=token) + try: + client.chat_postMessage( + channel=channel, + text=message, + username="QC Reporter", + ) + print(f"QC profile for `{df_name}` sent to Slack successfully.") + except Exception as e: + print(f"Failed to send QC profile for `{df_name}` to Slack: {e}") + else: + raise ValueError("Slack API token not found in environment variables.") + + +def send_pg_stats_to_slack(conn): + """ + Report total sizes for all hypertables using hypertable_detailed_size + and send the result to a Slack channel. + """ + # Step 1: Get all hypertable names + hypertable_query = """ + SELECT hypertable_name + FROM timescaledb_information.hypertables; + """ + result = conn.execute(text(hypertable_query)) + hypertables = [row[0] for row in result] # Extract first column of each tuple + + # Step 2: Query detailed size for each hypertable + detailed_sizes = [] + for hypertable in hypertables: + size_query = f"SELECT * FROM hypertable_detailed_size('{hypertable}');" + size_result = conn.execute(text(size_query)) + for row in size_result: + # Append the total size (row[3] = total_bytes) + detailed_sizes.append( + { + "hypertable": hypertable, + "total_bytes": row[3], + } + ) + + # Step 3: Format the message for Slack + message = "*Hypertable Total Sizes:*\n" + for size in detailed_sizes: + total_bytes = size["total_bytes"] + total_size = ( + f"{total_bytes / 1073741824:.2f} GB" + if total_bytes >= 1073741824 + else f"{total_bytes / 1048576:.2f} MB" + if total_bytes >= 1048576 + else f"{total_bytes / 1024:.2f} KB" + ) + message += f"- {size['hypertable']}: {total_size}\n" + + # Step 4: Send to Slack + token = os.getenv("CAGP_SLACK_API_TOKEN") + if token: + client = WebClient(token=token) + client.chat_postMessage( + channel="clean-and-green-philly-pipeline", + text=message, + username="PG Stats Reporter", + ) + else: + raise ValueError("Slack API token not found in environment variables.") + + +def send_diff_report_to_slack( + diff_summary: str, report_url: str, channel="clean-and-green-philly-pipeline" +): + """ + Sends a difference report summary to a Slack channel. + + Args: + diff_summary (str): The summary of differences to post. + report_url (str): The URL to the detailed difference report. + channel (str): The Slack channel to post the message to. + """ + print( + f"send_diff_report_to_slack called with:\ndiff_summary:\n{diff_summary}\nreport_url: {report_url}" + ) + + # Step 1: Format the message + message = f"*Data Difference Report*\n\n{diff_summary}\n\nDetailed report: <{report_url}|View Report>" + print(f"Formatted Slack message:\n{message}") + + # Step 2: Send the message to Slack + token = os.getenv("CAGP_SLACK_API_TOKEN") + if token: + client = WebClient(token=token) + try: + client.chat_postMessage( + channel=channel, + text=message, + username="Diff Reporter", + ) + print("Diff report sent to Slack successfully.") + except Exception as e: + print(f"Failed to send diff report to Slack: {e}") + else: + raise ValueError("Slack API token not found in environment variables.") + + +def send_error_to_slack(error_message: str) -> None: + """Send error message to Slack.""" + token: str | None = os.getenv("CAGP_SLACK_API_TOKEN") # token can be None + if token: + client = WebClient(token=token) + client.chat_postMessage( + channel="clean-and-green-philly-back-end", # Replace with actual Slack channel ID + text=error_message, + username="Backend Error Reporter", + ) + else: + raise ValueError("Slack API token not found in environment variables.") diff --git a/data/src/new_etl/data_utils/__init__.py b/data/src/new_etl/data_utils/__init__.py index d351ecd4..e464f7c8 100644 --- a/data/src/new_etl/data_utils/__init__.py +++ b/data/src/new_etl/data_utils/__init__.py @@ -6,11 +6,25 @@ from .tree_canopy import tree_canopy from .nbhoods import nbhoods from .gun_crimes import gun_crimes +from .drug_crimes import drug_crimes # Add missing import from .delinquencies import delinquencies from .opa_properties import opa_properties from .vacant_properties import vacant_properties from .priority_level import priority_level from .access_process import access_process +from .contig_neighbors import contig_neighbors # Add missing import +from .dev_probability import dev_probability # Add missing import +from .negligent_devs import negligent_devs # Add missing import +from .pwd_parcels import pwd_parcels # Add missing import +from .unsafe_buildings import unsafe_buildings # Add missing import +from .imm_dang_buildings import imm_dang_buildings # Add missing import +from .tactical_urbanism import tactical_urbanism # Add missing import +from .conservatorship import conservatorship # Add missing import +from .owner_type import owner_type # Add missing import +from .community_gardens import community_gardens # Add missing import +from .park_priority import park_priority # Add missing import +from .ppr_properties import ppr_properties # Add missing import +from .council_dists import council_dists __all__ = [ "city_owned_properties", @@ -21,9 +35,23 @@ "tree_canopy", "nbhoods", "gun_crimes", + "drug_crimes", # Ensure completeness "delinquencies", "opa_properties", "vacant_properties", "priority_level", "access_process", + "contig_neighbors", + "dev_probability", + "negligent_devs", + "pwd_parcels", + "unsafe_buildings", + "imm_dang_buildings", + "tactical_urbanism", + "conservatorship", + "owner_type", + "community_gardens", + "park_priority", + "ppr_properties", + "council_dists", ] diff --git a/data/src/new_etl/data_utils/city_owned_properties.py b/data/src/new_etl/data_utils/city_owned_properties.py index e9d93059..7e967712 100644 --- a/data/src/new_etl/data_utils/city_owned_properties.py +++ b/data/src/new_etl/data_utils/city_owned_properties.py @@ -22,8 +22,6 @@ def city_owned_properties(primary_featurelayer: FeatureLayer) -> FeatureLayer: cols=["OPABRT", "AGENCY", "SIDEYARDELIGIBLE"], ) - print("Columns for city_owned_properties:", city_owned_properties.gdf.columns) - city_owned_properties.gdf.dropna(subset=["opabrt"], inplace=True) primary_featurelayer.opa_join(city_owned_properties.gdf, "opabrt") diff --git a/data/src/new_etl/data_utils/community_gardens.py b/data/src/new_etl/data_utils/community_gardens.py index a47b6760..80fa00df 100644 --- a/data/src/new_etl/data_utils/community_gardens.py +++ b/data/src/new_etl/data_utils/community_gardens.py @@ -12,11 +12,6 @@ def community_gardens(primary_featurelayer: FeatureLayer) -> FeatureLayer: if "vacant" not in primary_featurelayer.gdf.columns: raise ValueError("The 'vacant' column is missing in the primary feature layer.") - print( - "Geometry types in primary feature layer:", - primary_featurelayer.gdf.geometry.type.value_counts(), - ) - # Load community gardens community_gardens = FeatureLayer( name="Community Gardens", esri_rest_urls=COMMUNITY_GARDENS_TO_LOAD @@ -29,23 +24,11 @@ def community_gardens(primary_featurelayer: FeatureLayer) -> FeatureLayer: ) community_gardens.gdf = community_gardens.gdf.to_crs(USE_CRS) - # Check geometry types + # Identify problematic gardens geom_types = community_gardens.gdf.geometry.geom_type.value_counts() - print("\nCommunity gardens geometry types:") - print(geom_types) - # Identify problematic gardens if len(geom_types) > 1: - print("\nGardens with non-Point geometries:") - non_point_gardens = community_gardens.gdf[ - community_gardens.gdf.geometry.geom_type != "Point" - ] - print(f"Total non-Point geometries: {len(non_point_gardens)}") - print("\nSample of problematic records:") - print(non_point_gardens[["site_name", "geometry"]].head()) - # Convert any non-point geometries to points using centroid - print("\nConverting non-Point geometries to points using centroids...") community_gardens.gdf.loc[ community_gardens.gdf.geometry.geom_type != "Point", "geometry" ] = community_gardens.gdf[ @@ -66,38 +49,6 @@ def community_gardens(primary_featurelayer: FeatureLayer) -> FeatureLayer: community_gardens.gdf, predicate="contains", how="inner" ) - # Count matches per garden - matches_per_garden = joined_gdf.groupby("site_name").size() - print("\nMatches per garden:") - print(f"Min matches: {matches_per_garden.min()}") - print(f"Max matches: {matches_per_garden.max()}") - print(f"Average matches: {matches_per_garden.mean():.2f}") - - # Print details for gardens with unusually high matches - # Gardens with high number of matches - if matches_per_garden.max() > 1: # arbitrary threshold - print("\nGardens with high number of matches:") - high_matches = matches_per_garden[matches_per_garden > 1] - print(high_matches) - - # Print concise details about properties matching these gardens - print("\nSummary of matched properties for high-match gardens:") - for garden_name in high_matches.index: - matched_properties = joined_gdf[joined_gdf["site_name"] == garden_name] - print(f"\nGarden: {garden_name}") - print("Matched parcels:") - print( - matched_properties[["opa_id"]] - .drop_duplicates() - .head(5) - .to_string(index=False) - ) - print( - f"...and {len(matched_properties) - 5} more matches." - if len(matched_properties) > 5 - else "" - ) - # Get unique parcels that contain garden points garden_parcels = set(joined_gdf["opa_id"]) print(f"\nUnique parcels containing gardens: {len(garden_parcels)}") diff --git a/data/src/new_etl/data_utils/conservatorship.py b/data/src/new_etl/data_utils/conservatorship.py index 2294e3ac..4e53c0fe 100644 --- a/data/src/new_etl/data_utils/conservatorship.py +++ b/data/src/new_etl/data_utils/conservatorship.py @@ -1,3 +1,4 @@ +from ..classes.featurelayer import FeatureLayer import datetime from dateutil.parser import parse import pytz @@ -8,7 +9,17 @@ ) -def conservatorship(primary_featurelayer): +def conservatorship(primary_featurelayer: FeatureLayer) -> FeatureLayer: + """ + Determines conservatorship eligibility for properties in a feature layer. + + Args: + primary_featurelayer (FeatureLayer): A feature layer containing property data in a GeoDataFrame (`gdf`). + + Returns: + FeatureLayer: The input feature layer with an added "conservatorship" column indicating + whether each property qualifies for conservatorship ("Yes" or "No"). + """ conservatorships = [] for idx, row in primary_featurelayer.gdf.iterrows(): diff --git a/data/src/new_etl/data_utils/contig_neighbors.py b/data/src/new_etl/data_utils/contig_neighbors.py index 48656bbd..c05f9d4c 100644 --- a/data/src/new_etl/data_utils/contig_neighbors.py +++ b/data/src/new_etl/data_utils/contig_neighbors.py @@ -2,9 +2,20 @@ import networkx as nx from libpysal.weights import Queen import numpy as np +from ..classes.featurelayer import FeatureLayer -def contig_neighbors(primary_featurelayer): +def contig_neighbors(primary_featurelayer: FeatureLayer) -> FeatureLayer: + """ + Calculates the number of contiguous vacant neighbors for each property in a feature layer. + + Args: + primary_featurelayer (FeatureLayer): A feature layer containing property data in a GeoDataFrame (`gdf`). + + Returns: + FeatureLayer: The input feature layer with an added "n_contiguous" column indicating + the number of contiguous vacant neighbors for each property. + """ # Create a filtered dataframe with only vacant properties and polygon geometries vacant_parcels = primary_featurelayer.gdf.loc[ (primary_featurelayer.gdf["vacant"]) @@ -17,8 +28,6 @@ def contig_neighbors(primary_featurelayer): primary_featurelayer.gdf["n_contiguous"] = np.nan return primary_featurelayer - print(f"Found {len(vacant_parcels)} vacant properties.") - with warnings.catch_warnings(): warnings.filterwarnings("ignore", category=FutureWarning) warnings.filterwarnings( @@ -28,15 +37,12 @@ def contig_neighbors(primary_featurelayer): ) # Create a spatial weights matrix for vacant parcels - print("Creating spatial weights matrix for vacant parcels...") w = Queen.from_dataframe(vacant_parcels) # Convert the spatial weights matrix to a NetworkX graph - print("Converting spatial weights matrix to NetworkX graph...") g = w.to_networkx() # Calculate the number of contiguous vacant properties for each vacant parcel - print("Calculating number of contiguous vacant neighbors for each property...") n_contiguous = { node: len(nx.node_connected_component(g, node)) - 1 for node in g.nodes } @@ -54,5 +60,4 @@ def contig_neighbors(primary_featurelayer): ~primary_featurelayer.gdf["vacant"], "n_contiguous" ] = np.nan - print("Process completed. Returning updated primary feature layer.") return primary_featurelayer diff --git a/data/src/new_etl/data_utils/council_dists.py b/data/src/new_etl/data_utils/council_dists.py index 6ef8b8ee..74709709 100644 --- a/data/src/new_etl/data_utils/council_dists.py +++ b/data/src/new_etl/data_utils/council_dists.py @@ -2,11 +2,21 @@ from ..constants.services import COUNCIL_DISTRICTS_TO_LOAD import pandas as pd - pd.set_option("future.no_silent_downcasting", True) -def council_dists(primary_featurelayer): +def council_dists(primary_featurelayer: FeatureLayer) -> FeatureLayer: + """ + Associates properties in the primary feature layer with council districts + using a spatial join. + + Args: + primary_featurelayer (FeatureLayer): The feature layer containing property data. + + Returns: + FeatureLayer: The input feature layer with properties spatially joined + to council districts, ensuring no duplicate entries. + """ # Load council districts council_dists = FeatureLayer( name="Council Districts", esri_rest_urls=COUNCIL_DISTRICTS_TO_LOAD diff --git a/data/src/new_etl/data_utils/delinquencies.py b/data/src/new_etl/data_utils/delinquencies.py index 7512c163..700372b2 100644 --- a/data/src/new_etl/data_utils/delinquencies.py +++ b/data/src/new_etl/data_utils/delinquencies.py @@ -2,7 +2,18 @@ from ..constants.services import DELINQUENCIES_QUERY -def delinquencies(primary_featurelayer): +def delinquencies(primary_featurelayer: FeatureLayer) -> FeatureLayer: + """ + Adds property tax delinquency information to the primary feature layer by + joining with a tax delinquencies dataset. + + Args: + primary_featurelayer (FeatureLayer): The feature layer containing property data. + + Returns: + FeatureLayer: The input feature layer with added columns for tax delinquency + information, including total due, actionable status, payment agreements, and more. + """ tax_delinquencies = FeatureLayer( name="Property Tax Delinquencies", carto_sql_queries=DELINQUENCIES_QUERY, diff --git a/data/src/new_etl/data_utils/dev_probability.py b/data/src/new_etl/data_utils/dev_probability.py index ab36ea31..325b2e04 100644 --- a/data/src/new_etl/data_utils/dev_probability.py +++ b/data/src/new_etl/data_utils/dev_probability.py @@ -4,11 +4,22 @@ import requests from ..classes.featurelayer import FeatureLayer from ..constants.services import CENSUS_BGS_URL, PERMITS_QUERY - from config.config import USE_CRS -def dev_probability(primary_featurelayer): +def dev_probability(primary_featurelayer: FeatureLayer) -> FeatureLayer: + """ + Calculates development probability based on permit counts and assigns + development ranks to census block groups. The results are joined to the + primary feature layer. + + Args: + primary_featurelayer (FeatureLayer): The feature layer containing property data. + + Returns: + FeatureLayer: The input feature layer with added spatial join data for + development probability and ranks. + """ census_bgs_gdf = gpd.read_file(CENSUS_BGS_URL) census_bgs_gdf = census_bgs_gdf.to_crs(USE_CRS) diff --git a/data/src/new_etl/data_utils/drug_crimes.py b/data/src/new_etl/data_utils/drug_crimes.py index 371fac36..479ac08a 100644 --- a/data/src/new_etl/data_utils/drug_crimes.py +++ b/data/src/new_etl/data_utils/drug_crimes.py @@ -1,10 +1,18 @@ +from ..classes.featurelayer import FeatureLayer from ..constants.services import DRUGCRIME_SQL_QUERY +from new_etl.data_utils.kde import apply_kde_to_primary -from new_etl.data_utils.kde import apply_kde_to_primary +def drug_crimes(primary_featurelayer: FeatureLayer) -> FeatureLayer: + """ + Applies kernel density estimation (KDE) analysis for drug crimes to the primary feature layer. + Args: + primary_featurelayer (FeatureLayer): The feature layer containing property data. -def drug_crimes(primary_featurelayer): + Returns: + FeatureLayer: The input feature layer with KDE analysis results for drug crimes. + """ return apply_kde_to_primary( primary_featurelayer, "Drug Crimes", DRUGCRIME_SQL_QUERY ) diff --git a/data/src/new_etl/data_utils/gun_crimes.py b/data/src/new_etl/data_utils/gun_crimes.py index 0b038eef..e9f2d1fd 100644 --- a/data/src/new_etl/data_utils/gun_crimes.py +++ b/data/src/new_etl/data_utils/gun_crimes.py @@ -1,8 +1,16 @@ +from ..classes.featurelayer import FeatureLayer from ..constants.services import GUNCRIME_SQL_QUERY +from new_etl.data_utils.kde import apply_kde_to_primary -from new_etl.data_utils.kde import apply_kde_to_primary +def gun_crimes(primary_featurelayer: FeatureLayer) -> FeatureLayer: + """ + Applies kernel density estimation (KDE) analysis for gun crimes to the primary feature layer. + Args: + primary_featurelayer (FeatureLayer): The feature layer containing property data. -def gun_crimes(primary_featurelayer): + Returns: + FeatureLayer: The input feature layer with KDE analysis results for gun crimes. + """ return apply_kde_to_primary(primary_featurelayer, "Gun Crimes", GUNCRIME_SQL_QUERY) diff --git a/data/src/new_etl/data_utils/imm_dang_buildings.py b/data/src/new_etl/data_utils/imm_dang_buildings.py index 628c88b0..5163be63 100644 --- a/data/src/new_etl/data_utils/imm_dang_buildings.py +++ b/data/src/new_etl/data_utils/imm_dang_buildings.py @@ -2,7 +2,18 @@ from ..constants.services import IMMINENT_DANGER_BUILDINGS_QUERY -def imm_dang_buildings(primary_featurelayer): +def imm_dang_buildings(primary_featurelayer: FeatureLayer) -> FeatureLayer: + """ + Adds information about imminently dangerous buildings to the primary feature layer + by joining with a dataset of dangerous buildings. + + Args: + primary_featurelayer (FeatureLayer): The feature layer containing property data. + + Returns: + FeatureLayer: The input feature layer with an added "imm_dang_building" column, + indicating whether each property is categorized as imminently dangerous ("Y" or "N"). + """ imm_dang_buildings = FeatureLayer( name="Imminently Dangerous Buildings", use_wkb_geom_field="the_geom", diff --git a/data/src/new_etl/data_utils/kde.py b/data/src/new_etl/data_utils/kde.py index b3789f74..63cb4df4 100644 --- a/data/src/new_etl/data_utils/kde.py +++ b/data/src/new_etl/data_utils/kde.py @@ -1,3 +1,4 @@ +from typing import Tuple import numpy as np import rasterio from awkde.awkde import GaussianKDE @@ -6,26 +7,47 @@ from rasterio.transform import Affine from tqdm import tqdm from concurrent.futures import ProcessPoolExecutor, as_completed - import mapclassify -resolution = 1320 # 0.25 miles (in feet, bc the CRS is 2272) +resolution = 1320 # 0.25 miles (in feet, since the CRS is 2272) batch_size = 100000 -def kde_predict_chunk(kde, chunk): - """Helper function to predict KDE for a chunk of grid points.""" +def kde_predict_chunk(kde: GaussianKDE, chunk: np.ndarray) -> np.ndarray: + """ + Helper function to predict KDE for a chunk of grid points. + + Args: + kde (GaussianKDE): The KDE model to use for prediction. + chunk (np.ndarray): A chunk of grid points for prediction. + + Returns: + np.ndarray: Predicted KDE values for the chunk. + """ return kde.predict(chunk) -def generic_kde(name, query, resolution=resolution, batch_size=batch_size): +def generic_kde( + name: str, query: str, resolution: int = resolution, batch_size: int = batch_size +) -> Tuple[str, np.ndarray]: + """ + Generates a raster file and grid points from kernel density estimation (KDE) for a dataset. + + Args: + name (str): Name of the dataset being processed. + query (str): SQL query to fetch data. + resolution (int): Resolution for the grid. Defaults to 1320. + batch_size (int): Batch size for processing grid points. Defaults to 100000. + + Returns: + Tuple[str, np.ndarray]: The raster filename and the array of input points. + """ print(f"Initializing FeatureLayer for {name}") feature_layer = FeatureLayer(name=name, carto_sql_queries=query) coords = np.array([geom.xy for geom in feature_layer.gdf.geometry]) x, y = coords[:, 0, :].flatten(), coords[:, 1, :].flatten() - X = np.column_stack((x, y)) x_grid, y_grid = ( @@ -41,22 +63,17 @@ def generic_kde(name, query, resolution=resolution, batch_size=batch_size): print(f"Predicting KDE values for grid of size {grid_points.shape}") - # Split grid points into chunks chunks = [ grid_points[i : i + batch_size] for i in range(0, len(grid_points), batch_size) ] - - # Run predictions in parallel - z = np.zeros(len(grid_points)) # Placeholder for predicted values + z = np.zeros(len(grid_points)) with ProcessPoolExecutor() as executor: - # Submit the tasks first, wrapped with tqdm to monitor as they're submitted futures = { executor.submit(kde_predict_chunk, kde, chunk): i for i, chunk in enumerate(tqdm(chunks, desc="Submitting tasks")) } - # Now wrap the as_completed with tqdm for progress tracking for future in tqdm( as_completed(futures), total=len(futures), desc="Processing tasks" ): @@ -92,14 +109,29 @@ def generic_kde(name, query, resolution=resolution, batch_size=batch_size): return raster_filename, X -def apply_kde_to_primary(primary_featurelayer, name, query, resolution=resolution): - # Generate KDE and raster file +def apply_kde_to_primary( + primary_featurelayer: FeatureLayer, + name: str, + query: str, + resolution: int = resolution, +) -> FeatureLayer: + """ + Applies KDE to the primary feature layer and adds columns for density, z-score, + percentile, and percentile as a string. + + Args: + primary_featurelayer (FeatureLayer): The feature layer containing property data. + name (str): Name of the KDE feature. + query (str): SQL query to fetch data for KDE. + resolution (int): Resolution for the KDE raster grid. + + Returns: + FeatureLayer: The input feature layer with added KDE-related columns. + """ raster_filename, crime_coords = generic_kde(name, query, resolution) - # Add centroid column temporarily primary_featurelayer.gdf["centroid"] = primary_featurelayer.gdf.geometry.centroid - # Create list of (x, y) coordinates for centroids coord_list = [ (x, y) for x, y in zip( @@ -108,45 +140,50 @@ def apply_kde_to_primary(primary_featurelayer, name, query, resolution=resolutio ) ] - # Remove the temporary centroid column primary_featurelayer.gdf = primary_featurelayer.gdf.drop(columns=["centroid"]) - # Open the generated raster file and sample the KDE density values at the centroids with rasterio.open(raster_filename) as src: sampled_values = [x[0] for x in src.sample(coord_list)] - # Create a column for the density values density_column = f"{name.lower().replace(' ', '_')}_density" primary_featurelayer.gdf[density_column] = sampled_values - # Calculate percentiles using mapclassify.Percentiles - percentile_breaks = list(range(101)) # Percentile breaks from 0 to 100 + # Calculate z-scores + mean_density = primary_featurelayer.gdf[density_column].mean() + std_density = primary_featurelayer.gdf[density_column].std() + z_score_column = f"{density_column}_zscore" + primary_featurelayer.gdf[z_score_column] = ( + primary_featurelayer.gdf[density_column] - mean_density + ) / std_density + + # Calculate percentiles + percentile_breaks = list(range(101)) classifier = mapclassify.Percentiles( primary_featurelayer.gdf[density_column], pct=percentile_breaks ) + percentile_column = f"{density_column}_percentile" + primary_featurelayer.gdf[percentile_column] = classifier.yb.astype(float) - # Assign the percentile bins to the density values - primary_featurelayer.gdf[density_column + "_percentile"] = ( - classifier.yb - ) # yb gives the bin index - - # Apply percentile labels (e.g., 1st Percentile, 2nd Percentile, etc.) - primary_featurelayer.gdf[density_column + "_label"] = primary_featurelayer.gdf[ - density_column + "_percentile" + # Assign percentile labels + label_column = f"{density_column}_label" + primary_featurelayer.gdf[label_column] = primary_featurelayer.gdf[ + percentile_column ].apply(label_percentile) - # Convert the percentile column to float and drop the density column - primary_featurelayer.gdf[density_column + "_percentile"] = primary_featurelayer.gdf[ - density_column + "_percentile" - ].astype(float) - - primary_featurelayer.gdf = primary_featurelayer.gdf.drop(columns=[density_column]) - print(f"Finished processing {name}") return primary_featurelayer -def label_percentile(value): +def label_percentile(value: float) -> str: + """ + Converts a percentile value into a human-readable string. + + Args: + value (float): The percentile value. + + Returns: + str: The formatted percentile string (e.g., '1st Percentile'). + """ if 10 <= value % 100 <= 13: return f"{value}th Percentile" elif value % 10 == 1: diff --git a/data/src/new_etl/data_utils/li_complaints.py b/data/src/new_etl/data_utils/li_complaints.py index c6253d4c..3778cd23 100644 --- a/data/src/new_etl/data_utils/li_complaints.py +++ b/data/src/new_etl/data_utils/li_complaints.py @@ -1,10 +1,19 @@ +from ..classes.featurelayer import FeatureLayer from ..constants.services import COMPLAINTS_SQL_QUERY +from ..data_utils.kde import apply_kde_to_primary -from ..data_utils.kde import apply_kde_to_primary +def li_complaints(primary_featurelayer: FeatureLayer) -> FeatureLayer: + """ + Applies kernel density estimation (KDE) analysis for L&I complaints to the primary feature layer. + Args: + primary_featurelayer (FeatureLayer): The feature layer containing property data. -def li_complaints(primary_featurelayer): + Returns: + FeatureLayer: The input feature layer with KDE analysis results for L&I complaints, + including density and derived metrics. + """ return apply_kde_to_primary( primary_featurelayer, "L and I Complaints", COMPLAINTS_SQL_QUERY ) diff --git a/data/src/new_etl/data_utils/nbhoods.py b/data/src/new_etl/data_utils/nbhoods.py index f0fe7570..31c1f0a3 100644 --- a/data/src/new_etl/data_utils/nbhoods.py +++ b/data/src/new_etl/data_utils/nbhoods.py @@ -1,14 +1,24 @@ import geopandas as gpd from ..classes.featurelayer import FeatureLayer from ..constants.services import NBHOODS_URL - from config.config import USE_CRS -def nbhoods(primary_featurelayer): +def nbhoods(primary_featurelayer: FeatureLayer) -> FeatureLayer: + """ + Adds neighborhood information to the primary feature layer by performing a spatial join + with a neighborhoods dataset. + + Args: + primary_featurelayer (FeatureLayer): The feature layer containing property data. + + Returns: + FeatureLayer: The input feature layer with an added "neighborhood" column, + containing the name of the neighborhood for each property. + """ phl_nbhoods = gpd.read_file(NBHOODS_URL) - # Correct the column name to uppercase if needed + # Correct the column name to lowercase if needed if "MAPNAME" in phl_nbhoods.columns: phl_nbhoods.rename(columns={"MAPNAME": "neighborhood"}, inplace=True) diff --git a/data/src/new_etl/data_utils/negligent_devs.py b/data/src/new_etl/data_utils/negligent_devs.py index 9e0cf440..194eb2cc 100644 --- a/data/src/new_etl/data_utils/negligent_devs.py +++ b/data/src/new_etl/data_utils/negligent_devs.py @@ -1,12 +1,19 @@ -def negligent_devs(primary_featurelayer): - devs = primary_featurelayer.gdf +from ..classes.featurelayer import FeatureLayer - print("Columns in 'devs' DataFrame:", devs.columns) - print("Initial properties data:") - print( - devs[["opa_id", "city_owner_agency", "standardized_address", "vacant"]].head(10) - ) +def negligent_devs(primary_featurelayer: FeatureLayer) -> FeatureLayer: + """ + Identifies negligent developers based on the number of vacant properties owned + and flags them in the primary feature layer. + + Args: + primary_featurelayer (FeatureLayer): The feature layer containing property data. + + Returns: + FeatureLayer: The input feature layer with additional columns for total properties + owned, vacant properties owned, and a "negligent_dev" flag. + """ + devs = primary_featurelayer.gdf # Count total properties and vacant properties by standardized_address property_counts = ( @@ -18,9 +25,6 @@ def negligent_devs(primary_featurelayer): .reset_index() ) - print("Head of resulting DataFrame with property counts:") - print(property_counts.head(10)) - # Merge the property counts back to the main DataFrame primary_featurelayer.gdf = primary_featurelayer.gdf.merge( property_counts, on="standardized_address", how="left" @@ -34,16 +38,4 @@ def negligent_devs(primary_featurelayer): | (primary_featurelayer.gdf["city_owner_agency"] == "") ) - print("Final feature layer data with negligent_dev flag:") - print( - primary_featurelayer.gdf[ - [ - "opa_id", - "n_total_properties_owned", - "n_vacant_properties_owned", - "negligent_dev", - ] - ].head(10) - ) - return primary_featurelayer diff --git a/data/src/new_etl/data_utils/opa_properties.py b/data/src/new_etl/data_utils/opa_properties.py index d74db05d..5b6ce34c 100644 --- a/data/src/new_etl/data_utils/opa_properties.py +++ b/data/src/new_etl/data_utils/opa_properties.py @@ -1,7 +1,7 @@ -from ..classes.featurelayer import FeatureLayer -from ..constants.services import OPA_PROPERTIES_QUERY import pandas as pd import re +from ..classes.featurelayer import FeatureLayer +from ..constants.services import OPA_PROPERTIES_QUERY replacements = { "STREET": "ST", @@ -31,7 +31,16 @@ } -def standardize_street(street): +def standardize_street(street: str) -> str: + """ + Standardizes street names by replacing common full names with abbreviations. + + Args: + street (str): The street name to standardize. + + Returns: + str: The standardized street name. + """ if not isinstance(street, str): return "" for full, abbr in replacements.items(): @@ -39,31 +48,40 @@ def standardize_street(street): return street -def create_standardized_address(row): +def create_standardized_address(row: pd.Series) -> str: + """ + Creates a standardized address from multiple address-related columns in a row. + + Args: + row (pd.Series): A row of a DataFrame containing address-related fields. + + Returns: + str: A standardized, lowercased address string. + """ parts = [ - ( - row["mailing_address_1"].strip() - if pd.notnull(row["mailing_address_1"]) - else "" - ), - ( - row["mailing_address_2"].strip() - if pd.notnull(row["mailing_address_2"]) - else "" - ), + row["mailing_address_1"].strip() + if pd.notnull(row["mailing_address_1"]) + else "", + row["mailing_address_2"].strip() + if pd.notnull(row["mailing_address_2"]) + else "", row["mailing_street"].strip() if pd.notnull(row["mailing_street"]) else "", - ( - row["mailing_city_state"].strip() - if pd.notnull(row["mailing_city_state"]) - else "" - ), + row["mailing_city_state"].strip() + if pd.notnull(row["mailing_city_state"]) + else "", row["mailing_zip"].strip() if pd.notnull(row["mailing_zip"]) else "", ] standardized_address = ", ".join([part for part in parts if part]) return standardized_address.lower() -def opa_properties(): +def opa_properties() -> FeatureLayer: + """ + Loads and processes OPA property data, standardizing addresses and cleaning geometries. + + Returns: + FeatureLayer: A feature layer containing processed OPA property data. + """ opa = FeatureLayer( name="OPA Properties", carto_sql_queries=OPA_PROPERTIES_QUERY, @@ -117,10 +135,5 @@ def opa_properties(): # Drop empty geometries opa.gdf = opa.gdf[~opa.gdf.is_empty] - final_row_count = len(opa.gdf) - print(f"Final row count after cleaning geometries: {final_row_count}") - - # Exclude the geometry column when checking NA counts - print("NA Counts:\n", opa.gdf.drop(columns="geometry").isna().sum()) return opa diff --git a/data/src/new_etl/data_utils/ppr_properties.py b/data/src/new_etl/data_utils/ppr_properties.py index 46e3dc87..84c16a8b 100644 --- a/data/src/new_etl/data_utils/ppr_properties.py +++ b/data/src/new_etl/data_utils/ppr_properties.py @@ -64,7 +64,7 @@ def ppr_properties(primary_featurelayer: FeatureLayer) -> FeatureLayer: # Count rows where the garden is identified and 'vacant' is currently True count_updated = primary_featurelayer.gdf.loc[ - mask & (primary_featurelayer.gdf["vacant"] == True) + mask & primary_featurelayer.gdf["vacant"] ].shape[0] # Update the 'vacant' column to False for identified PPR properties diff --git a/data/src/new_etl/data_utils/priority_level.py b/data/src/new_etl/data_utils/priority_level.py index 5d2ea793..6c0525c6 100644 --- a/data/src/new_etl/data_utils/priority_level.py +++ b/data/src/new_etl/data_utils/priority_level.py @@ -1,28 +1,39 @@ import pandas as pd +from ..classes.featurelayer import FeatureLayer -def priority_level(dataset): +def priority_level(dataset: FeatureLayer) -> FeatureLayer: + """ + Determines priority levels for properties based on gun crime density, + violations, tree canopy gaps, and PHS Landcare status. + + Args: + dataset (FeatureLayer): A feature layer containing property data. + + Returns: + FeatureLayer: The input feature layer with an added "priority_level" column, + indicating the priority for each property as "Low", "Medium", or "High". + """ priority_levels = [] for idx, row in dataset.gdf.iterrows(): priority_level = "" # Decision Points - guncrime_density_percentile = row["gun_crimes_density_percentile"] + guncrime_density_zscore = row["gun_crimes_density_zscore"] in_phs_landcare = pd.notna(row["phs_care_program"]) has_violation_or_high_density = ( float(row["all_violations_past_year"]) > 0 - or row["l_and_i_complaints_density_percentile"] > 50 + or row["l_and_i_complaints_density_zscore"] > 0 # above the mean ) very_low_tree_canopy = row["tree_canopy_gap"] >= 0.3 - # Updated logic based on percentile values - if guncrime_density_percentile <= 50: - # Low Gun Crime Density (Bottom 50%) + # Logic for priority levels + if guncrime_density_zscore <= 0: + # Low Gun Crime Density (below the mean) priority_level = "Low" - elif guncrime_density_percentile > 75: - # High Gun Crime Density (Top 25%) - + elif guncrime_density_zscore > 1: + # High Gun Crime Density (more than 1 std above the mean) if has_violation_or_high_density: priority_level = "High" else: @@ -35,7 +46,7 @@ def priority_level(dataset): priority_level = "High" else: - # Medium Gun Crime Density (Between 50% and 75%) + # Medium Gun Crime Density (between the mean and 1 std above the mean) if has_violation_or_high_density: if in_phs_landcare: priority_level = "Medium" diff --git a/data/src/new_etl/data_utils/pwd_parcels.py b/data/src/new_etl/data_utils/pwd_parcels.py index f8b5ccff..0bde9b59 100644 --- a/data/src/new_etl/data_utils/pwd_parcels.py +++ b/data/src/new_etl/data_utils/pwd_parcels.py @@ -24,8 +24,6 @@ def pwd_parcels(primary_featurelayer: FeatureLayer) -> FeatureLayer: cols=["brt_id"], ) - print("Columns in PWD Parcels:", pwd_parcels.gdf.columns) - # Drop rows with null brt_id, rename to opa_id, and validate geometries pwd_parcels.gdf.dropna(subset=["brt_id"], inplace=True) pwd_parcels.gdf.rename(columns={"brt_id": "opa_id"}, inplace=True) @@ -35,14 +33,6 @@ def pwd_parcels(primary_featurelayer: FeatureLayer) -> FeatureLayer: if not all(pwd_parcels.gdf.geometry.type.isin(["Polygon", "MultiPolygon"])): raise ValueError("Some geometries are not polygons or multipolygons.") - # Log initial feature counts - print("Size of primary feature layer:", len(primary_featurelayer.gdf)) - print("Size of PWD parcels:", len(pwd_parcels.gdf)) - print( - "Number of valid geometries in PWD parcels:", - pwd_parcels.gdf.geometry.notnull().sum(), - ) - # Temporarily drop geometry from the primary feature layer primary_df = primary_featurelayer.gdf.drop(columns=["geometry"]) @@ -62,7 +52,6 @@ def pwd_parcels(primary_featurelayer: FeatureLayer) -> FeatureLayer: # Log observations with no polygon geometry no_geometry_count = merged_gdf["geometry"].isnull().sum() - print("Number of observations with no polygon geometry:", no_geometry_count) # Retain point geometry for rows with no polygon geometry merged_gdf["geometry"] = merged_gdf["geometry"].combine_first( @@ -70,14 +59,5 @@ def pwd_parcels(primary_featurelayer: FeatureLayer) -> FeatureLayer: ) print("Number of observations retaining point geometry:", no_geometry_count) - # Count observations with point geometry grouped by 'vacant' - point_geometry_counts = ( - merged_gdf[merged_gdf["geometry"].geom_type == "Point"].groupby("vacant").size() - ) - - # Log the results - print("Counts of point geometry grouped by 'vacant':") - print(point_geometry_counts) - # Wrap the GeoDataFrame back into a FeatureLayer return FeatureLayer(name=primary_featurelayer.name, gdf=merged_gdf) diff --git a/data/src/new_etl/data_utils/rco_geoms.py b/data/src/new_etl/data_utils/rco_geoms.py index 361a5daf..504a8d90 100644 --- a/data/src/new_etl/data_utils/rco_geoms.py +++ b/data/src/new_etl/data_utils/rco_geoms.py @@ -5,7 +5,18 @@ pd.set_option("future.no_silent_downcasting", True) -def rco_geoms(primary_featurelayer): +def rco_geoms(primary_featurelayer: FeatureLayer) -> FeatureLayer: + """ + Adds Registered Community Organization (RCO) information to the primary feature layer + by performing a spatial join and aggregating RCO data. + + Args: + primary_featurelayer (FeatureLayer): The feature layer containing property data. + + Returns: + FeatureLayer: The input feature layer with added RCO-related columns, + including aggregated RCO information and names. + """ rco_geoms = FeatureLayer(name="RCOs", esri_rest_urls=RCOS_LAYERS_TO_LOAD) rco_aggregate_cols = [ @@ -17,6 +28,7 @@ def rco_geoms(primary_featurelayer): rco_use_cols = ["rco_info", "rco_names", "geometry"] + # Aggregate RCO information into a single column rco_geoms.gdf["rco_info"] = rco_geoms.gdf[rco_aggregate_cols].apply( lambda x: "; ".join(map(str, x)), axis=1 ) @@ -26,17 +38,20 @@ def rco_geoms(primary_featurelayer): rco_geoms.gdf = rco_geoms.gdf[rco_use_cols].copy() rco_geoms.rebuild_gdf() + # Perform spatial join primary_featurelayer.spatial_join(rco_geoms) group_columns = [ col for col in primary_featurelayer.gdf.columns if col not in rco_use_cols ] + # Ensure columns are appropriately filled and cast for col in group_columns: primary_featurelayer.gdf[col] = ( primary_featurelayer.gdf[col].fillna("").infer_objects(copy=False) ) + # Group by non-RCO columns and aggregate RCO data primary_featurelayer.gdf = ( primary_featurelayer.gdf.groupby(group_columns) .agg( diff --git a/data/src/new_etl/data_utils/tree_canopy.py b/data/src/new_etl/data_utils/tree_canopy.py index 108d505d..9e67b4e2 100644 --- a/data/src/new_etl/data_utils/tree_canopy.py +++ b/data/src/new_etl/data_utils/tree_canopy.py @@ -6,27 +6,43 @@ from config.config import USE_CRS -def tree_canopy(primary_featurelayer): +def tree_canopy(primary_featurelayer: FeatureLayer) -> FeatureLayer: + """ + Adds tree canopy gap information to the primary feature layer by downloading, + processing, and spatially joining tree canopy data for Philadelphia County. + + Args: + primary_featurelayer (FeatureLayer): The feature layer containing property data. + + Returns: + FeatureLayer: The input feature layer with an added "tree_canopy_gap" column + indicating the tree canopy gap for each property. + """ tree_url = ( "https://national-tes-data-share.s3.amazonaws.com/national_tes_share/pa.zip.zip" ) + # Download and extract tree canopy data tree_response = requests.get(tree_url) with io.BytesIO(tree_response.content) as f: with zipfile.ZipFile(f, "r") as zip_ref: zip_ref.extractall("tmp/") + # Load and process the tree canopy shapefile pa_trees = gpd.read_file("tmp/pa.shp") pa_trees = pa_trees.to_crs(USE_CRS) phl_trees = pa_trees[pa_trees["county"] == "Philadelphia County"] phl_trees = phl_trees[["tc_gap", "geometry"]] + # Rename column to match intended output phl_trees.rename(columns={"tc_gap": "tree_canopy_gap"}, inplace=True) + # Create a FeatureLayer for tree canopy data tree_canopy = FeatureLayer("Tree Canopy") tree_canopy.gdf = phl_trees + # Perform spatial join primary_featurelayer.spatial_join(tree_canopy) return primary_featurelayer diff --git a/data/src/new_etl/data_utils/unsafe_buildings.py b/data/src/new_etl/data_utils/unsafe_buildings.py index 2aae8dfd..655621a3 100644 --- a/data/src/new_etl/data_utils/unsafe_buildings.py +++ b/data/src/new_etl/data_utils/unsafe_buildings.py @@ -2,7 +2,18 @@ from ..constants.services import UNSAFE_BUILDINGS_QUERY -def unsafe_buildings(primary_featurelayer): +def unsafe_buildings(primary_featurelayer: FeatureLayer) -> FeatureLayer: + """ + Adds unsafe building information to the primary feature layer by joining with a dataset + of unsafe buildings. + + Args: + primary_featurelayer (FeatureLayer): The feature layer containing property data. + + Returns: + FeatureLayer: The input feature layer with an added "unsafe_building" column, + indicating whether each property is categorized as an unsafe building ("Y" or "N"). + """ unsafe_buildings = FeatureLayer( name="Unsafe Buildings", carto_sql_queries=UNSAFE_BUILDINGS_QUERY, @@ -10,17 +21,21 @@ def unsafe_buildings(primary_featurelayer): cols=["opa_account_num"], ) + # Mark unsafe buildings unsafe_buildings.gdf.loc[:, "unsafe_building"] = "Y" + # Rename column for consistency unsafe_buildings.gdf = unsafe_buildings.gdf.rename( columns={"opa_account_num": "opa_number"} ) + # Join unsafe buildings data with primary feature layer primary_featurelayer.opa_join( unsafe_buildings.gdf, "opa_number", ) + # Fill missing values with "N" for non-unsafe buildings primary_featurelayer.gdf.loc[:, "unsafe_building"] = primary_featurelayer.gdf[ "unsafe_building" ].fillna("N") diff --git a/data/src/new_etl/data_utils/vacant_properties.py b/data/src/new_etl/data_utils/vacant_properties.py index 5e52344d..84845ee4 100644 --- a/data/src/new_etl/data_utils/vacant_properties.py +++ b/data/src/new_etl/data_utils/vacant_properties.py @@ -6,6 +6,15 @@ def load_backup_data_from_gcs(file_name: str) -> pd.DataFrame: + """ + Loads backup data from Google Cloud Storage as a DataFrame, ensuring compatibility for matching. + + Args: + file_name (str): The name of the file to load from GCS. + + Returns: + pd.DataFrame: A DataFrame containing the backup data with only the "opa_id" column. + """ bucket = google_cloud_bucket() blob = bucket.blob(file_name) if not blob.exists(): @@ -13,7 +22,6 @@ def load_backup_data_from_gcs(file_name: str) -> pd.DataFrame: file_bytes = blob.download_as_bytes() try: - # Read GeoJSON as a GeoDataFrame gdf = gpd.read_file(BytesIO(file_bytes)) except Exception as e: raise ValueError(f"Error reading GeoJSON file: {e}") @@ -22,15 +30,22 @@ def load_backup_data_from_gcs(file_name: str) -> pd.DataFrame: # Ensure only opa_id is retained and convert to DataFrame (drop geometry) gdf = gdf[["OPA_ID"]].rename(columns={"OPA_ID": "opa_id"}) - - # Drop the geometry column to avoid CRS issues (we don't need the geometry for matching) gdf = gdf.drop(columns=["geometry"], errors="ignore") return gdf -def check_null_percentage(df: pd.DataFrame, threshold: float = 0.05): - """Checks if any column in the dataframe has more than the given threshold of null values.""" +def check_null_percentage(df: pd.DataFrame, threshold: float = 0.05) -> None: + """ + Checks if any column in the DataFrame has more than the given threshold of null values. + + Args: + df (pd.DataFrame): The DataFrame to check for null percentages. + threshold (float): The threshold for acceptable null percentages (default is 5%). + + Raises: + ValueError: If any column has more null values than the threshold. + """ null_percentages = df.isnull().mean() for col, pct in null_percentages.items(): if pct > threshold: @@ -39,68 +54,68 @@ def check_null_percentage(df: pd.DataFrame, threshold: float = 0.05): ) -def vacant_properties(primary_featurelayer) -> FeatureLayer: +def vacant_properties(primary_featurelayer: FeatureLayer) -> FeatureLayer: + """ + Adds a "vacant" column to the primary feature layer based on vacant property data from + ESRI layers and backup data from Google Cloud Storage if necessary. + + Args: + primary_featurelayer (FeatureLayer): The feature layer containing property data. + + Returns: + FeatureLayer: The input feature layer with an added "vacant" column. + """ vacant_properties = FeatureLayer( name="Vacant Properties", esri_rest_urls=VACANT_PROPS_LAYERS_TO_LOAD, - cols=[ - "OPA_ID", - "parcel_type", - ], # Only need opa_id and parcel_type from the vacancy layers + cols=["OPA_ID", "parcel_type"], # Only need opa_id and parcel_type ) - print("Columns in vacant properties dataset:", vacant_properties.gdf.columns) - - # Rename columns for consistency in the original data + # Rename columns for consistency vacant_properties.gdf = vacant_properties.gdf.rename(columns={"OPA_ID": "opa_id"}) - # Check for "Land" properties in the default dataset + # Filter for "Land" properties in the dataset vacant_land_gdf = vacant_properties.gdf[ vacant_properties.gdf["parcel_type"] == "Land" ] print(f"Vacant land data size in the default dataset: {len(vacant_land_gdf)} rows.") - # If vacant land properties are below the threshold (20,000 rows), load backup data + # Check if the vacant land data is below the threshold if len(vacant_land_gdf) < 20000: print( "Vacant land data is below the threshold. Removing vacant land rows and loading backup data from GCS." ) - - # Drop vacant land rows from the current dataset vacant_properties.gdf = vacant_properties.gdf[ vacant_properties.gdf["parcel_type"] != "Land" ] - # Load backup data and ensure it's a DataFrame (dropping geometry) + # Load backup data backup_gdf = load_backup_data_from_gcs("vacant_indicators_land_06_2024.geojson") - # Add a parcel_type column with value "Land" for all rows in the backup data + # Add parcel_type column to backup data backup_gdf["parcel_type"] = "Land" - # Concatenate the backup data with the existing data + # Append backup data to the existing dataset print(f"Appending backup data ({len(backup_gdf)} rows) to the existing data.") vacant_properties.gdf = pd.concat( [vacant_properties.gdf, backup_gdf], ignore_index=True ) - # Drop the geometry column to convert to a regular DataFrame + # Convert to a regular DataFrame by dropping geometry df = vacant_properties.gdf.drop(columns=["geometry"], errors="ignore") - # Drop rows where opa_id is missing + # Drop rows with missing opa_id df.dropna(subset=["opa_id"], inplace=True) - # Final null value check before returning - check_null_percentage(df) + # Final check for null percentages + # check_null_percentage(df) - # Create vacant column in the primary feature layer as True/False + # Add "vacant" column to primary feature layer primary_featurelayer.gdf["vacant"] = primary_featurelayer.gdf["opa_id"].isin( df["opa_id"] ) - print("Vacant column added based on opa_id match.") - - # Drop the parcel_type column once the decision has been made + # Drop parcel_type column after processing df.drop(columns=["parcel_type"], inplace=True) - # Return primary_featurelayer after adding vacant column return primary_featurelayer diff --git a/data/src/new_etl/database.py b/data/src/new_etl/database.py new file mode 100644 index 00000000..330d163c --- /dev/null +++ b/data/src/new_etl/database.py @@ -0,0 +1,134 @@ +from sqlalchemy import text + + +def is_hypertable(conn, table_name): + """ + Check if a given table is already a hypertable. + + Args: + conn: SQLAlchemy connection to the database. + table_name (str): The name of the table to check. + + Returns: + bool: True if the table is a hypertable, False otherwise. + """ + query = f""" + SELECT EXISTS ( + SELECT 1 + FROM timescaledb_information.hypertables + WHERE hypertable_name = '{table_name}' + ); + """ + result = conn.execute(text(query)).scalar() + return result + + +def execute_optional_sql(conn, query, description): + """ + Execute a SQL query in a separate transaction and handle errors gracefully. + + Args: + conn: SQLAlchemy connection to the database. + query (str): The SQL query to execute. + description (str): A description of the operation for logging purposes. + """ + try: + conn.execute(text(query)) + except Exception as e: + print(f"Warning: {description} failed. Error: {e}") + + +def sync_table_schema(gdf, table_name, conn): + """ + Synchronize the schema of a GeoDataFrame with the database table. + + Args: + gdf (GeoDataFrame): The GeoDataFrame with updated schema. + table_name (str): The name of the table in the database. + conn: SQLAlchemy connection to the database. + """ + result = conn.execute( + text(f""" + SELECT column_name + FROM information_schema.columns + WHERE table_name = '{table_name}'; + """) + ) + existing_columns = {row[0] for row in result} + for column in set(gdf.columns) - existing_columns: + dtype = gdf[column].dtype + sql_type = { + "int64": "INTEGER", + "float64": "FLOAT", + "object": "TEXT", + "bool": "BOOLEAN", + "datetime64[ns]": "TIMESTAMP", + }.get(str(dtype), "TEXT") + conn.execute(text(f"ALTER TABLE {table_name} ADD COLUMN {column} {sql_type};")) + + +def to_postgis_with_schema(gdf, table_name, conn, if_exists="append", chunksize=1000): + """ + Save a GeoDataFrame to PostGIS, ensure the `create_date` column exists, and configure the table as a hypertable. + + Args: + gdf (GeoDataFrame): The GeoDataFrame to save. + table_name (str): The name of the table in PostGIS. + conn: SQLAlchemy connection to the database. + if_exists (str): Behavior when the table already exists ('replace', 'append', 'fail'). + chunksize (int): Number of rows to write at a time. + """ + try: + # Begin a transaction + with conn.begin() as transaction: + # Synchronize schema with database table + if if_exists == "append": + sync_table_schema(gdf, table_name, conn) + + # Save GeoDataFrame to PostGIS + gdf.to_postgis(table_name, conn, if_exists=if_exists, chunksize=chunksize) + + # Add the `create_date` column via SQL + conn.execute( + text(f""" + DO $$ + BEGIN + IF NOT EXISTS ( + SELECT 1 + FROM information_schema.columns + WHERE table_name = '{table_name}' AND column_name = 'create_date' + ) THEN + ALTER TABLE {table_name} ADD COLUMN create_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP; + END IF; + END $$; + """) + ) + + # Check if the table is already a hypertable + if not is_hypertable(conn, table_name): + conn.execute( + text(f""" + SELECT create_hypertable('{table_name}', 'create_date', migrate_data => true); + """) + ) + + # Optional operations in separate transactions + execute_optional_sql( + conn, + f"SELECT set_chunk_time_interval('{table_name}', INTERVAL '1 month');", + f"Setting chunk interval for {table_name}", + ) + execute_optional_sql( + conn, + f"ALTER TABLE {table_name} SET (timescaledb.compress);", + f"Enabling compression on {table_name}", + ) + execute_optional_sql( + conn, + f"SELECT add_compression_policy('{table_name}', INTERVAL '3 months', if_not_exists => true);", + f"Adding compression policy for {table_name}", + ) + except Exception as e: + # Rollback the transaction on any error + conn.rollback() + raise RuntimeError(f"Error during to_postgis_with_schema: {e}") diff --git a/data/src/new_etl/loaders.py b/data/src/new_etl/loaders.py new file mode 100644 index 00000000..59d8bcb3 --- /dev/null +++ b/data/src/new_etl/loaders.py @@ -0,0 +1,107 @@ +import geopandas as gpd +import pandas as pd +from esridump.dumper import EsriDumper +import requests +from concurrent.futures import ThreadPoolExecutor, as_completed +from tqdm import tqdm +from shapely import wkb + + +# Esri data loader +def load_esri_data(esri_rest_urls, input_crs, target_crs): + """ + Load data from Esri REST URLs and add a parcel_type column based on the URL. + + Args: + esri_rest_urls (list[str]): List of Esri REST URLs to fetch data from. + input_crs (str): CRS of the source data. + target_crs (str): Target CRS to which data should be reprojected. + + Returns: + GeoDataFrame: Combined GeoDataFrame with data from all URLs. + """ + gdfs = [] + for url in esri_rest_urls: + # Determine parcel_type based on URL patterns + parcel_type = ( + "Land" + if "Vacant_Indicators_Land" in url + else "Building" + if "Vacant_Indicators_Bldg" in url + else None + ) + + dumper = EsriDumper(url) + features = [feature for feature in dumper] + + if not features: + continue # Skip if no features were found + + geojson_features = {"type": "FeatureCollection", "features": features} + gdf = gpd.GeoDataFrame.from_features(geojson_features, crs=input_crs).to_crs( + target_crs + ) + + if parcel_type: + gdf["parcel_type"] = parcel_type # Add the parcel_type column + + gdfs.append(gdf) + + return pd.concat(gdfs, ignore_index=True) + + +# Carto data loader +def load_carto_data( + queries, max_workers, chunk_size, use_wkb_geom_field, input_crs, target_crs +): + gdfs = [] + with ThreadPoolExecutor(max_workers=max_workers) as executor: + futures = [] + for query in queries: + total_rows = get_carto_total_rows(query) + for offset in range(0, total_rows, chunk_size): + futures.append( + executor.submit( + fetch_carto_chunk, + query, + offset, + chunk_size, + use_wkb_geom_field, + input_crs, + target_crs, + ) + ) + for future in tqdm( + as_completed(futures), total=len(futures), desc="Processing Carto chunks" + ): + gdfs.append(future.result()) + return pd.concat(gdfs, ignore_index=True) + + +def fetch_carto_chunk( + query, offset, chunk_size, use_wkb_geom_field, input_crs, target_crs +): + chunk_query = f"{query} LIMIT {chunk_size} OFFSET {offset}" + response = requests.get( + "https://phl.carto.com/api/v2/sql", params={"q": chunk_query} + ) + response.raise_for_status() + data = response.json().get("rows", []) + if not data: + return gpd.GeoDataFrame() + df = pd.DataFrame(data) + geometry = ( + wkb.loads(df[use_wkb_geom_field], hex=True) + if use_wkb_geom_field + else gpd.points_from_xy(df.x, df.y) + ) + return gpd.GeoDataFrame(df, geometry=geometry, crs=input_crs).to_crs(target_crs) + + +def get_carto_total_rows(query): + count_query = f"SELECT COUNT(*) as count FROM ({query}) as subquery" + response = requests.get( + "https://phl.carto.com/api/v2/sql", params={"q": count_query} + ) + response.raise_for_status() + return response.json()["rows"][0]["count"] diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 8ca5217c..6c4abc58 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -1,85 +1,75 @@ -# Clean & Green Philly Summer 2024 Roadmap +## Clean & Green Philly 2025 Roadmap -## 🗓 Roadmap Overview +### Overview -Welcome to the project roadmap for Clean & Green Philly! After launching v1.0.0 in spring of 2024, we will spend the summer shifting our focus from frontend tasks to building out a more robust data pipeline and a project CI/CD workflow. We will still continue to work on front end stuff, but will have a more balanced distribution across all parts of the project, while also gathering input through user research. +Welcome to the project roadmap for Clean & Green Philly! This is a living document that serves to outline the scope of what we intend to work on in the coming year. We intend to make quarterly updates to this document as the year progresses. -## Front End +We launched v1.0.0 of [cleanandgreenphilly.org](https://www.cleanandgreenphilly.org/) in the spring of 2024. Since then, we have made substantial updates to our data pipeline, incorporating parcel-level data for every property in Philadelphia, while undertaking several months of user research and workshops to understand where we want to go next. -### 🌐 Relevant Organizations Page +In 2025, we plan to begin taking advantage of our new data pipeline to pursue new analytical possibilities, and to implement the necessary updates to the website that we’ve identified through the user research process. At a high level, this will entail: 1) rebranding to more clearly articulate our aims as an organization and avoid confusion with the Mayor’s Office of Clean & Green Initiatives; 2) rewriting site content to align with the rebrand, as well as adding improved contact and donations pages; 3) focusing our front-end development on improved web and mobile accessibility for the cleanandgreenphilly.org site, 4) streamlining our data pipeline and adding improved data quality controls, 5) establishing more robust CI/CD, 6) exploring the possibility of using machine learning to identify vacant properties. -- **Goal:** Create a page listing organizations actively working on related initiatives. -- **Key Actions:** - - Research and compile a list of relevant organizations. - - Design and develop the webpage to display this information in an accessible format. +#### Organizational Rebrand 🎨 -### 📝 Grant Writing Opportunities +**Goal:** The Executive Director will recruit pro bono support to help Clean & Green Philly rebrand based on our strategic plan, in order to clearly articulate our aims as an organization and how we are distinct from other initiatives (especially the Mayor’s Office of Clean and Green Initiatives). This Code for Philly team will support the integration of this rebrand into the website as needed, e.g., by updating site content, graphics, colors, etc. +**Key Actions:** -- **Goal:** Provide a comprehensive list of grant writing opportunities for local organizations. -- **Key Actions:** - - Identify and list potential grant sources. - - Connect users to detailed grant writing resources and guides. +- Recruit pro bono technical support for branding, marketing, graphic design, content writing. +- Develop a branding kit with appropriate material for use in the newsletter, site, and elsewhere. +- Communicate new branding guidelines for use on the website; update the site accordingly. -## Dataset Enhancements +#### Content Rewrite ✍️ -### 🔢 Development Risk Indicator +**Goal:** Following the organizational rebrand, the Executive Director will secure support in writing new site content to ensure alignment with our rebrand and strategic plan. Additionally, new material is needed for technical documentation (e.g., a data dictionary and clearer explanations of data points like our “priority level” designation) and the “Transform” page. +**Key Actions:** -✅ **Completed June 2024** +- Identify and draft needed technical documentation (note that this will require some concerted thought based on comparable examples and best practices); mock and implement actual UI. +- Conduct user research and a comparative analysis to identify what content is missing on the website. +- Survey domain experts to identify content missing from Get Access and Transform pages. +- Recruit one or more content writers to update content for these pages. +- Coordinate with the UX team on mocking and implementation. +- Consider mocking and implementing/updating the following pages: + - Contact + - Donations + - Grant opportunities for local organizations + - A page listing organizations actively working on related initiatives -- **Goal:** Add a development risk metric based on building permits per census block. -- **Key Actions:** - - Calculate a z-scored count of building permits issued in the past year per census block. - - Scale the data from high to low, noting the approximation to avoid a false appearance of precision. +#### Improving Web and Mobile Accessibility 📱 -### 📊 Tangled Titles Model +**Goal:** In order to support less tech-saavy users, we will prioritize enhancements to web and mobile accessibility for cleanandgreenphilly.org. +**Key Actions:** -- **Goal:** Develop a model to identify properties with tangled titles. -- **Key Actions:** - - Reach out to Pew about getting their dataste on tangled titles. - - Build and implement a predictive model to identify potential tangled titles in our dataset dataset. +- Build on the user journeys mapped out in the 2024 community workshop slides, adding these to the website to help guide new users. +- Create a tutorial for the “Find Properties” page using the [React Joyride guided tours package](https://github.com/gilbarbara/react-joyride). +- Implement fixes to pain points as identified by the UX team in our user workshops. +- Consider adding an “FAQ” page to the site, if needed. -### 🏗 Negligent Developers Data +#### Enhancing Data Pipeline ⚙️ -✅ **Completed June 2024** +**Goal:** Improve the maintainability and functionality of the data pipeline by improving code quality and adding better data quality control checks. +**Key Actions:** -- **Goal:** Include data on negligent developers in our dataset. -- **Key Actions:** - - Define what constitutes negligent development. - - Implement a workflow to identify likely cases of negligent developers in our dataset. +- Refactor the data pipeline to improve the maintainability of the codebase (considering using tools like `vulture` and `radon` for this purpose). + - Relatedly: + - https://github.com/CodeForPhilly/clean-and-green-philly/issues/717 + - https://github.com/CodeForPhilly/clean-and-green-philly/issues/706 +- Add formal, consistent data quality controls using libraries like `dbt`, rather than our current ad hoc checks that exist in the pipeline. -## Back-End +#### Improving CI/CD 🚀 -### 📦 Containerization +**Goal:** Improve our CI/CD process to reduce the workload on project leads and ensure the maintainability of the codebase. +**Key Actions:** -- **Goal:** Containerize the entire backend setup to improve deployment efficiency and allow for migration to Google Cloud Services (GCS). -- **Key Actions:** - - Build out a Dockerfile to run the entire ETL pipeline in the cloud. - - Deploy the container to GCS. +- Add tests for key parts of the data pipeline, using a sample dataset stored on GitHub, or mocking data when necessary. +- Ensure that tests are integrated into the GitHub Actions workflows to prevent breaking code from being added to the codebase. + - Along the way, address: https://github.com/CodeForPhilly/clean-and-green-philly/issues/830 +- Eliminate redundant GitHub Actions workflows while also making sure that the remaining ones all function as intended. +- Review documentation to ensure that it is up to date and accurate. -### ☁️ Porting to Google Cloud +#### Exploring ML 🧠 -- **Goal:** Migrate our pipeline to Google Cloud. -- **Key Actions:** - - Automate data updates with scheduled cloud functions to refresh our dataset monthly. - - Document migration steps and best practices for future reference. +**Goal:** Explore the possibility of using ML with our dataset to identify vacant properties. +**Key Actions:** -### 🧪 Implementing Data Quality Tests - -- **Goal:** Implement data quality assurance measures using tools like `dbt`. -- **Key Actions:** - - Define data quality metrics and tests. - - Integrate `dbt` into our data processing pipelines. - - Schedule and monitor data quality checks to ensure continuous data integrity. - -### 🔄 Setting Up CI/CD - -- **Goal:** Establish a Continuous Integration/Continuous Deployment (CI/CD) workflow to streamline development and deployment. -- **Key Actions:** - - Develop CI/CD pipelines using GitHub Actions including automated testing, building, and deployment. - -### 📧 Automated Stakeholder Communication - -- **Goal:** Automate the process of sending targeted email updates to stakeholders such as Habitat for Humanity about new, eligible properties. -- **Key Actions:** - - Set up email triggers in our system to send out monthly updates. - - Customize email content based on stakeholder interest and relevance. +- Perform EDA to identify key features in the dataset associated with vacancy indicators. +- Explore basic modeling approaches (e.g., logit, RF) as proofs of concept to demonstrate the viability of using ML to identify vacant properties. +- Work with key stakeholders to identify scalable ways to supplant the City’s outdated vacancy by building our own model.