diff --git a/CHANGELOG.md b/CHANGELOG.md index 7518d5a..817d8d6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,6 @@ +### v1.14.4 +- Internal code improvements. + ### v1.14.3 - Update `property_v2.search` to simplify pagination logic.. diff --git a/Pipfile b/Pipfile index af0ec34..7e75e85 100644 --- a/Pipfile +++ b/Pipfile @@ -7,6 +7,7 @@ name = "pypi" requests = ">=2.25" pandas = ">=1.2" numpy = "*" +pydantic = ">=2.0.0" [dev-packages] parcllabs = {path = ".", editable = true} diff --git a/Pipfile.lock b/Pipfile.lock index cf80173..c4f3dcf 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "d822619d3bb81014b530224d183d59cd56550c74daf7587608f69499a1309e6a" + "sha256": "484dd598e60a2a5c0fc18ee0e311b6aede344d34eec563b6279ce7073393d21d" }, "pipfile-spec": 6, "requires": { @@ -16,13 +16,21 @@ ] }, "default": { + "annotated-types": { + "hashes": [ + "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", + "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89" + ], + "markers": "python_version >= '3.8'", + "version": "==0.7.0" + }, "certifi": { "hashes": [ - "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6", - "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3" + "sha256:2e0c7ce7cb5d8f8634ca55d2ba7e6ec2689a2fd6537d8dec1296a477a4910057", + "sha256:d747aa5a8b9bbbb1bb8c22bb13e22bd1f18e9796defa16bab421f7f7a317323b" ], - "markers": "python_version >= '3.6'", - "version": "==2025.4.26" + "markers": "python_version >= '3.7'", + "version": "==2025.6.15" }, "charset-normalizer": { "hashes": [ @@ -132,112 +140,224 @@ }, "numpy": { "hashes": [ - "sha256:0255732338c4fdd00996c0421884ea8a3651eea555c3a56b84892b66f696eb70", - "sha256:02f226baeefa68f7d579e213d0f3493496397d8f1cff5e2b222af274c86a552a", - "sha256:059b51b658f4414fff78c6d7b1b4e18283ab5fa56d270ff212d5ba0c561846f4", - "sha256:0bcb1d057b7571334139129b7f941588f69ce7c4ed15a9d6162b2ea54ded700c", - "sha256:0cd48122a6b7eab8f06404805b1bd5856200e3ed6f8a1b9a194f9d9054631beb", - "sha256:19f4718c9012e3baea91a7dba661dcab2451cda2550678dc30d53acb91a7290f", - "sha256:1a161c2c79ab30fe4501d5a2bbfe8b162490757cf90b7f05be8b80bc02f7bb8e", - "sha256:1f4a922da1729f4c40932b2af4fe84909c7a6e167e6e99f71838ce3a29f3fe26", - "sha256:261a1ef047751bb02f29dfe337230b5882b54521ca121fc7f62668133cb119c9", - "sha256:262d23f383170f99cd9191a7c85b9a50970fe9069b2f8ab5d786eca8a675d60b", - "sha256:2ba321813a00e508d5421104464510cc962a6f791aa2fca1c97b1e65027da80d", - "sha256:2c1a1c6ccce4022383583a6ded7bbcda22fc635eb4eb1e0a053336425ed36dfa", - "sha256:352d330048c055ea6db701130abc48a21bec690a8d38f8284e00fab256dc1376", - "sha256:369e0d4647c17c9363244f3468f2227d557a74b6781cb62ce57cf3ef5cc7c610", - "sha256:36ab5b23915887543441efd0417e6a3baa08634308894316f446027611b53bf1", - "sha256:37e32e985f03c06206582a7323ef926b4e78bdaa6915095ef08070471865b906", - "sha256:3a801fef99668f309b88640e28d261991bfad9617c27beda4a3aec4f217ea073", - "sha256:3d14b17b9be5f9c9301f43d2e2a4886a33b53f4e6fdf9ca2f4cc60aeeee76372", - "sha256:422cc684f17bc963da5f59a31530b3936f57c95a29743056ef7a7903a5dbdf88", - "sha256:4520caa3807c1ceb005d125a75e715567806fed67e315cea619d5ec6e75a4191", - "sha256:47834cde750d3c9f4e52c6ca28a7361859fcaf52695c7dc3cc1a720b8922683e", - "sha256:47f9ed103af0bc63182609044b0490747e03bd20a67e391192dde119bf43d52f", - "sha256:498815b96f67dc347e03b719ef49c772589fb74b8ee9ea2c37feae915ad6ebda", - "sha256:54088a5a147ab71a8e7fdfd8c3601972751ded0739c6b696ad9cb0343e21ab73", - "sha256:55f09e00d4dccd76b179c0f18a44f041e5332fd0e022886ba1c0bbf3ea4a18d0", - "sha256:5a0ac90e46fdb5649ab6369d1ab6104bfe5854ab19b645bf5cda0127a13034ae", - "sha256:6411f744f7f20081b1b4e7112e0f4c9c5b08f94b9f086e6f0adf3645f85d3a4d", - "sha256:6413d48a9be53e183eb06495d8e3b006ef8f87c324af68241bbe7a39e8ff54c3", - "sha256:7451f92eddf8503c9b8aa4fe6aa7e87fd51a29c2cfc5f7dbd72efde6c65acf57", - "sha256:8b4c0773b6ada798f51f0f8e30c054d32304ccc6e9c5d93d46cb26f3d385ab19", - "sha256:8dfa94b6a4374e7851bbb6f35e6ded2120b752b063e6acdd3157e4d2bb922eba", - "sha256:97c8425d4e26437e65e1d189d22dff4a079b747ff9c2788057bfb8114ce1e133", - "sha256:9d75f338f5f79ee23548b03d801d28a505198297534f62416391857ea0479571", - "sha256:9de6832228f617c9ef45d948ec1cd8949c482238d68b2477e6f642c33a7b0a54", - "sha256:a4cbdef3ddf777423060c6f81b5694bad2dc9675f110c4b2a60dc0181543fac7", - "sha256:a9c0d994680cd991b1cb772e8b297340085466a6fe964bc9d4e80f5e2f43c291", - "sha256:aa70fdbdc3b169d69e8c59e65c07a1c9351ceb438e627f0fdcd471015cd956be", - "sha256:abe38cd8381245a7f49967a6010e77dbf3680bd3627c0fe4362dd693b404c7f8", - "sha256:b13f04968b46ad705f7c8a80122a42ae8f620536ea38cf4bdd374302926424dd", - "sha256:b4ea7e1cff6784e58fe281ce7e7f05036b3e1c89c6f922a6bfbc0a7e8768adbe", - "sha256:b6f91524d31b34f4a5fee24f5bc16dcd1491b668798b6d85585d836c1e633a6a", - "sha256:c26843fd58f65da9491165072da2cccc372530681de481ef670dcc8e27cfb066", - "sha256:c42365005c7a6c42436a54d28c43fe0e01ca11eb2ac3cefe796c25a5f98e5e9b", - "sha256:c8b82a55ef86a2d8e81b63da85e55f5537d2157165be1cb2ce7cfa57b6aef38b", - "sha256:ced69262a8278547e63409b2653b372bf4baff0870c57efa76c5703fd6543282", - "sha256:d2e3bdadaba0e040d1e7ab39db73e0afe2c74ae277f5614dad53eadbecbbb169", - "sha256:d403c84991b5ad291d3809bace5e85f4bbf44a04bdc9a88ed2bb1807b3360bb8", - "sha256:d7543263084a85fbc09c704b515395398d31d6395518446237eac219eab9e55e", - "sha256:d8882a829fd779f0f43998e931c466802a77ca1ee0fe25a3abe50278616b1471", - "sha256:e4f0b035d9d0ed519c813ee23e0a733db81ec37d2e9503afbb6e54ccfdee0fa7", - "sha256:e8b025c351b9f0e8b5436cf28a07fa4ac0204d67b38f01433ac7f9b870fa38c6", - "sha256:eb7fd5b184e5d277afa9ec0ad5e4eb562ecff541e7f60e69ee69c8d59e9aeaba", - "sha256:ec31367fd6a255dc8de4772bd1658c3e926d8e860a0b6e922b615e532d320ddc", - "sha256:ee461a4eaab4f165b68780a6a1af95fb23a29932be7569b9fab666c407969051", - "sha256:f5045039100ed58fa817a6227a356240ea1b9a1bc141018864c306c1a16d4175" + "sha256:0025048b3c1557a20bc80d06fdeb8cc7fc193721484cca82b2cfa072fec71a93", + "sha256:010ce9b4f00d5c036053ca684c77441f2f2c934fd23bee058b4d6f196efd8280", + "sha256:0bb3a4a61e1d327e035275d2a993c96fa786e4913aa089843e6a2d9dd205c66a", + "sha256:0c4d9e0a8368db90f93bd192bfa771ace63137c3488d198ee21dfb8e7771916e", + "sha256:15aa4c392ac396e2ad3d0a2680c0f0dee420f9fed14eef09bdb9450ee6dcb7b7", + "sha256:18703df6c4a4fee55fd3d6e5a253d01c5d33a295409b03fda0c86b3ca2ff41a1", + "sha256:1ec9ae20a4226da374362cca3c62cd753faf2f951440b0e3b98e93c235441d2b", + "sha256:23ab05b2d241f76cb883ce8b9a93a680752fbfcbd51c50eff0b88b979e471d8c", + "sha256:25a1992b0a3fdcdaec9f552ef10d8103186f5397ab45e2d25f8ac51b1a6b97e8", + "sha256:2959d8f268f3d8ee402b04a9ec4bb7604555aeacf78b360dc4ec27f1d508177d", + "sha256:2a809637460e88a113e186e87f228d74ae2852a2e0c44de275263376f17b5bdc", + "sha256:2fb86b7e58f9ac50e1e9dd1290154107e47d1eef23a0ae9145ded06ea606f992", + "sha256:36890eb9e9d2081137bd78d29050ba63b8dab95dff7912eadf1185e80074b2a0", + "sha256:39bff12c076812595c3a306f22bfe49919c5513aa1e0e70fac756a0be7c2a2b8", + "sha256:467db865b392168ceb1ef1ffa6f5a86e62468c43e0cfb4ab6da667ede10e58db", + "sha256:4e602e1b8682c2b833af89ba641ad4176053aaa50f5cacda1a27004352dde943", + "sha256:5902660491bd7a48b2ec16c23ccb9124b8abfd9583c5fdfa123fe6b421e03de1", + "sha256:5ccb7336eaf0e77c1635b232c141846493a588ec9ea777a7c24d7166bb8533ae", + "sha256:5f1b8f26d1086835f442286c1d9b64bb3974b0b1e41bb105358fd07d20872952", + "sha256:6269b9edfe32912584ec496d91b00b6d34282ca1d07eb10e82dfc780907d6c2e", + "sha256:6ea9e48336a402551f52cd8f593343699003d2353daa4b72ce8d34f66b722070", + "sha256:762e0c0c6b56bdedfef9a8e1d4538556438288c4276901ea008ae44091954e29", + "sha256:7be91b2239af2658653c5bb6f1b8bccafaf08226a258caf78ce44710a0160d30", + "sha256:7dea630156d39b02a63c18f508f85010230409db5b2927ba59c8ba4ab3e8272e", + "sha256:867ef172a0976aaa1f1d1b63cf2090de8b636a7674607d514505fb7276ab08fc", + "sha256:8d5ee6eec45f08ce507a6570e06f2f879b374a552087a4179ea7838edbcbfa42", + "sha256:8e333040d069eba1652fb08962ec5b76af7f2c7bce1df7e1418c8055cf776f25", + "sha256:a5ee121b60aa509679b682819c602579e1df14a5b07fe95671c8849aad8f2115", + "sha256:a780033466159c2270531e2b8ac063704592a0bc62ec4a1b991c7c40705eb0e8", + "sha256:a894f3816eb17b29e4783e5873f92faf55b710c2519e5c351767c51f79d8526d", + "sha256:a8b740f5579ae4585831b3cf0e3b0425c667274f82a484866d2adf9570539369", + "sha256:ad506d4b09e684394c42c966ec1527f6ebc25da7f4da4b1b056606ffe446b8a3", + "sha256:afed2ce4a84f6b0fc6c1ce734ff368cbf5a5e24e8954a338f3bdffa0718adffb", + "sha256:b0b5397374f32ec0649dd98c652a1798192042e715df918c20672c62fb52d4b8", + "sha256:bada6058dd886061f10ea15f230ccf7dfff40572e99fef440a4a857c8728c9c0", + "sha256:c4913079974eeb5c16ccfd2b1f09354b8fed7e0d6f2cab933104a09a6419b1ee", + "sha256:c5bdf2015ccfcee8253fb8be695516ac4457c743473a43290fd36eba6a1777eb", + "sha256:c6e0bf9d1a2f50d2b65a7cf56db37c095af17b59f6c132396f7c6d5dd76484df", + "sha256:ce2ce9e5de4703a673e705183f64fd5da5bf36e7beddcb63a25ee2286e71ca48", + "sha256:cfecc7822543abdea6de08758091da655ea2210b8ffa1faf116b940693d3df76", + "sha256:d4580adadc53311b163444f877e0789f1c8861e2698f6b2a4ca852fda154f3ff", + "sha256:d70f20df7f08b90a2062c1f07737dd340adccf2068d0f1b9b3d56e2038979fee", + "sha256:e344eb79dab01f1e838ebb67aab09965fb271d6da6b00adda26328ac27d4a66e", + "sha256:e610832418a2bc09d974cc9fecebfa51e9532d6190223bc5ef6a7402ebf3b5cb", + "sha256:e772dda20a6002ef7061713dc1e2585bc1b534e7909b2030b5a46dae8ff077ab", + "sha256:e7cbf5a5eafd8d230a3ce356d892512185230e4781a361229bd902ff403bc660", + "sha256:eabd7e8740d494ce2b4ea0ff05afa1b7b291e978c0ae075487c51e8bd93c0c68", + "sha256:ebb8603d45bc86bbd5edb0d63e52c5fd9e7945d3a503b77e486bd88dde67a19b", + "sha256:ec0bdafa906f95adc9a0c6f26a4871fa753f25caaa0e032578a30457bff0af6a", + "sha256:eccb9a159db9aed60800187bc47a6d3451553f0e1b08b068d8b277ddfbb9b244", + "sha256:ee8340cb48c9b7a5899d1149eece41ca535513a9698098edbade2a8e7a84da77" ], "index": "pypi", - "version": "==2.2.5" + "markers": "python_version >= '3.11'", + "version": "==2.3.1" }, "pandas": { "hashes": [ - "sha256:062309c1b9ea12a50e8ce661145c6aab431b1e99530d3cd60640e255778bd43a", - "sha256:15c0e1e02e93116177d29ff83e8b1619c93ddc9c49083f237d4312337a61165d", - "sha256:1948ddde24197a0f7add2bdc4ca83bf2b1ef84a1bc8ccffd95eda17fd836ecb5", - "sha256:1db71525a1538b30142094edb9adc10be3f3e176748cd7acc2240c2f2e5aa3a4", - "sha256:22a9d949bfc9a502d320aa04e5d02feab689d61da4e7764b62c30b991c42c5f0", - "sha256:29401dbfa9ad77319367d36940cd8a0b3a11aba16063e39632d98b0e931ddf32", - "sha256:31d0ced62d4ea3e231a9f228366919a5ea0b07440d9d4dac345376fd8e1477ea", - "sha256:3508d914817e153ad359d7e069d752cdd736a247c322d932eb89e6bc84217f28", - "sha256:37e0aced3e8f539eccf2e099f65cdb9c8aa85109b0be6e93e2baff94264bdc6f", - "sha256:381175499d3802cde0eabbaf6324cce0c4f5d52ca6f8c377c29ad442f50f6348", - "sha256:38cf8125c40dae9d5acc10fa66af8ea6fdf760b2714ee482ca691fc66e6fcb18", - "sha256:3b71f27954685ee685317063bf13c7709a7ba74fc996b84fc6821c59b0f06468", - "sha256:3fc6873a41186404dad67245896a6e440baacc92f5b716ccd1bc9ed2995ab2c5", - "sha256:4850ba03528b6dd51d6c5d273c46f183f39a9baf3f0143e566b89450965b105e", - "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667", - "sha256:56534ce0746a58afaf7942ba4863e0ef81c9c50d3f0ae93e9497d6a41a057645", - "sha256:59ef3764d0fe818125a5097d2ae867ca3fa64df032331b7e0917cf5d7bf66b13", - "sha256:5dbca4c1acd72e8eeef4753eeca07de9b1db4f398669d5994086f788a5d7cc30", - "sha256:5de54125a92bb4d1c051c0659e6fcb75256bf799a732a87184e5ea503965bce3", - "sha256:61c5ad4043f791b61dd4752191d9f07f0ae412515d59ba8f005832a532f8736d", - "sha256:6374c452ff3ec675a8f46fd9ab25c4ad0ba590b71cf0656f8b6daa5202bca3fb", - "sha256:63cc132e40a2e084cf01adf0775b15ac515ba905d7dcca47e9a251819c575ef3", - "sha256:66108071e1b935240e74525006034333f98bcdb87ea116de573a6a0dccb6c039", - "sha256:6dfcb5ee8d4d50c06a51c2fffa6cff6272098ad6540aed1a76d15fb9318194d8", - "sha256:7c2875855b0ff77b2a64a0365e24455d9990730d6431b9e0ee18ad8acee13dbd", - "sha256:7eee9e7cea6adf3e3d24e304ac6b8300646e2a5d1cd3a3c2abed9101b0846761", - "sha256:800250ecdadb6d9c78eae4990da62743b857b470883fa27f652db8bdde7f6659", - "sha256:86976a1c5b25ae3f8ccae3a5306e443569ee3c3faf444dfd0f41cda24667ad57", - "sha256:8cd6d7cc958a3910f934ea8dbdf17b2364827bb4dafc38ce6eef6bb3d65ff09c", - "sha256:99df71520d25fade9db7c1076ac94eb994f4d2673ef2aa2e86ee039b6746d20c", - "sha256:a5a1595fe639f5988ba6a8e5bc9649af3baf26df3998a0abe56c02609392e0a4", - "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a", - "sha256:b1d432e8d08679a40e2a6d8b2f9770a5c21793a6f9f47fdd52c5ce1948a5a8a9", - "sha256:b8661b0238a69d7aafe156b7fa86c44b881387509653fdf857bebc5e4008ad42", - "sha256:ba96630bc17c875161df3818780af30e43be9b166ce51c9a18c1feae342906c2", - "sha256:bc6b93f9b966093cb0fd62ff1a7e4c09e6d546ad7c1de191767baffc57628f39", - "sha256:c124333816c3a9b03fbeef3a9f230ba9a737e9e5bb4060aa2107a86cc0a497fc", - "sha256:cd8d0c3be0515c12fed0bdbae072551c8b54b7192c7b1fda0ba56059a0179698", - "sha256:d9c45366def9a3dd85a6454c0e7908f2b3b8e9c138f5dc38fed7ce720d8453ed", - "sha256:f00d1345d84d8c86a63e476bb4955e46458b304b9575dcf71102b5c705320015", - "sha256:f3a255b2c19987fbbe62a9dfd6cff7ff2aa9ccab3fc75218fd4b7530f01efa24", - "sha256:fffb8ae78d8af97f849404f21411c95062db1496aeb3e56f146f0355c9989319" + "sha256:034abd6f3db8b9880aaee98f4f5d4dbec7c4829938463ec046517220b2f8574e", + "sha256:094e271a15b579650ebf4c5155c05dcd2a14fd4fdd72cf4854b2f7ad31ea30be", + "sha256:14a0cc77b0f089d2d2ffe3007db58f170dae9b9f54e569b299db871a3ab5bf46", + "sha256:1a881bc1309f3fce34696d07b00f13335c41f5f5a8770a33b09ebe23261cfc67", + "sha256:1d2b33e68d0ce64e26a4acc2e72d747292084f4e8db4c847c6f5f6cbe56ed6d8", + "sha256:213cd63c43263dbb522c1f8a7c9d072e25900f6975596f883f4bebd77295d4f3", + "sha256:23c2b2dc5213810208ca0b80b8666670eb4660bbfd9d45f58592cc4ddcfd62e1", + "sha256:2c7e2fc25f89a49a11599ec1e76821322439d90820108309bf42130d2f36c983", + "sha256:2eb4728a18dcd2908c7fccf74a982e241b467d178724545a48d0caf534b38ebf", + "sha256:34600ab34ebf1131a7613a260a61dbe8b62c188ec0ea4c296da7c9a06b004133", + "sha256:39ff73ec07be5e90330cc6ff5705c651ace83374189dcdcb46e6ff54b4a72cd6", + "sha256:404d681c698e3c8a40a61d0cd9412cc7364ab9a9cc6e144ae2992e11a2e77a20", + "sha256:40cecc4ea5abd2921682b57532baea5588cc5f80f0231c624056b146887274d2", + "sha256:430a63bae10b5086995db1b02694996336e5a8ac9a96b4200572b413dfdfccb9", + "sha256:4930255e28ff5545e2ca404637bcc56f031893142773b3468dc021c6c32a1390", + "sha256:6021910b086b3ca756755e86ddc64e0ddafd5e58e076c72cb1585162e5ad259b", + "sha256:625466edd01d43b75b1883a64d859168e4556261a5035b32f9d743b67ef44634", + "sha256:75651c14fde635e680496148a8526b328e09fe0572d9ae9b638648c46a544ba3", + "sha256:84141f722d45d0c2a89544dd29d35b3abfc13d2250ed7e68394eda7564bd6324", + "sha256:8adff9f138fc614347ff33812046787f7d43b3cef7c0f0171b3340cae333f6ca", + "sha256:951805d146922aed8357e4cc5671b8b0b9be1027f0619cea132a9f3f65f2f09c", + "sha256:9efc0acbbffb5236fbdf0409c04edce96bec4bdaa649d49985427bd1ec73e085", + "sha256:9ff730713d4c4f2f1c860e36c005c7cefc1c7c80c21c0688fd605aa43c9fcf09", + "sha256:a6872d695c896f00df46b71648eea332279ef4077a409e2fe94220208b6bb675", + "sha256:b198687ca9c8529662213538a9bb1e60fa0bf0f6af89292eb68fea28743fcd5a", + "sha256:b9d8c3187be7479ea5c3d30c32a5d73d62a621166675063b2edd21bc47614027", + "sha256:ba24af48643b12ffe49b27065d3babd52702d95ab70f50e1b34f71ca703e2c0d", + "sha256:bb32dc743b52467d488e7a7c8039b821da2826a9ba4f85b89ea95274f863280f", + "sha256:bb3be958022198531eb7ec2008cfc78c5b1eed51af8600c6c5d9160d89d8d249", + "sha256:bf5be867a0541a9fb47a4be0c5790a4bccd5b77b92f0a59eeec9375fafc2aa14", + "sha256:c06f6f144ad0a1bf84699aeea7eff6068ca5c63ceb404798198af7eb86082e33", + "sha256:c6da97aeb6a6d233fb6b17986234cc723b396b50a3c6804776351994f2a658fd", + "sha256:e0f51973ba93a9f97185049326d75b942b9aeb472bec616a129806facb129ebb", + "sha256:e1991bbb96f4050b09b5f811253c4f3cf05ee89a589379aa36cd623f21a31d6f", + "sha256:e5f08eb9a445d07720776df6e641975665c9ea12c9d8a331e0f6890f2dcd76ef", + "sha256:e78ad363ddb873a631e92a3c063ade1ecfb34cae71e9a2be6ad100f875ac1042", + "sha256:ed16339bc354a73e0a609df36d256672c7d296f3f767ac07257801aa064ff73c", + "sha256:f4dd97c19bd06bc557ad787a15b6489d2614ddaab5d104a0310eb314c724b2d2", + "sha256:f925f1ef673b4bd0271b1809b72b3270384f2b7d9d14a189b12b7fc02574d575", + "sha256:f95a2aef32614ed86216d3c450ab12a4e82084e8102e355707a1d96e33d51c34", + "sha256:fa07e138b3f6c04addfeaf56cc7fdb96c3b68a3fe5e5401251f231fce40a0d7a", + "sha256:fa35c266c8cd1a67d75971a1912b185b492d257092bdd2709bbdebe574ed228d" + ], + "index": "pypi", + "markers": "python_version >= '3.9'", + "version": "==2.3.0" + }, + "pydantic": { + "hashes": [ + "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", + "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b" ], "index": "pypi", - "version": "==2.2.3" + "markers": "python_version >= '3.9'", + "version": "==2.11.7" + }, + "pydantic-core": { + "hashes": [ + "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d", + "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac", + "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02", + "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", + "sha256:09fb9dd6571aacd023fe6aaca316bd01cf60ab27240d7eb39ebd66a3a15293b4", + "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22", + "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", + "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec", + "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d", + "sha256:0e6116757f7959a712db11f3e9c0a99ade00a5bbedae83cb801985aa154f071b", + "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", + "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", + "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052", + "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab", + "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", + "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c", + "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf", + "sha256:2807668ba86cb38c6817ad9bc66215ab8584d1d304030ce4f0887336f28a5e27", + "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", + "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8", + "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7", + "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612", + "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1", + "sha256:3a1c81334778f9e3af2f8aeb7a960736e5cab1dfebfb26aabca09afd2906c039", + "sha256:3abcd9392a36025e3bd55f9bd38d908bd17962cc49bc6da8e7e96285336e2bca", + "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7", + "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a", + "sha256:3eb3fe62804e8f859c49ed20a8451342de53ed764150cb14ca71357c765dc2a6", + "sha256:44857c3227d3fb5e753d5fe4a3420d6376fa594b07b621e220cd93703fe21782", + "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b", + "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7", + "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025", + "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", + "sha256:53a57d2ed685940a504248187d5685e49eb5eef0f696853647bf37c418c538f7", + "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b", + "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa", + "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", + "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea", + "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", + "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51", + "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e", + "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", + "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65", + "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2", + "sha256:6fa6dfc3e4d1f734a34710f391ae822e0a8eb8559a85c6979e14e65ee6ba2954", + "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b", + "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de", + "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", + "sha256:7f92c15cd1e97d4b12acd1cc9004fa092578acfa57b67ad5e43a197175d01a64", + "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb", + "sha256:83aa99b1285bc8f038941ddf598501a86f1536789740991d7d8756e34f1e74d9", + "sha256:87acbfcf8e90ca885206e98359d7dca4bcbb35abdc0ff66672a293e1d7a19101", + "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d", + "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef", + "sha256:8d55ab81c57b8ff8548c3e4947f119551253f4e3787a7bbc0b6b3ca47498a9d3", + "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1", + "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", + "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88", + "sha256:970919794d126ba8645f3837ab6046fb4e72bbc057b3709144066204c19a455d", + "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290", + "sha256:9fcd347d2cc5c23b06de6d3b7b8275be558a0c90549495c699e379a80bf8379e", + "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", + "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808", + "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc", + "sha256:a2b911a5b90e0374d03813674bf0a5fbbb7741570dcd4b4e85a2e48d17def29d", + "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc", + "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e", + "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640", + "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30", + "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e", + "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", + "sha256:c20c462aa4434b33a2661701b861604913f912254e441ab8d78d30485736115a", + "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", + "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f", + "sha256:c54c939ee22dc8e2d545da79fc5381f1c020d6d3141d3bd747eab59164dc89fb", + "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", + "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab", + "sha256:d3f26877a748dc4251cfcfda9dfb5f13fcb034f5308388066bcfe9031b63ae7d", + "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572", + "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593", + "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29", + "sha256:dac89aea9af8cd672fa7b510e7b8c33b0bba9a43186680550ccf23020f32d535", + "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1", + "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f", + "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8", + "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf", + "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246", + "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", + "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011", + "sha256:eb9b459ca4df0e5c87deb59d37377461a538852765293f9e6ee834f0435a93b9", + "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a", + "sha256:f481959862f57f29601ccced557cc2e817bce7533ab8e01a797a48b49c9692b3", + "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", + "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8", + "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a", + "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2", + "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c", + "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6", + "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d" + ], + "markers": "python_version >= '3.9'", + "version": "==2.33.2" }, "python-dateutil": { "hashes": [ @@ -256,11 +376,12 @@ }, "requests": { "hashes": [ - "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", - "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6" + "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", + "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422" ], "index": "pypi", - "version": "==2.32.3" + "markers": "python_version >= '3.8'", + "version": "==2.32.4" }, "six": { "hashes": [ @@ -270,6 +391,22 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.17.0" }, + "typing-extensions": { + "hashes": [ + "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4", + "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af" + ], + "markers": "python_version >= '3.9'", + "version": "==4.14.0" + }, + "typing-inspection": { + "hashes": [ + "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", + "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28" + ], + "markers": "python_version >= '3.9'", + "version": "==0.4.1" + }, "tzdata": { "hashes": [ "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", @@ -280,21 +417,29 @@ }, "urllib3": { "hashes": [ - "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", - "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813" + "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", + "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc" ], "markers": "python_version >= '3.9'", - "version": "==2.4.0" + "version": "==2.5.0" } }, "develop": { + "annotated-types": { + "hashes": [ + "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", + "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89" + ], + "markers": "python_version >= '3.8'", + "version": "==0.7.0" + }, "certifi": { "hashes": [ - "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6", - "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3" + "sha256:2e0c7ce7cb5d8f8634ca55d2ba7e6ec2689a2fd6537d8dec1296a477a4910057", + "sha256:d747aa5a8b9bbbb1bb8c22bb13e22bd1f18e9796defa16bab421f7f7a317323b" ], - "markers": "python_version >= '3.6'", - "version": "==2025.4.26" + "markers": "python_version >= '3.7'", + "version": "==2025.6.15" }, "charset-normalizer": { "hashes": [ @@ -412,64 +557,61 @@ }, "numpy": { "hashes": [ - "sha256:0255732338c4fdd00996c0421884ea8a3651eea555c3a56b84892b66f696eb70", - "sha256:02f226baeefa68f7d579e213d0f3493496397d8f1cff5e2b222af274c86a552a", - "sha256:059b51b658f4414fff78c6d7b1b4e18283ab5fa56d270ff212d5ba0c561846f4", - "sha256:0bcb1d057b7571334139129b7f941588f69ce7c4ed15a9d6162b2ea54ded700c", - "sha256:0cd48122a6b7eab8f06404805b1bd5856200e3ed6f8a1b9a194f9d9054631beb", - "sha256:19f4718c9012e3baea91a7dba661dcab2451cda2550678dc30d53acb91a7290f", - "sha256:1a161c2c79ab30fe4501d5a2bbfe8b162490757cf90b7f05be8b80bc02f7bb8e", - "sha256:1f4a922da1729f4c40932b2af4fe84909c7a6e167e6e99f71838ce3a29f3fe26", - "sha256:261a1ef047751bb02f29dfe337230b5882b54521ca121fc7f62668133cb119c9", - "sha256:262d23f383170f99cd9191a7c85b9a50970fe9069b2f8ab5d786eca8a675d60b", - "sha256:2ba321813a00e508d5421104464510cc962a6f791aa2fca1c97b1e65027da80d", - "sha256:2c1a1c6ccce4022383583a6ded7bbcda22fc635eb4eb1e0a053336425ed36dfa", - "sha256:352d330048c055ea6db701130abc48a21bec690a8d38f8284e00fab256dc1376", - "sha256:369e0d4647c17c9363244f3468f2227d557a74b6781cb62ce57cf3ef5cc7c610", - "sha256:36ab5b23915887543441efd0417e6a3baa08634308894316f446027611b53bf1", - "sha256:37e32e985f03c06206582a7323ef926b4e78bdaa6915095ef08070471865b906", - "sha256:3a801fef99668f309b88640e28d261991bfad9617c27beda4a3aec4f217ea073", - "sha256:3d14b17b9be5f9c9301f43d2e2a4886a33b53f4e6fdf9ca2f4cc60aeeee76372", - "sha256:422cc684f17bc963da5f59a31530b3936f57c95a29743056ef7a7903a5dbdf88", - "sha256:4520caa3807c1ceb005d125a75e715567806fed67e315cea619d5ec6e75a4191", - "sha256:47834cde750d3c9f4e52c6ca28a7361859fcaf52695c7dc3cc1a720b8922683e", - "sha256:47f9ed103af0bc63182609044b0490747e03bd20a67e391192dde119bf43d52f", - "sha256:498815b96f67dc347e03b719ef49c772589fb74b8ee9ea2c37feae915ad6ebda", - "sha256:54088a5a147ab71a8e7fdfd8c3601972751ded0739c6b696ad9cb0343e21ab73", - "sha256:55f09e00d4dccd76b179c0f18a44f041e5332fd0e022886ba1c0bbf3ea4a18d0", - "sha256:5a0ac90e46fdb5649ab6369d1ab6104bfe5854ab19b645bf5cda0127a13034ae", - "sha256:6411f744f7f20081b1b4e7112e0f4c9c5b08f94b9f086e6f0adf3645f85d3a4d", - "sha256:6413d48a9be53e183eb06495d8e3b006ef8f87c324af68241bbe7a39e8ff54c3", - "sha256:7451f92eddf8503c9b8aa4fe6aa7e87fd51a29c2cfc5f7dbd72efde6c65acf57", - "sha256:8b4c0773b6ada798f51f0f8e30c054d32304ccc6e9c5d93d46cb26f3d385ab19", - "sha256:8dfa94b6a4374e7851bbb6f35e6ded2120b752b063e6acdd3157e4d2bb922eba", - "sha256:97c8425d4e26437e65e1d189d22dff4a079b747ff9c2788057bfb8114ce1e133", - "sha256:9d75f338f5f79ee23548b03d801d28a505198297534f62416391857ea0479571", - "sha256:9de6832228f617c9ef45d948ec1cd8949c482238d68b2477e6f642c33a7b0a54", - "sha256:a4cbdef3ddf777423060c6f81b5694bad2dc9675f110c4b2a60dc0181543fac7", - "sha256:a9c0d994680cd991b1cb772e8b297340085466a6fe964bc9d4e80f5e2f43c291", - "sha256:aa70fdbdc3b169d69e8c59e65c07a1c9351ceb438e627f0fdcd471015cd956be", - "sha256:abe38cd8381245a7f49967a6010e77dbf3680bd3627c0fe4362dd693b404c7f8", - "sha256:b13f04968b46ad705f7c8a80122a42ae8f620536ea38cf4bdd374302926424dd", - "sha256:b4ea7e1cff6784e58fe281ce7e7f05036b3e1c89c6f922a6bfbc0a7e8768adbe", - "sha256:b6f91524d31b34f4a5fee24f5bc16dcd1491b668798b6d85585d836c1e633a6a", - "sha256:c26843fd58f65da9491165072da2cccc372530681de481ef670dcc8e27cfb066", - "sha256:c42365005c7a6c42436a54d28c43fe0e01ca11eb2ac3cefe796c25a5f98e5e9b", - "sha256:c8b82a55ef86a2d8e81b63da85e55f5537d2157165be1cb2ce7cfa57b6aef38b", - "sha256:ced69262a8278547e63409b2653b372bf4baff0870c57efa76c5703fd6543282", - "sha256:d2e3bdadaba0e040d1e7ab39db73e0afe2c74ae277f5614dad53eadbecbbb169", - "sha256:d403c84991b5ad291d3809bace5e85f4bbf44a04bdc9a88ed2bb1807b3360bb8", - "sha256:d7543263084a85fbc09c704b515395398d31d6395518446237eac219eab9e55e", - "sha256:d8882a829fd779f0f43998e931c466802a77ca1ee0fe25a3abe50278616b1471", - "sha256:e4f0b035d9d0ed519c813ee23e0a733db81ec37d2e9503afbb6e54ccfdee0fa7", - "sha256:e8b025c351b9f0e8b5436cf28a07fa4ac0204d67b38f01433ac7f9b870fa38c6", - "sha256:eb7fd5b184e5d277afa9ec0ad5e4eb562ecff541e7f60e69ee69c8d59e9aeaba", - "sha256:ec31367fd6a255dc8de4772bd1658c3e926d8e860a0b6e922b615e532d320ddc", - "sha256:ee461a4eaab4f165b68780a6a1af95fb23a29932be7569b9fab666c407969051", - "sha256:f5045039100ed58fa817a6227a356240ea1b9a1bc141018864c306c1a16d4175" + "sha256:0025048b3c1557a20bc80d06fdeb8cc7fc193721484cca82b2cfa072fec71a93", + "sha256:010ce9b4f00d5c036053ca684c77441f2f2c934fd23bee058b4d6f196efd8280", + "sha256:0bb3a4a61e1d327e035275d2a993c96fa786e4913aa089843e6a2d9dd205c66a", + "sha256:0c4d9e0a8368db90f93bd192bfa771ace63137c3488d198ee21dfb8e7771916e", + "sha256:15aa4c392ac396e2ad3d0a2680c0f0dee420f9fed14eef09bdb9450ee6dcb7b7", + "sha256:18703df6c4a4fee55fd3d6e5a253d01c5d33a295409b03fda0c86b3ca2ff41a1", + "sha256:1ec9ae20a4226da374362cca3c62cd753faf2f951440b0e3b98e93c235441d2b", + "sha256:23ab05b2d241f76cb883ce8b9a93a680752fbfcbd51c50eff0b88b979e471d8c", + "sha256:25a1992b0a3fdcdaec9f552ef10d8103186f5397ab45e2d25f8ac51b1a6b97e8", + "sha256:2959d8f268f3d8ee402b04a9ec4bb7604555aeacf78b360dc4ec27f1d508177d", + "sha256:2a809637460e88a113e186e87f228d74ae2852a2e0c44de275263376f17b5bdc", + "sha256:2fb86b7e58f9ac50e1e9dd1290154107e47d1eef23a0ae9145ded06ea606f992", + "sha256:36890eb9e9d2081137bd78d29050ba63b8dab95dff7912eadf1185e80074b2a0", + "sha256:39bff12c076812595c3a306f22bfe49919c5513aa1e0e70fac756a0be7c2a2b8", + "sha256:467db865b392168ceb1ef1ffa6f5a86e62468c43e0cfb4ab6da667ede10e58db", + "sha256:4e602e1b8682c2b833af89ba641ad4176053aaa50f5cacda1a27004352dde943", + "sha256:5902660491bd7a48b2ec16c23ccb9124b8abfd9583c5fdfa123fe6b421e03de1", + "sha256:5ccb7336eaf0e77c1635b232c141846493a588ec9ea777a7c24d7166bb8533ae", + "sha256:5f1b8f26d1086835f442286c1d9b64bb3974b0b1e41bb105358fd07d20872952", + "sha256:6269b9edfe32912584ec496d91b00b6d34282ca1d07eb10e82dfc780907d6c2e", + "sha256:6ea9e48336a402551f52cd8f593343699003d2353daa4b72ce8d34f66b722070", + "sha256:762e0c0c6b56bdedfef9a8e1d4538556438288c4276901ea008ae44091954e29", + "sha256:7be91b2239af2658653c5bb6f1b8bccafaf08226a258caf78ce44710a0160d30", + "sha256:7dea630156d39b02a63c18f508f85010230409db5b2927ba59c8ba4ab3e8272e", + "sha256:867ef172a0976aaa1f1d1b63cf2090de8b636a7674607d514505fb7276ab08fc", + "sha256:8d5ee6eec45f08ce507a6570e06f2f879b374a552087a4179ea7838edbcbfa42", + "sha256:8e333040d069eba1652fb08962ec5b76af7f2c7bce1df7e1418c8055cf776f25", + "sha256:a5ee121b60aa509679b682819c602579e1df14a5b07fe95671c8849aad8f2115", + "sha256:a780033466159c2270531e2b8ac063704592a0bc62ec4a1b991c7c40705eb0e8", + "sha256:a894f3816eb17b29e4783e5873f92faf55b710c2519e5c351767c51f79d8526d", + "sha256:a8b740f5579ae4585831b3cf0e3b0425c667274f82a484866d2adf9570539369", + "sha256:ad506d4b09e684394c42c966ec1527f6ebc25da7f4da4b1b056606ffe446b8a3", + "sha256:afed2ce4a84f6b0fc6c1ce734ff368cbf5a5e24e8954a338f3bdffa0718adffb", + "sha256:b0b5397374f32ec0649dd98c652a1798192042e715df918c20672c62fb52d4b8", + "sha256:bada6058dd886061f10ea15f230ccf7dfff40572e99fef440a4a857c8728c9c0", + "sha256:c4913079974eeb5c16ccfd2b1f09354b8fed7e0d6f2cab933104a09a6419b1ee", + "sha256:c5bdf2015ccfcee8253fb8be695516ac4457c743473a43290fd36eba6a1777eb", + "sha256:c6e0bf9d1a2f50d2b65a7cf56db37c095af17b59f6c132396f7c6d5dd76484df", + "sha256:ce2ce9e5de4703a673e705183f64fd5da5bf36e7beddcb63a25ee2286e71ca48", + "sha256:cfecc7822543abdea6de08758091da655ea2210b8ffa1faf116b940693d3df76", + "sha256:d4580adadc53311b163444f877e0789f1c8861e2698f6b2a4ca852fda154f3ff", + "sha256:d70f20df7f08b90a2062c1f07737dd340adccf2068d0f1b9b3d56e2038979fee", + "sha256:e344eb79dab01f1e838ebb67aab09965fb271d6da6b00adda26328ac27d4a66e", + "sha256:e610832418a2bc09d974cc9fecebfa51e9532d6190223bc5ef6a7402ebf3b5cb", + "sha256:e772dda20a6002ef7061713dc1e2585bc1b534e7909b2030b5a46dae8ff077ab", + "sha256:e7cbf5a5eafd8d230a3ce356d892512185230e4781a361229bd902ff403bc660", + "sha256:eabd7e8740d494ce2b4ea0ff05afa1b7b291e978c0ae075487c51e8bd93c0c68", + "sha256:ebb8603d45bc86bbd5edb0d63e52c5fd9e7945d3a503b77e486bd88dde67a19b", + "sha256:ec0bdafa906f95adc9a0c6f26a4871fa753f25caaa0e032578a30457bff0af6a", + "sha256:eccb9a159db9aed60800187bc47a6d3451553f0e1b08b068d8b277ddfbb9b244", + "sha256:ee8340cb48c9b7a5899d1149eece41ca535513a9698098edbade2a8e7a84da77" ], "index": "pypi", - "version": "==2.2.5" + "markers": "python_version >= '3.11'", + "version": "==2.3.1" }, "packaging": { "hashes": [ @@ -481,51 +623,52 @@ }, "pandas": { "hashes": [ - "sha256:062309c1b9ea12a50e8ce661145c6aab431b1e99530d3cd60640e255778bd43a", - "sha256:15c0e1e02e93116177d29ff83e8b1619c93ddc9c49083f237d4312337a61165d", - "sha256:1948ddde24197a0f7add2bdc4ca83bf2b1ef84a1bc8ccffd95eda17fd836ecb5", - "sha256:1db71525a1538b30142094edb9adc10be3f3e176748cd7acc2240c2f2e5aa3a4", - "sha256:22a9d949bfc9a502d320aa04e5d02feab689d61da4e7764b62c30b991c42c5f0", - "sha256:29401dbfa9ad77319367d36940cd8a0b3a11aba16063e39632d98b0e931ddf32", - "sha256:31d0ced62d4ea3e231a9f228366919a5ea0b07440d9d4dac345376fd8e1477ea", - "sha256:3508d914817e153ad359d7e069d752cdd736a247c322d932eb89e6bc84217f28", - "sha256:37e0aced3e8f539eccf2e099f65cdb9c8aa85109b0be6e93e2baff94264bdc6f", - "sha256:381175499d3802cde0eabbaf6324cce0c4f5d52ca6f8c377c29ad442f50f6348", - "sha256:38cf8125c40dae9d5acc10fa66af8ea6fdf760b2714ee482ca691fc66e6fcb18", - "sha256:3b71f27954685ee685317063bf13c7709a7ba74fc996b84fc6821c59b0f06468", - "sha256:3fc6873a41186404dad67245896a6e440baacc92f5b716ccd1bc9ed2995ab2c5", - "sha256:4850ba03528b6dd51d6c5d273c46f183f39a9baf3f0143e566b89450965b105e", - "sha256:4f18ba62b61d7e192368b84517265a99b4d7ee8912f8708660fb4a366cc82667", - "sha256:56534ce0746a58afaf7942ba4863e0ef81c9c50d3f0ae93e9497d6a41a057645", - "sha256:59ef3764d0fe818125a5097d2ae867ca3fa64df032331b7e0917cf5d7bf66b13", - "sha256:5dbca4c1acd72e8eeef4753eeca07de9b1db4f398669d5994086f788a5d7cc30", - "sha256:5de54125a92bb4d1c051c0659e6fcb75256bf799a732a87184e5ea503965bce3", - "sha256:61c5ad4043f791b61dd4752191d9f07f0ae412515d59ba8f005832a532f8736d", - "sha256:6374c452ff3ec675a8f46fd9ab25c4ad0ba590b71cf0656f8b6daa5202bca3fb", - "sha256:63cc132e40a2e084cf01adf0775b15ac515ba905d7dcca47e9a251819c575ef3", - "sha256:66108071e1b935240e74525006034333f98bcdb87ea116de573a6a0dccb6c039", - "sha256:6dfcb5ee8d4d50c06a51c2fffa6cff6272098ad6540aed1a76d15fb9318194d8", - "sha256:7c2875855b0ff77b2a64a0365e24455d9990730d6431b9e0ee18ad8acee13dbd", - "sha256:7eee9e7cea6adf3e3d24e304ac6b8300646e2a5d1cd3a3c2abed9101b0846761", - "sha256:800250ecdadb6d9c78eae4990da62743b857b470883fa27f652db8bdde7f6659", - "sha256:86976a1c5b25ae3f8ccae3a5306e443569ee3c3faf444dfd0f41cda24667ad57", - "sha256:8cd6d7cc958a3910f934ea8dbdf17b2364827bb4dafc38ce6eef6bb3d65ff09c", - "sha256:99df71520d25fade9db7c1076ac94eb994f4d2673ef2aa2e86ee039b6746d20c", - "sha256:a5a1595fe639f5988ba6a8e5bc9649af3baf26df3998a0abe56c02609392e0a4", - "sha256:ad5b65698ab28ed8d7f18790a0dc58005c7629f227be9ecc1072aa74c0c1d43a", - "sha256:b1d432e8d08679a40e2a6d8b2f9770a5c21793a6f9f47fdd52c5ce1948a5a8a9", - "sha256:b8661b0238a69d7aafe156b7fa86c44b881387509653fdf857bebc5e4008ad42", - "sha256:ba96630bc17c875161df3818780af30e43be9b166ce51c9a18c1feae342906c2", - "sha256:bc6b93f9b966093cb0fd62ff1a7e4c09e6d546ad7c1de191767baffc57628f39", - "sha256:c124333816c3a9b03fbeef3a9f230ba9a737e9e5bb4060aa2107a86cc0a497fc", - "sha256:cd8d0c3be0515c12fed0bdbae072551c8b54b7192c7b1fda0ba56059a0179698", - "sha256:d9c45366def9a3dd85a6454c0e7908f2b3b8e9c138f5dc38fed7ce720d8453ed", - "sha256:f00d1345d84d8c86a63e476bb4955e46458b304b9575dcf71102b5c705320015", - "sha256:f3a255b2c19987fbbe62a9dfd6cff7ff2aa9ccab3fc75218fd4b7530f01efa24", - "sha256:fffb8ae78d8af97f849404f21411c95062db1496aeb3e56f146f0355c9989319" + "sha256:034abd6f3db8b9880aaee98f4f5d4dbec7c4829938463ec046517220b2f8574e", + "sha256:094e271a15b579650ebf4c5155c05dcd2a14fd4fdd72cf4854b2f7ad31ea30be", + "sha256:14a0cc77b0f089d2d2ffe3007db58f170dae9b9f54e569b299db871a3ab5bf46", + "sha256:1a881bc1309f3fce34696d07b00f13335c41f5f5a8770a33b09ebe23261cfc67", + "sha256:1d2b33e68d0ce64e26a4acc2e72d747292084f4e8db4c847c6f5f6cbe56ed6d8", + "sha256:213cd63c43263dbb522c1f8a7c9d072e25900f6975596f883f4bebd77295d4f3", + "sha256:23c2b2dc5213810208ca0b80b8666670eb4660bbfd9d45f58592cc4ddcfd62e1", + "sha256:2c7e2fc25f89a49a11599ec1e76821322439d90820108309bf42130d2f36c983", + "sha256:2eb4728a18dcd2908c7fccf74a982e241b467d178724545a48d0caf534b38ebf", + "sha256:34600ab34ebf1131a7613a260a61dbe8b62c188ec0ea4c296da7c9a06b004133", + "sha256:39ff73ec07be5e90330cc6ff5705c651ace83374189dcdcb46e6ff54b4a72cd6", + "sha256:404d681c698e3c8a40a61d0cd9412cc7364ab9a9cc6e144ae2992e11a2e77a20", + "sha256:40cecc4ea5abd2921682b57532baea5588cc5f80f0231c624056b146887274d2", + "sha256:430a63bae10b5086995db1b02694996336e5a8ac9a96b4200572b413dfdfccb9", + "sha256:4930255e28ff5545e2ca404637bcc56f031893142773b3468dc021c6c32a1390", + "sha256:6021910b086b3ca756755e86ddc64e0ddafd5e58e076c72cb1585162e5ad259b", + "sha256:625466edd01d43b75b1883a64d859168e4556261a5035b32f9d743b67ef44634", + "sha256:75651c14fde635e680496148a8526b328e09fe0572d9ae9b638648c46a544ba3", + "sha256:84141f722d45d0c2a89544dd29d35b3abfc13d2250ed7e68394eda7564bd6324", + "sha256:8adff9f138fc614347ff33812046787f7d43b3cef7c0f0171b3340cae333f6ca", + "sha256:951805d146922aed8357e4cc5671b8b0b9be1027f0619cea132a9f3f65f2f09c", + "sha256:9efc0acbbffb5236fbdf0409c04edce96bec4bdaa649d49985427bd1ec73e085", + "sha256:9ff730713d4c4f2f1c860e36c005c7cefc1c7c80c21c0688fd605aa43c9fcf09", + "sha256:a6872d695c896f00df46b71648eea332279ef4077a409e2fe94220208b6bb675", + "sha256:b198687ca9c8529662213538a9bb1e60fa0bf0f6af89292eb68fea28743fcd5a", + "sha256:b9d8c3187be7479ea5c3d30c32a5d73d62a621166675063b2edd21bc47614027", + "sha256:ba24af48643b12ffe49b27065d3babd52702d95ab70f50e1b34f71ca703e2c0d", + "sha256:bb32dc743b52467d488e7a7c8039b821da2826a9ba4f85b89ea95274f863280f", + "sha256:bb3be958022198531eb7ec2008cfc78c5b1eed51af8600c6c5d9160d89d8d249", + "sha256:bf5be867a0541a9fb47a4be0c5790a4bccd5b77b92f0a59eeec9375fafc2aa14", + "sha256:c06f6f144ad0a1bf84699aeea7eff6068ca5c63ceb404798198af7eb86082e33", + "sha256:c6da97aeb6a6d233fb6b17986234cc723b396b50a3c6804776351994f2a658fd", + "sha256:e0f51973ba93a9f97185049326d75b942b9aeb472bec616a129806facb129ebb", + "sha256:e1991bbb96f4050b09b5f811253c4f3cf05ee89a589379aa36cd623f21a31d6f", + "sha256:e5f08eb9a445d07720776df6e641975665c9ea12c9d8a331e0f6890f2dcd76ef", + "sha256:e78ad363ddb873a631e92a3c063ade1ecfb34cae71e9a2be6ad100f875ac1042", + "sha256:ed16339bc354a73e0a609df36d256672c7d296f3f767ac07257801aa064ff73c", + "sha256:f4dd97c19bd06bc557ad787a15b6489d2614ddaab5d104a0310eb314c724b2d2", + "sha256:f925f1ef673b4bd0271b1809b72b3270384f2b7d9d14a189b12b7fc02574d575", + "sha256:f95a2aef32614ed86216d3c450ab12a4e82084e8102e355707a1d96e33d51c34", + "sha256:fa07e138b3f6c04addfeaf56cc7fdb96c3b68a3fe5e5401251f231fce40a0d7a", + "sha256:fa35c266c8cd1a67d75971a1912b185b492d257092bdd2709bbdebe574ed228d" ], "index": "pypi", - "version": "==2.2.3" + "markers": "python_version >= '3.9'", + "version": "==2.3.0" }, "parcllabs": { "editable": true, @@ -533,19 +676,142 @@ }, "pluggy": { "hashes": [ - "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", - "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669" + "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", + "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746" + ], + "markers": "python_version >= '3.9'", + "version": "==1.6.0" + }, + "pydantic": { + "hashes": [ + "sha256:d989c3c6cb79469287b1569f7447a17848c998458d49ebe294e975b9baf0f0db", + "sha256:dde5df002701f6de26248661f6835bbe296a47bf73990135c7d07ce741b9623b" + ], + "index": "pypi", + "markers": "python_version >= '3.9'", + "version": "==2.11.7" + }, + "pydantic-core": { + "hashes": [ + "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d", + "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac", + "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02", + "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56", + "sha256:09fb9dd6571aacd023fe6aaca316bd01cf60ab27240d7eb39ebd66a3a15293b4", + "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22", + "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef", + "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec", + "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d", + "sha256:0e6116757f7959a712db11f3e9c0a99ade00a5bbedae83cb801985aa154f071b", + "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a", + "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f", + "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052", + "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab", + "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916", + "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c", + "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf", + "sha256:2807668ba86cb38c6817ad9bc66215ab8584d1d304030ce4f0887336f28a5e27", + "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a", + "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8", + "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7", + "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612", + "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1", + "sha256:3a1c81334778f9e3af2f8aeb7a960736e5cab1dfebfb26aabca09afd2906c039", + "sha256:3abcd9392a36025e3bd55f9bd38d908bd17962cc49bc6da8e7e96285336e2bca", + "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7", + "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a", + "sha256:3eb3fe62804e8f859c49ed20a8451342de53ed764150cb14ca71357c765dc2a6", + "sha256:44857c3227d3fb5e753d5fe4a3420d6376fa594b07b621e220cd93703fe21782", + "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b", + "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7", + "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025", + "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849", + "sha256:53a57d2ed685940a504248187d5685e49eb5eef0f696853647bf37c418c538f7", + "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b", + "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa", + "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e", + "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea", + "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac", + "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51", + "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e", + "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162", + "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65", + "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2", + "sha256:6fa6dfc3e4d1f734a34710f391ae822e0a8eb8559a85c6979e14e65ee6ba2954", + "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b", + "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de", + "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc", + "sha256:7f92c15cd1e97d4b12acd1cc9004fa092578acfa57b67ad5e43a197175d01a64", + "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb", + "sha256:83aa99b1285bc8f038941ddf598501a86f1536789740991d7d8756e34f1e74d9", + "sha256:87acbfcf8e90ca885206e98359d7dca4bcbb35abdc0ff66672a293e1d7a19101", + "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d", + "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef", + "sha256:8d55ab81c57b8ff8548c3e4947f119551253f4e3787a7bbc0b6b3ca47498a9d3", + "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1", + "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5", + "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88", + "sha256:970919794d126ba8645f3837ab6046fb4e72bbc057b3709144066204c19a455d", + "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290", + "sha256:9fcd347d2cc5c23b06de6d3b7b8275be558a0c90549495c699e379a80bf8379e", + "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d", + "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808", + "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc", + "sha256:a2b911a5b90e0374d03813674bf0a5fbbb7741570dcd4b4e85a2e48d17def29d", + "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc", + "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e", + "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640", + "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30", + "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e", + "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9", + "sha256:c20c462aa4434b33a2661701b861604913f912254e441ab8d78d30485736115a", + "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9", + "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f", + "sha256:c54c939ee22dc8e2d545da79fc5381f1c020d6d3141d3bd747eab59164dc89fb", + "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5", + "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab", + "sha256:d3f26877a748dc4251cfcfda9dfb5f13fcb034f5308388066bcfe9031b63ae7d", + "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572", + "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593", + "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29", + "sha256:dac89aea9af8cd672fa7b510e7b8c33b0bba9a43186680550ccf23020f32d535", + "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1", + "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f", + "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8", + "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf", + "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246", + "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9", + "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011", + "sha256:eb9b459ca4df0e5c87deb59d37377461a538852765293f9e6ee834f0435a93b9", + "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a", + "sha256:f481959862f57f29601ccced557cc2e817bce7533ab8e01a797a48b49c9692b3", + "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6", + "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8", + "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a", + "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2", + "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c", + "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6", + "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d" + ], + "markers": "python_version >= '3.9'", + "version": "==2.33.2" + }, + "pygments": { + "hashes": [ + "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", + "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b" ], "markers": "python_version >= '3.8'", - "version": "==1.5.0" + "version": "==2.19.2" }, "pytest": { "hashes": [ - "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", - "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845" + "sha256:539c70ba6fcead8e78eebbf1115e8b589e7565830d7d006a8723f19ac8a0afb7", + "sha256:7c67fd69174877359ed9371ec3af8a3d2b04741818c51e5e99cc1742251fa93c" ], "index": "pypi", - "version": "==8.3.5" + "markers": "python_version >= '3.9'", + "version": "==8.4.1" }, "python-dateutil": { "hashes": [ @@ -564,35 +830,37 @@ }, "requests": { "hashes": [ - "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", - "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6" + "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", + "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422" ], "index": "pypi", - "version": "==2.32.3" + "markers": "python_version >= '3.8'", + "version": "==2.32.4" }, "ruff": { "hashes": [ - "sha256:0eba551324733efc76116d9f3a0d52946bc2751f0cd30661564117d6fd60897c", - "sha256:161eb4cff5cfefdb6c9b8b3671d09f7def2f960cee33481dd898caf2bcd02304", - "sha256:258f3585057508d317610e8a412788cf726efeefa2fec4dba4001d9e6f90d46c", - "sha256:304432e4c4a792e3da85b7699feb3426a0908ab98bf29df22a31b0cdd098fac2", - "sha256:3dca977cc4fc8f66e89900fa415ffe4dbc2e969da9d7a54bfca81a128c5ac219", - "sha256:4d9aaa91035bdf612c8ee7266153bcf16005c7c7e2f5878406911c92a31633cb", - "sha256:5b18caa297a786465cc511d7f8be19226acf9c0a1127e06e736cd4e1878c3ea2", - "sha256:6d742d10626f9004b781f4558154bb226620a7242080e11caeffab1a40e99df8", - "sha256:6e70d11043bef637c5617297bdedec9632af15d53ac1e1ba29c448da9341b0c4", - "sha256:727d01702f7c30baed3fc3a34901a640001a2828c793525043c29f7614994a8c", - "sha256:7f024d32e62faad0f76b2d6afd141b8c171515e4fb91ce9fd6464335c81244e5", - "sha256:896a37516c594805e34020c4a7546c8f8a234b679a7716a3f08197f38913e1a3", - "sha256:ab86d22d3d721a40dd3ecbb5e86ab03b2e053bc93c700dc68d1c3346b36ce835", - "sha256:c1dba3135ca503727aa4648152c0fa67c3b1385d3dc81c75cd8a229c4b2a1458", - "sha256:c657fa987d60b104d2be8b052d66da0a2a88f9bd1d66b2254333e84ea2720c7f", - "sha256:d365618d3ad747432e1ae50d61775b78c055fee5936d77fb4d92c6f559741948", - "sha256:f2e74b021d0de5eceb8bd32919f6ff8a9b40ee62ed97becd44993ae5b9949474", - "sha256:f9b5ef39820abc0f2c62111f7045009e46b275f5b99d5e59dda113c39b7f4f38" + "sha256:05ed0c914fabc602fc1f3b42c53aa219e5736cb030cdd85640c32dbc73da74a6", + "sha256:07a7aa9b69ac3fcfda3c507916d5d1bca10821fe3797d46bad10f2c6de1edda0", + "sha256:0c0758038f81beec8cc52ca22de9685b8ae7f7cc18c013ec2050012862cc9165", + "sha256:139b3d28027987b78fc8d6cfb61165447bdf3740e650b7c480744873688808c2", + "sha256:1e55e44e770e061f55a7dbc6e9aed47feea07731d809a3710feda2262d2d4d8a", + "sha256:3a9512af224b9ac4757f7010843771da6b2b0935a9e5e76bb407caa901a1a514", + "sha256:4d047db3662418d4a848a3fdbfaf17488b34b62f527ed6f10cb8afd78135bc5c", + "sha256:5652a9ecdb308a1754d96a68827755f28d5dfb416b06f60fd9e13f26191a8848", + "sha256:68853e8517b17bba004152aebd9dd77d5213e503a5f2789395b25f26acac0da4", + "sha256:6a315992297a7435a66259073681bb0d8647a826b7a6de45c6934b2ca3a9ed51", + "sha256:7162a4c816f8d1555eb195c46ae0bd819834d2a3f18f98cc63819a7b46f474fb", + "sha256:7d235618283718ee2fe14db07f954f9b2423700919dc688eacf3f8797a11315c", + "sha256:8cd24580405ad8c1cc64d61725bca091d6b6da7eb3d36f72cc605467069d7e8b", + "sha256:952d0630eae628250ab1c70a7fffb641b03e6b4a2d3f3ec6c1d19b4ab6c6c807", + "sha256:b08df3d96db798e5beb488d4df03011874aff919a97dcc2dd8539bb2be5d6a88", + "sha256:c021f04ea06966b02614d442e94071781c424ab8e02ec7af2f037b4c1e01cc82", + "sha256:d00b7a157b8fb6d3827b49d3324da34a1e3f93492c1f97b08e222ad7e9b291e0", + "sha256:e7731c3eec50af71597243bace7ec6104616ca56dda2b99c89935fe926bdcd48" ], "index": "pypi", - "version": "==0.11.8" + "markers": "python_version >= '3.7'", + "version": "==0.12.0" }, "six": { "hashes": [ @@ -602,6 +870,22 @@ "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", "version": "==1.17.0" }, + "typing-extensions": { + "hashes": [ + "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4", + "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af" + ], + "markers": "python_version >= '3.9'", + "version": "==4.14.0" + }, + "typing-inspection": { + "hashes": [ + "sha256:389055682238f53b04f7badcb49b989835495a96700ced5dab2d8feae4b26f51", + "sha256:6ae134cc0203c33377d43188d4064e9b357dba58cff3185f22924610e70a9d28" + ], + "markers": "python_version >= '3.9'", + "version": "==0.4.1" + }, "tzdata": { "hashes": [ "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", @@ -612,11 +896,11 @@ }, "urllib3": { "hashes": [ - "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", - "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813" + "sha256:3fc47733c7e419d4bc3f6b3dc2b4f890bb743906a30d56ba4a5bfa4bbff92760", + "sha256:e6b01673c0fa6a13e374b50871808eb3bf7046c4b125b216f6bf1cc604cff0dc" ], "markers": "python_version >= '3.9'", - "version": "==2.4.0" + "version": "==2.5.0" } } } diff --git a/parcllabs/__init__.py b/parcllabs/__init__.py index f4d4dc9..858b3c9 100644 --- a/parcllabs/__init__.py +++ b/parcllabs/__init__.py @@ -1,11 +1,9 @@ -from typing import Optional - from parcllabs.__version__ import VERSION as __version__ # noqa: F401, N811 # Constants DEFAULT_API_BASE = "https://api.parcllabs.com" -api_key: Optional[str] = None # noqa: UP007 +api_key: str | None = None api_base = DEFAULT_API_BASE from parcllabs.parcllabs_client import ParclLabsClient # noqa: E402, F401 diff --git a/parcllabs/__version__.py b/parcllabs/__version__.py index b78c38e..43fc4a2 100644 --- a/parcllabs/__version__.py +++ b/parcllabs/__version__.py @@ -1 +1 @@ -VERSION = "1.14.3" +VERSION = "1.14.4" diff --git a/parcllabs/schemas/__init__.py b/parcllabs/schemas/__init__.py new file mode 100644 index 0000000..0b3a7e6 --- /dev/null +++ b/parcllabs/schemas/__init__.py @@ -0,0 +1 @@ +# Schemas package for ParclLabs SDK diff --git a/parcllabs/schemas/schemas.py b/parcllabs/schemas/schemas.py new file mode 100644 index 0000000..cf62f8a --- /dev/null +++ b/parcllabs/schemas/schemas.py @@ -0,0 +1,296 @@ +""" +Pydantic schemas for PropertyV2Service input parameters. +""" + +from datetime import datetime +from typing import Any + +from pydantic import BaseModel, Field, ValidationInfo, field_validator + +from parcllabs.enums import PropertyTypes, RequestLimits + + +class GeoCoordinates(BaseModel): + """Schema for geographic coordinates with radius.""" + + latitude: float = Field(..., ge=-90, le=90, description="Latitude in decimal degrees") + longitude: float = Field(..., ge=-180, le=180, description="Longitude in decimal degrees") + radius: float = Field(..., gt=0, description="Radius in miles") + + +class PropertyV2RetrieveParams(BaseModel): + """ + Input parameters schema for PropertyV2Service.retrieve() method. + + This schema validates and manages all input parameters for property searches, + including search criteria, property filters, event filters, and owner filters. + """ + + # Search criteria + parcl_ids: list[int] | None = Field(default=None, description="List of parcl_ids to filter by") + parcl_property_ids: list[int] | None = Field( + default=None, description="List of parcl_property_ids to filter by" + ) + geo_coordinates: GeoCoordinates | None = Field( + default=None, description="Geographic coordinates with radius to filter by" + ) + + # Property filters + property_types: list[str] | None = Field( + default=None, description="List of property types to filter by" + ) + min_beds: int | None = Field(default=None, ge=0, description="Minimum number of bedrooms") + max_beds: int | None = Field(default=None, ge=0, description="Maximum number of bedrooms") + min_baths: float | None = Field(default=None, ge=0, description="Minimum number of bathrooms") + max_baths: float | None = Field(default=None, ge=0, description="Maximum number of bathrooms") + min_sqft: int | None = Field(default=None, ge=0, description="Minimum square footage") + max_sqft: int | None = Field(default=None, ge=0, description="Maximum square footage") + min_year_built: int | None = Field( + default=None, ge=1800, le=2100, description="Minimum year built" + ) + max_year_built: int | None = Field( + default=None, ge=1800, le=2100, description="Maximum year built" + ) + include_property_details: bool | None = Field( + default=None, description="Whether to include property details" + ) + min_record_added_date: str | None = Field( + default=None, description="Minimum record added date (YYYY-MM-DD)" + ) + max_record_added_date: str | None = Field( + default=None, description="Maximum record added date (YYYY-MM-DD)" + ) + + # Event filters + event_names: list[str] | None = Field( + default=None, description="List of event names to filter by" + ) + min_event_date: str | None = Field(default=None, description="Minimum event date (YYYY-MM-DD)") + max_event_date: str | None = Field(default=None, description="Maximum event date (YYYY-MM-DD)") + min_price: int | None = Field(default=None, ge=0, description="Minimum price") + max_price: int | None = Field(default=None, ge=0, description="Maximum price") + is_new_construction: bool | None = Field( + default=None, description="Whether to filter by new construction" + ) + min_record_updated_date: str | None = Field( + default=None, description="Minimum record updated date (YYYY-MM-DD)" + ) + max_record_updated_date: str | None = Field( + default=None, description="Maximum record updated date (YYYY-MM-DD)" + ) + + # Owner filters + is_current_owner: bool | None = Field( + default=None, description="Whether to filter by current owner" + ) + owner_name: list[str] | None = Field( + default=None, description="List of owner names to filter by" + ) + is_investor_owned: bool | None = Field( + default=None, description="Whether to filter by investor owned" + ) + is_owner_occupied: bool | None = Field( + default=None, description="Whether to filter by owner occupied" + ) + + # Market flags + current_on_market_flag: bool | None = Field( + default=None, description="Whether to filter by current on market flag" + ) + current_on_market_rental_flag: bool | None = Field( + default=None, description="Whether to filter by current on market rental flag" + ) + + # Pagination + limit: int | None = Field( + default=None, + ge=1, + le=RequestLimits.PROPERTY_V2_MAX.value, + description=f"Number of results to return (max: {RequestLimits.PROPERTY_V2_MAX.value})", + ) + + # Additional parameters + params: dict[str, Any] | None = Field( + default_factory=dict, description="Additional parameters to pass to the request" + ) + + @field_validator("property_types") + @classmethod + def validate_property_types(cls, v: list[str] | None) -> list[str] | None: + """Validate property types against allowed values.""" + if v is not None: + valid_types = [pt.value for pt in PropertyTypes] + for prop_type in v: + if prop_type.upper() not in valid_types: + raise ValueError(f"Invalid property type: {prop_type}") + return [pt.upper() for pt in v] + return v + + @field_validator("event_names") + @classmethod + def validate_event_names(cls, v: list[str] | None) -> list[str] | None: + """Validate event names and convert to uppercase.""" + if v is not None: + return [name.upper() for name in v] + return v + + @field_validator("owner_name") + @classmethod + def validate_owner_names(cls, v: list[str] | None) -> list[str] | None: + """Validate owner names and convert to uppercase.""" + if v is not None: + return [name.upper() for name in v] + return v + + @field_validator( + "min_record_added_date", + "max_record_added_date", + "min_event_date", + "max_event_date", + "min_record_updated_date", + "max_record_updated_date", + ) + @classmethod + def validate_date_format(cls, v: str | None) -> str | None: + """Validate date format is YYYY-MM-DD.""" + if v is not None: + try: + datetime.fromisoformat(v) + except ValueError as err: + raise ValueError(f"Date must be in YYYY-MM-DD format, got: {v}") from err + else: + return v + return v + + @field_validator("min_beds", "max_beds") + @classmethod + def validate_bedroom_range(cls, v: int | None, info: ValidationInfo) -> int | None: + """Validate bedroom range consistency.""" + if v is not None and info.data: + field_name = info.field_name + if field_name == "max_beds" and info.data.get("min_beds"): + if v < info.data["min_beds"]: + raise ValueError("max_beds cannot be less than min_beds") + elif field_name == "min_beds" and info.data.get("max_beds"): + if v > info.data["max_beds"]: + raise ValueError("min_beds cannot be greater than max_beds") + return v + + @field_validator("min_baths", "max_baths") + @classmethod + def validate_bathroom_range(cls, v: float | None, info: ValidationInfo) -> float | None: + """Validate bathroom range consistency.""" + if v is not None and info.data: + field_name = info.field_name + if field_name == "max_baths" and info.data.get("min_baths"): + if v < info.data["min_baths"]: + raise ValueError("max_baths cannot be less than min_baths") + elif field_name == "min_baths" and info.data.get("max_baths"): + if v > info.data["max_baths"]: + raise ValueError("min_baths cannot be greater than max_baths") + return v + + @field_validator("min_sqft", "max_sqft") + @classmethod + def validate_sqft_range(cls, v: int | None, info: ValidationInfo) -> int | None: + """Validate square footage range consistency.""" + if v is not None and info.data: + field_name = info.field_name + if field_name == "max_sqft" and info.data.get("min_sqft"): + if v < info.data["min_sqft"]: + raise ValueError("max_sqft cannot be less than min_sqft") + elif field_name == "min_sqft" and info.data.get("max_sqft"): + if v > info.data["max_sqft"]: + raise ValueError("min_sqft cannot be greater than max_sqft") + return v + + @field_validator("min_year_built", "max_year_built") + @classmethod + def validate_year_built_range(cls, v: int | None, info: ValidationInfo) -> int | None: + """Validate year built range consistency.""" + if v is not None and info.data: + field_name = info.field_name + if field_name == "max_year_built" and info.data.get("min_year_built"): + if v < info.data["min_year_built"]: + raise ValueError("max_year_built cannot be less than min_year_built") + elif field_name == "min_year_built" and info.data.get("max_year_built"): + if v > info.data["max_year_built"]: + raise ValueError("min_year_built cannot be greater than max_year_built") + return v + + @field_validator("min_price", "max_price") + @classmethod + def validate_price_range(cls, v: int | None, info: ValidationInfo) -> int | None: + """Validate price range consistency.""" + if v is not None and info.data: + field_name = info.field_name + if field_name == "max_price" and info.data.get("min_price"): + if v < info.data["min_price"]: + raise ValueError("max_price cannot be less than min_price") + elif field_name == "min_price" and info.data.get("max_price"): + if v > info.data["max_price"]: + raise ValueError("min_price cannot be greater than max_price") + return v + + @field_validator("min_record_added_date", "max_record_added_date") + @classmethod + def validate_record_added_date_range(cls, v: str | None, info: ValidationInfo) -> str | None: + """Validate record added date range consistency.""" + if v is not None and info.data: + field_name = info.field_name + if field_name == "max_record_added_date" and info.data.get("min_record_added_date"): + if v < info.data["min_record_added_date"]: + raise ValueError("max_record_added_date cannot be before min_record_added_date") + elif field_name == "min_record_added_date" and info.data.get("max_record_added_date"): + if v > info.data["max_record_added_date"]: + raise ValueError("min_record_added_date cannot be after max_record_added_date") + return v + + @field_validator("min_event_date", "max_event_date") + @classmethod + def validate_event_date_range(cls, v: str | None, info: ValidationInfo) -> str | None: + """Validate event date range consistency.""" + if v is not None and info.data: + field_name = info.field_name + if field_name == "max_event_date" and info.data.get("min_event_date"): + if v < info.data["min_event_date"]: + raise ValueError("max_event_date cannot be before min_event_date") + elif field_name == "min_event_date" and info.data.get("max_event_date"): + if v > info.data["max_event_date"]: + raise ValueError("min_event_date cannot be after max_event_date") + return v + + @field_validator("min_record_updated_date", "max_record_updated_date") + @classmethod + def validate_record_updated_date_range(cls, v: str | None, info: ValidationInfo) -> str | None: + """Validate record updated date range consistency.""" + if v is not None and info.data: + field_name = info.field_name + if field_name == "max_record_updated_date" and info.data.get("min_record_updated_date"): + if v < info.data["min_record_updated_date"]: + raise ValueError( + "max_record_updated_date cannot be before min_record_updated_date" + ) + elif field_name == "min_record_updated_date" and info.data.get( + "max_record_updated_date" + ): + if v > info.data["max_record_updated_date"]: + raise ValueError( + "min_record_updated_date cannot be after max_record_updated_date" + ) + return v + + class Config: + """Pydantic configuration.""" + + extra = "forbid" # Reject any extra fields + validate_assignment = True # Validate on assignment + str_strip_whitespace = True # Strip whitespace from strings + + +class PropertyV2RetrieveParamCategories(BaseModel): + """High level categories for PropertyV2RetrieveParams.""" + + property_filters: dict[str, Any] = Field(default_factory=dict, description="Property filters") + event_filters: dict[str, Any] = Field(default_factory=dict, description="Event filters") + owner_filters: dict[str, Any] = Field(default_factory=dict, description="Owner filters") diff --git a/parcllabs/services/properties/property_v2.py b/parcllabs/services/properties/property_v2.py index 45cb9b0..3741b21 100644 --- a/parcllabs/services/properties/property_v2.py +++ b/parcllabs/services/properties/property_v2.py @@ -5,6 +5,7 @@ import pandas as pd from parcllabs.enums import RequestLimits +from parcllabs.schemas.schemas import PropertyV2RetrieveParamCategories, PropertyV2RetrieveParams from parcllabs.services.parcllabs_service import ParclLabsService from parcllabs.services.validators import Validators @@ -12,6 +13,7 @@ class PropertyV2Service(ParclLabsService): def __init__(self, *args: object, **kwargs: object) -> None: super().__init__(*args, **kwargs) + self.simple_bool_validator = Validators.validate_input_bool_param_simple def _fetch_post(self, params: dict[str, Any], data: dict[str, Any]) -> list[dict]: """Fetch data using POST request with pagination support.""" @@ -168,104 +170,129 @@ def _build_search_criteria( return data - def _build_property_filters(self, **kwargs: dict) -> dict[str, Any]: - """Build property filters from keyword arguments.""" + def _build_numeric_filters(self, params: PropertyV2RetrieveParams) -> dict[str, Any]: + """Build numeric property filters.""" + filters = {} + + if params.min_beds is not None: + filters["min_beds"] = params.min_beds + if params.max_beds is not None: + filters["max_beds"] = params.max_beds + if params.min_baths is not None: + filters["min_baths"] = params.min_baths + if params.max_baths is not None: + filters["max_baths"] = params.max_baths + if params.min_sqft is not None: + filters["min_sqft"] = params.min_sqft + if params.max_sqft is not None: + filters["max_sqft"] = params.max_sqft + if params.min_year_built is not None: + filters["min_year_built"] = params.min_year_built + if params.max_year_built is not None: + filters["max_year_built"] = params.max_year_built + + return filters + + def _build_date_filters(self, params: PropertyV2RetrieveParams) -> dict[str, Any]: + """Build date-related property filters.""" + filters = {} + + if params.min_record_added_date is not None: + filters["min_record_added_date"] = params.min_record_added_date + if params.max_record_added_date is not None: + filters["max_record_added_date"] = params.max_record_added_date + + return filters + + def _build_boolean_filters(self, params: PropertyV2RetrieveParams) -> dict[str, Any]: + """Build boolean property filters.""" + filters = {} + + if params.current_on_market_flag is not None: + filters["current_on_market_flag"] = self.simple_bool_validator( + params.current_on_market_flag + ) + if params.current_on_market_rental_flag is not None: + filters["current_on_market_rental_flag"] = self.simple_bool_validator( + params.current_on_market_rental_flag + ) + if params.include_property_details is not None: + filters["include_property_details"] = self.simple_bool_validator( + params.include_property_details + ) + + return filters + + def _build_property_filters(self, params: PropertyV2RetrieveParams) -> dict[str, Any]: + """Build property filters from validated Pydantic schema.""" property_filters = {} - # Handle numeric range filters - numeric_filters = { - "bedrooms": ("min_beds", "max_beds"), - "bathrooms": ("min_baths", "max_baths"), - "square_feet": ("min_sqft", "max_sqft"), - "year_built": ("min_year_built", "max_year_built"), - } + # Build numeric filters + property_filters.update(self._build_numeric_filters(params)) + + # Build date filters + property_filters.update(self._build_date_filters(params)) - for _, (min_key, max_key) in numeric_filters.items(): - if kwargs.get(min_key): - property_filters[min_key] = kwargs[min_key] - if kwargs.get(max_key): - property_filters[max_key] = kwargs[max_key] + # Build boolean filters + property_filters.update(self._build_boolean_filters(params)) # Handle property types - if kwargs.get("property_types"): + if params.property_types: property_filters["property_types"] = [ - property_type.upper() for property_type in kwargs["property_types"] + property_type.upper() for property_type in params.property_types ] - # Handle date filters - date_filters = [ - ("min_record_added_date", "min_record_added_date"), - ("max_record_added_date", "max_record_added_date"), - ] - - for param_key, filter_key in date_filters: - if kwargs.get(param_key): - property_filters[filter_key] = Validators.validate_date(kwargs[param_key]) - - # Handle boolean parameters - if "include_property_details" in kwargs: - property_filters = Validators.validate_input_bool_param( - param=kwargs["include_property_details"], - param_name="include_property_details", - params_dict=property_filters, - ) - return property_filters - def _build_event_filters(self, **kwargs: dict) -> dict[str, Any]: - """Build event filters from keyword arguments.""" + def _build_event_filters(self, params: PropertyV2RetrieveParams) -> dict[str, Any]: + """Build event filters from validated Pydantic schema.""" event_filters = {} # Handle event names - if kwargs.get("event_names"): - event_filters["event_names"] = [name.upper() for name in kwargs["event_names"]] - - # Handle date filters - date_filters = [ - ("min_event_date", "min_event_date"), - ("max_event_date", "max_event_date"), - ("min_record_updated_date", "min_record_updated_date"), - ("max_record_updated_date", "max_record_updated_date"), - ] - - for param_key, filter_key in date_filters: - if kwargs.get(param_key): - event_filters[filter_key] = Validators.validate_date(kwargs[param_key]) - - # Handle price filters - if kwargs.get("min_price"): - event_filters["min_price"] = kwargs["min_price"] - if kwargs.get("max_price"): - event_filters["max_price"] = kwargs["max_price"] + if params.event_names: + event_filters["event_names"] = [event_name.upper() for event_name in params.event_names] + + # Handle date and price filters + if params.min_event_date is not None: + event_filters["min_event_date"] = params.min_event_date + if params.max_event_date is not None: + event_filters["max_event_date"] = params.max_event_date + if params.min_record_updated_date is not None: + event_filters["min_record_updated_date"] = params.min_record_updated_date + if params.max_record_updated_date is not None: + event_filters["max_record_updated_date"] = params.max_record_updated_date + if params.min_price is not None: + event_filters["min_price"] = params.min_price + if params.max_price is not None: + event_filters["max_price"] = params.max_price # Handle boolean parameters - if "is_new_construction" in kwargs: - event_filters = Validators.validate_input_bool_param( - param=kwargs["is_new_construction"], - param_name="is_new_construction", - params_dict=event_filters, + if params.is_new_construction is not None: + event_filters["is_new_construction"] = self.simple_bool_validator( + params.is_new_construction ) return event_filters - def _build_owner_filters(self, **kwargs: dict) -> dict[str, Any]: - """Build owner filters from keyword arguments.""" + def _build_owner_filters(self, params: PropertyV2RetrieveParams) -> dict[str, Any]: + """Build owner filters from validated Pydantic schema.""" owner_filters = {} # Handle owner names - if kwargs.get("owner_name"): - owner_filters["owner_name"] = [name.upper() for name in kwargs["owner_name"]] + if params.owner_name: + owner_filters["owner_name"] = [owner_name.upper() for owner_name in params.owner_name] # Handle boolean parameters - bool_params = ["is_current_owner", "is_investor_owned", "is_owner_occupied"] - - for param_name in bool_params: - if param_name in kwargs: - owner_filters = Validators.validate_input_bool_param( - param=kwargs[param_name], - param_name=param_name, - params_dict=owner_filters, - ) + if params.is_current_owner is not None: + owner_filters["is_current_owner"] = self.simple_bool_validator(params.is_current_owner) + if params.is_investor_owned is not None: + owner_filters["is_investor_owned"] = self.simple_bool_validator( + params.is_investor_owned + ) + if params.is_owner_occupied is not None: + owner_filters["is_owner_occupied"] = self.simple_bool_validator( + params.is_owner_occupied + ) return owner_filters @@ -288,6 +315,16 @@ def _validate_limit(self, limit: int | None) -> int: return limit + def _build_param_categories( + self, params: PropertyV2RetrieveParams + ) -> PropertyV2RetrieveParamCategories: + """Build parameter categories from validated Pydantic schema.""" + return PropertyV2RetrieveParamCategories( + property_filters=self._build_property_filters(params), + event_filters=self._build_event_filters(params), + owner_filters=self._build_owner_filters(params), + ) + def retrieve( self, parcl_ids: list[int] | None = None, @@ -296,8 +333,8 @@ def retrieve( property_types: list[str] | None = None, min_beds: int | None = None, max_beds: int | None = None, - min_baths: int | None = None, - max_baths: int | None = None, + min_baths: float | None = None, + max_baths: float | None = None, min_sqft: int | None = None, max_sqft: int | None = None, min_year_built: int | None = None, @@ -317,8 +354,10 @@ def retrieve( owner_name: list[str] | None = None, is_investor_owned: bool | None = None, is_owner_occupied: bool | None = None, + current_on_market_flag: bool | None = None, + current_on_market_rental_flag: bool | None = None, limit: int | None = None, - params: Mapping[str, Any] | None = {}, + params: Mapping[str, Any] | None = None, ) -> tuple[pd.DataFrame, dict[str, Any]]: """ Retrieve property data based on search criteria and filters. @@ -352,41 +391,71 @@ def retrieve( owner_name: List of owner names to filter by. is_investor_owned: Whether to filter by investor owned. is_owner_occupied: Whether to filter by owner occupied. + current_on_market_flag: Whether to filter by current_on_market flag. + current_on_market_rental_flag: Whether to filter by current_on_market_rental flag. + limit: Number of results to return. params: Additional parameters to pass to the request. Returns: - A pandas DataFrame containing the property data. + A tuple containing (pandas DataFrame, metadata dictionary). """ print("Processing property search request...") - # Build search criteria - data = self._build_search_criteria( + # Validate and process input parameters using Pydantic schema + input_params = PropertyV2RetrieveParams( parcl_ids=parcl_ids, parcl_property_ids=parcl_property_ids, geo_coordinates=geo_coordinates, + property_types=property_types, + min_beds=min_beds, + max_beds=max_beds, + min_baths=min_baths, + max_baths=max_baths, + min_sqft=min_sqft, + max_sqft=max_sqft, + min_year_built=min_year_built, + max_year_built=max_year_built, + include_property_details=include_property_details, + min_record_added_date=min_record_added_date, + max_record_added_date=max_record_added_date, + event_names=event_names, + min_event_date=min_event_date, + max_event_date=max_event_date, + min_price=min_price, + max_price=max_price, + is_new_construction=is_new_construction, + min_record_updated_date=min_record_updated_date, + max_record_updated_date=max_record_updated_date, + is_current_owner=is_current_owner, + owner_name=owner_name, + is_investor_owned=is_investor_owned, + is_owner_occupied=is_owner_occupied, + current_on_market_flag=current_on_market_flag, + current_on_market_rental_flag=current_on_market_rental_flag, + limit=limit, + params=params or {}, ) - # Build filters using all provided parameters - kwargs = locals() - # Remove self and parameters that aren't filter-related - for key in [ - "self", - "parcl_ids", - "parcl_property_ids", - "params", - "data", - "geo_coordinates", - ]: - kwargs.pop(key, None) - - # Build and add filters to data - data["property_filters"] = self._build_property_filters(**kwargs) - data["event_filters"] = self._build_event_filters(**kwargs) - data["owner_filters"] = self._build_owner_filters(**kwargs) - - params["limit"] = self._validate_limit(limit) + # Build search criteria + data = self._build_search_criteria( + parcl_ids=input_params.parcl_ids, + parcl_property_ids=input_params.parcl_property_ids, + geo_coordinates=( + input_params.geo_coordinates.model_dump() if input_params.geo_coordinates else None + ), + ) + + # Build parameter categories using validated parameters + param_categories = self._build_param_categories(input_params) + + # Update data with categories + data.update(param_categories.model_dump(exclude_none=True)) + + # Set limit + request_params = input_params.params.copy() + request_params["limit"] = self._validate_limit(input_params.limit) # Make request with params - results = self._fetch_post(params=params, data=data) + results = self._fetch_post(params=request_params, data=data) # Get metadata from results metadata = self._get_metadata(results) diff --git a/parcllabs/services/validators.py b/parcllabs/services/validators.py index 2deb960..fd57365 100644 --- a/parcllabs/services/validators.py +++ b/parcllabs/services/validators.py @@ -56,6 +56,13 @@ def validate_input_bool_param( params_dict[param_name] = "true" if param else "false" return params_dict + @staticmethod + def validate_input_bool_param_simple(param: bool | None) -> str: + if not isinstance(param, bool): + raise TypeError(f"Expected boolean. Received: {param}") + + return "true" if param else "false" + @staticmethod def validate_us_zip_code(zip_code: str) -> str: """ diff --git a/pyproject.toml b/pyproject.toml index f063380..e2ccbdc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,6 +29,7 @@ classifiers = [ dependencies = [ "numpy", "pandas", + "pydantic>=2.0.0", "requests", ] diff --git a/requirements.txt b/requirements.txt index c60c720..f805167 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ pandas>=1.2 requests>=2.25 numpy>=2.1.0 +pydantic>=2.0.0 diff --git a/tests/integration/test_api.py b/tests/integration/test_api.py index dc3547c..27df589 100644 --- a/tests/integration/test_api.py +++ b/tests/integration/test_api.py @@ -89,7 +89,7 @@ def test_multiple_get_requests_with_bad_parcl_id(client: ParclLabsClient) -> Non start_date = "2010-01-01" end_date = "2023-12-31" limit = 1000 - test_pricefeed_markets = PRICEFEED_MARKETS[0:3] + [123] + test_pricefeed_markets = [*PRICEFEED_MARKETS[0:3], 123] results = client.price_feed.price_feed.retrieve( parcl_ids=test_pricefeed_markets, @@ -107,7 +107,7 @@ def test_multiple_get_requests_with_bad_parcl_id_and_auto_pagination( start_date = "2010-01-01" end_date = "2023-12-31" days = (pd.to_datetime(end_date) - pd.to_datetime(start_date)).days + 1 - test_pricefeed_markets = PRICEFEED_MARKETS[0:3] + [123] + test_pricefeed_markets = [*PRICEFEED_MARKETS[0:3], 123] results = client.price_feed.price_feed.retrieve( parcl_ids=test_pricefeed_markets, diff --git a/tests/test_property_v2.py b/tests/test_property_v2.py index 4488395..7c70708 100644 --- a/tests/test_property_v2.py +++ b/tests/test_property_v2.py @@ -3,6 +3,7 @@ import pytest from parcllabs.enums import RequestLimits +from parcllabs.schemas.schemas import GeoCoordinates, PropertyV2RetrieveParams from parcllabs.services.properties.property_v2 import PropertyV2Service @@ -68,12 +69,13 @@ def test_build_search_criteria(property_v2_service: PropertyV2Service) -> None: } -def test_build_property_filters(property_v2_service: PropertyV2Service) -> None: - filters = property_v2_service._build_property_filters( +def test_build_property_filters_from_schema(property_v2_service: PropertyV2Service) -> None: + """Test building property filters from Pydantic schema.""" + params = PropertyV2RetrieveParams( min_beds=2, max_beds=4, min_baths=1.5, - max_baths=3, + max_baths=3.0, min_sqft=1000, max_sqft=2000, min_year_built=1980, @@ -84,11 +86,13 @@ def test_build_property_filters(property_v2_service: PropertyV2Service) -> None: max_record_added_date="2023-12-31", ) + filters = property_v2_service._build_property_filters(params) + assert filters == { "min_beds": 2, "max_beds": 4, "min_baths": 1.5, - "max_baths": 3, + "max_baths": 3.0, "min_sqft": 1000, "max_sqft": 2000, "min_year_built": 1980, @@ -100,8 +104,9 @@ def test_build_property_filters(property_v2_service: PropertyV2Service) -> None: } -def test_build_event_filters(property_v2_service: PropertyV2Service) -> None: - filters = property_v2_service._build_event_filters( +def test_build_event_filters_from_schema(property_v2_service: PropertyV2Service) -> None: + """Test building event filters from Pydantic schema.""" + params = PropertyV2RetrieveParams( event_names=["LISTING", "SALE"], min_event_date="2023-01-01", max_event_date="2023-12-31", @@ -112,6 +117,8 @@ def test_build_event_filters(property_v2_service: PropertyV2Service) -> None: max_record_updated_date="2023-12-31", ) + filters = property_v2_service._build_event_filters(params) + assert filters == { "event_names": ["LISTING", "SALE"], "min_event_date": "2023-01-01", @@ -124,14 +131,17 @@ def test_build_event_filters(property_v2_service: PropertyV2Service) -> None: } -def test_build_owner_filters(property_v2_service: PropertyV2Service) -> None: - filters = property_v2_service._build_owner_filters( +def test_build_owner_filters_from_schema(property_v2_service: PropertyV2Service) -> None: + """Test building owner filters from Pydantic schema.""" + params = PropertyV2RetrieveParams( owner_name=["Blackstone", "Amherst"], is_current_owner=True, is_investor_owned=False, is_owner_occupied=True, ) + filters = property_v2_service._build_owner_filters(params) + assert filters == { "owner_name": ["BLACKSTONE", "AMHERST"], "is_current_owner": "true", @@ -140,6 +150,62 @@ def test_build_owner_filters(property_v2_service: PropertyV2Service) -> None: } +def test_schema_validation() -> None: + """Test Pydantic schema validation.""" + # Test valid parameters + params = PropertyV2RetrieveParams( + parcl_ids=[123, 456], + property_types=["SINGLE_FAMILY"], + min_beds=2, + max_beds=4, + min_price=500000, + max_price=1000000, + ) + assert params.parcl_ids == [123, 456] + assert params.property_types == ["SINGLE_FAMILY"] + assert params.min_beds == 2 + assert params.max_beds == 4 + + # Test geo coordinates + geo = GeoCoordinates(latitude=40.7128, longitude=-74.0060, radius=10.0) + params_with_geo = PropertyV2RetrieveParams(geo_coordinates=geo) + assert params_with_geo.geo_coordinates == geo + + +def test_schema_validation_errors() -> None: + """Test Pydantic schema validation errors.""" + # Test invalid property type + with pytest.raises(ValueError, match="Invalid property type"): + PropertyV2RetrieveParams(property_types=["INVALID_TYPE"]) + + # Test invalid geo coordinates + with pytest.raises(ValueError, match="Input should be less than or equal to 90"): + GeoCoordinates(latitude=100, longitude=-74.0060, radius=10.0) + + # Test invalid date format + with pytest.raises(ValueError, match="Date must be in YYYY-MM-DD format"): + PropertyV2RetrieveParams(min_event_date="2023/01/01") + + # Test invalid range (min > max) + with pytest.raises(ValueError, match="max_beds cannot be less than min_beds"): + PropertyV2RetrieveParams(min_beds=5, max_beds=3) + + # Test invalid price range + with pytest.raises(ValueError, match="max_price cannot be less than min_price"): + PropertyV2RetrieveParams(min_price=1000000, max_price=500000) + + +def test_schema_with_none_values() -> None: + """Test schema handles None values correctly.""" + params = PropertyV2RetrieveParams() + assert params.parcl_ids is None + assert params.property_types is None + assert params.min_beds is None + assert params.max_beds is None + assert params.geo_coordinates is None + assert params.params == {} + + def test_validate_limit(property_v2_service: PropertyV2Service) -> None: assert property_v2_service._validate_limit(limit=None) == RequestLimits.PROPERTY_V2_MAX.value assert property_v2_service._validate_limit(limit=None) == RequestLimits.PROPERTY_V2_MAX.value @@ -252,3 +318,59 @@ def test_retrieve( assert data["property_filters"]["min_beds"] == 2 assert data["property_filters"]["max_beds"] == 4 assert data["event_filters"]["event_names"] == ["LISTING"] + + +@patch.object(PropertyV2Service, "_fetch_post") +def test_retrieve_with_geo_coordinates( + mock_fetch_post: Mock, property_v2_service: PropertyV2Service, mock_response: Mock +) -> None: + """Test retrieve method with geo coordinates.""" + mock_fetch_post.return_value = [mock_response.json()] + + df, metadata = property_v2_service.retrieve( + geo_coordinates={"latitude": 40.7128, "longitude": -74.0060, "radius": 10.0}, + property_types=["CONDO"], + min_price=500000, + max_price=1000000, + ) + + # check that the dataframe has the expected data + assert len(df) == 1 + assert df.iloc[0]["parcl_id"] == 123 + + # check that the correct data was passed to _fetch_post + call_args = mock_fetch_post.call_args[1] + data = call_args["data"] + assert data["geo_coordinates"] == {"latitude": 40.7128, "longitude": -74.0060, "radius": 10.0} + assert data["property_filters"]["property_types"] == ["CONDO"] + assert data["event_filters"]["min_price"] == 500000 + assert data["event_filters"]["max_price"] == 1000000 + + +@patch.object(PropertyV2Service, "_fetch_post") +def test_retrieve_with_schema_validation_errors( + mock_fetch_post: Mock, # noqa: ARG001 + property_v2_service: PropertyV2Service, +) -> None: + """Test that retrieve method properly validates input using schema.""" + # This should raise a validation error due to invalid property type + with pytest.raises(ValueError, match="Invalid property type"): + property_v2_service.retrieve( + parcl_ids=[123], + property_types=["INVALID_TYPE"], + ) + + # This should raise a validation error due to invalid range + with pytest.raises(ValueError, match="max_beds cannot be less than min_beds"): + property_v2_service.retrieve( + parcl_ids=[123], + min_beds=5, + max_beds=3, + ) + + # This should raise a validation error due to invalid date format + with pytest.raises(ValueError, match="Date must be in YYYY-MM-DD format"): + property_v2_service.retrieve( + parcl_ids=[123], + min_event_date="2023/01/01", + )