diff --git a/Pipfile b/Pipfile index bb442791d89..0d4bc436852 100644 --- a/Pipfile +++ b/Pipfile @@ -40,6 +40,9 @@ drf-spectacular = "*" daphne = "*" drf-link-header-pagination = "*" networkx = "*" # Avoid its dependencies (SciPy) +stripe = "*" +requests = "*" +cryptography = "*" [dev-packages] pre-commit = "*" diff --git a/Pipfile.lock b/Pipfile.lock index 828f7e99529..09ff40642c7 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "2c6c7279511a59524494d4ae59fb78ac13efe891c03b28c26ea8c01ce6975a4d" + "sha256": "17ceb84c53cb204b70f912fe493e64c31e97c64003652855432729dafdde42d9" }, "pipfile-spec": 6, "requires": { @@ -35,25 +35,27 @@ }, "attrs": { "hashes": [ - "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30", - "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1" + "sha256:427318ce031701fea540783410126f03899a97ffc6f61596ad581ac2e40e3bc3", + "sha256:75d7cefc7fb576747b2c81b4442d4d4a1ce0900973527c011d1030fd3bf4af1b" ], - "markers": "python_version >= '3.7'", - "version": "==23.2.0" + "markers": "python_version >= '3.8'", + "version": "==25.3.0" }, "autobahn": { "hashes": [ - "sha256:ec9421c52a2103364d1ef0468036e6019ee84f71721e86b36fe19ad6966c1181" + "sha256:a2d71ef1b0cf780b6d11f8b205fd2c7749765e65795f2ea7d823796642ee92c9", + "sha256:c56a2abe7ac78abbfb778c02892d673a4de58fd004d088cd7ab297db25918e81" ], "markers": "python_version >= '3.9'", - "version": "==23.6.2" + "version": "==24.4.2" }, "automat": { "hashes": [ - "sha256:c3164f8742b9dc440f3682482d32aaff7bb53f71740dd018533f9de286b64180", - "sha256:e56beb84edad19dcc11d30e8d9b895f75deeb5ef5e96b84a467066b3b84bb04e" + "sha256:0017591a5477066e90d26b0e696ddc143baafd87b588cfac8100bc6be9634de0", + "sha256:04e9bce696a8d5671ee698005af6e5a9fa15354140a87f4870744604dcdd3ba1" ], - "version": "==22.10.0" + "markers": "python_version >= '3.9'", + "version": "==25.4.16" }, "backcall": { "hashes": [ @@ -64,95 +66,208 @@ }, "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" }, "certifi": { "hashes": [ - "sha256:0569859f95fc761b18b45ef421b1290a0f65f147e92a1e5eb3e635f9a5e4e66f", - "sha256:dc383c07b76109f368f6106eee2b593b04a011ea4d55f652c6ca24a754d1cdd1" + "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6", + "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3" ], "markers": "python_version >= '3.6'", - "version": "==2024.2.2" + "version": "==2025.4.26" }, "cffi": { "hashes": [ - "sha256:0c9ef6ff37e974b73c25eecc13952c55bceed9112be2d9d938ded8e856138bcc", - "sha256:131fd094d1065b19540c3d72594260f118b231090295d8c34e19a7bbcf2e860a", - "sha256:1b8ebc27c014c59692bb2664c7d13ce7a6e9a629be20e54e7271fa696ff2b417", - "sha256:2c56b361916f390cd758a57f2e16233eb4f64bcbeee88a4881ea90fca14dc6ab", - "sha256:2d92b25dbf6cae33f65005baf472d2c245c050b1ce709cc4588cdcdd5495b520", - "sha256:31d13b0f99e0836b7ff893d37af07366ebc90b678b6664c955b54561fc36ef36", - "sha256:32c68ef735dbe5857c810328cb2481e24722a59a2003018885514d4c09af9743", - "sha256:3686dffb02459559c74dd3d81748269ffb0eb027c39a6fc99502de37d501faa8", - "sha256:582215a0e9adbe0e379761260553ba11c58943e4bbe9c36430c4ca6ac74b15ed", - "sha256:5b50bf3f55561dac5438f8e70bfcdfd74543fd60df5fa5f62d94e5867deca684", - "sha256:5bf44d66cdf9e893637896c7faa22298baebcd18d1ddb6d2626a6e39793a1d56", - "sha256:6602bc8dc6f3a9e02b6c22c4fc1e47aa50f8f8e6d3f78a5e16ac33ef5fefa324", - "sha256:673739cb539f8cdaa07d92d02efa93c9ccf87e345b9a0b556e3ecc666718468d", - "sha256:68678abf380b42ce21a5f2abde8efee05c114c2fdb2e9eef2efdb0257fba1235", - "sha256:68e7c44931cc171c54ccb702482e9fc723192e88d25a0e133edd7aff8fcd1f6e", - "sha256:6b3d6606d369fc1da4fd8c357d026317fbb9c9b75d36dc16e90e84c26854b088", - "sha256:748dcd1e3d3d7cd5443ef03ce8685043294ad6bd7c02a38d1bd367cfd968e000", - "sha256:7651c50c8c5ef7bdb41108b7b8c5a83013bfaa8a935590c5d74627c047a583c7", - "sha256:7b78010e7b97fef4bee1e896df8a4bbb6712b7f05b7ef630f9d1da00f6444d2e", - "sha256:7e61e3e4fa664a8588aa25c883eab612a188c725755afff6289454d6362b9673", - "sha256:80876338e19c951fdfed6198e70bc88f1c9758b94578d5a7c4c91a87af3cf31c", - "sha256:8895613bcc094d4a1b2dbe179d88d7fb4a15cee43c052e8885783fac397d91fe", - "sha256:88e2b3c14bdb32e440be531ade29d3c50a1a59cd4e51b1dd8b0865c54ea5d2e2", - "sha256:8f8e709127c6c77446a8c0a8c8bf3c8ee706a06cd44b1e827c3e6a2ee6b8c098", - "sha256:9cb4a35b3642fc5c005a6755a5d17c6c8b6bcb6981baf81cea8bfbc8903e8ba8", - "sha256:9f90389693731ff1f659e55c7d1640e2ec43ff725cc61b04b2f9c6d8d017df6a", - "sha256:a09582f178759ee8128d9270cd1344154fd473bb77d94ce0aeb2a93ebf0feaf0", - "sha256:a6a14b17d7e17fa0d207ac08642c8820f84f25ce17a442fd15e27ea18d67c59b", - "sha256:a72e8961a86d19bdb45851d8f1f08b041ea37d2bd8d4fd19903bc3083d80c896", - "sha256:abd808f9c129ba2beda4cfc53bde801e5bcf9d6e0f22f095e45327c038bfe68e", - "sha256:ac0f5edd2360eea2f1daa9e26a41db02dd4b0451b48f7c318e217ee092a213e9", - "sha256:b29ebffcf550f9da55bec9e02ad430c992a87e5f512cd63388abb76f1036d8d2", - "sha256:b2ca4e77f9f47c55c194982e10f058db063937845bb2b7a86c84a6cfe0aefa8b", - "sha256:b7be2d771cdba2942e13215c4e340bfd76398e9227ad10402a8767ab1865d2e6", - "sha256:b84834d0cf97e7d27dd5b7f3aca7b6e9263c56308ab9dc8aae9784abb774d404", - "sha256:b86851a328eedc692acf81fb05444bdf1891747c25af7529e39ddafaf68a4f3f", - "sha256:bcb3ef43e58665bbda2fb198698fcae6776483e0c4a631aa5647806c25e02cc0", - "sha256:c0f31130ebc2d37cdd8e44605fb5fa7ad59049298b3f745c74fa74c62fbfcfc4", - "sha256:c6a164aa47843fb1b01e941d385aab7215563bb8816d80ff3a363a9f8448a8dc", - "sha256:d8a9d3ebe49f084ad71f9269834ceccbf398253c9fac910c4fd7053ff1386936", - "sha256:db8e577c19c0fda0beb7e0d4e09e0ba74b1e4c092e0e40bfa12fe05b6f6d75ba", - "sha256:dc9b18bf40cc75f66f40a7379f6a9513244fe33c0e8aa72e2d56b0196a7ef872", - "sha256:e09f3ff613345df5e8c3667da1d918f9149bd623cd9070c983c013792a9a62eb", - "sha256:e4108df7fe9b707191e55f33efbcb2d81928e10cea45527879a4749cbe472614", - "sha256:e6024675e67af929088fda399b2094574609396b1decb609c55fa58b028a32a1", - "sha256:e70f54f1796669ef691ca07d046cd81a29cb4deb1e5f942003f401c0c4a2695d", - "sha256:e715596e683d2ce000574bae5d07bd522c781a822866c20495e52520564f0969", - "sha256:e760191dd42581e023a68b758769e2da259b5d52e3103c6060ddc02c9edb8d7b", - "sha256:ed86a35631f7bfbb28e108dd96773b9d5a6ce4811cf6ea468bb6a359b256b1e4", - "sha256:ee07e47c12890ef248766a6e55bd38ebfb2bb8edd4142d56db91b21ea68b7627", - "sha256:fa3a0128b152627161ce47201262d3140edb5a5c3da88d73a1b790a959126956", - "sha256:fcc8eb6d5902bb1cf6dc4f187ee3ea80a1eba0a89aba40a5cb20a5087d961357" + "sha256:045d61c734659cc045141be4bae381a41d89b741f795af1dd018bfb532fd0df8", + "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2", + "sha256:0e2b1fac190ae3ebfe37b979cc1ce69c81f4e4fe5746bb401dca63a9062cdaf1", + "sha256:0f048dcf80db46f0098ccac01132761580d28e28bc0f78ae0d58048063317e15", + "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", + "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", + "sha256:1d599671f396c4723d016dbddb72fe8e0397082b0a77a4fab8028923bec050e8", + "sha256:28b16024becceed8c6dfbc75629e27788d8a3f9030691a1dbf9821a128b22c36", + "sha256:2bb1a08b8008b281856e5971307cc386a8e9c5b625ac297e853d36da6efe9c17", + "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf", + "sha256:31000ec67d4221a71bd3f67df918b1f88f676f1c3b535a7eb473255fdc0b83fc", + "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", + "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", + "sha256:45398b671ac6d70e67da8e4224a065cec6a93541bb7aebe1b198a61b58c7b702", + "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1", + "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", + "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", + "sha256:5da5719280082ac6bd9aa7becb3938dc9f9cbd57fac7d2871717b1feb0902ab6", + "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d", + "sha256:636062ea65bd0195bc012fea9321aca499c0504409f413dc88af450b57ffd03b", + "sha256:6883e737d7d9e4899a8a695e00ec36bd4e5e4f18fabe0aca0efe0a4b44cdb13e", + "sha256:6b8b4a92e1c65048ff98cfe1f735ef8f1ceb72e3d5f0c25fdb12087a23da22be", + "sha256:6f17be4345073b0a7b8ea599688f692ac3ef23ce28e5df79c04de519dbc4912c", + "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", + "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", + "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", + "sha256:7596d6620d3fa590f677e9ee430df2958d2d6d6de2feeae5b20e82c00b76fbf8", + "sha256:78122be759c3f8a014ce010908ae03364d00a1f81ab5c7f4a7a5120607ea56e1", + "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", + "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655", + "sha256:8f2cdc858323644ab277e9bb925ad72ae0e67f69e804f4898c070998d50b1a67", + "sha256:9755e4345d1ec879e3849e62222a18c7174d65a6a92d5b346b1863912168b595", + "sha256:98e3969bcff97cae1b2def8ba499ea3d6f31ddfdb7635374834cf89a1a08ecf0", + "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", + "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41", + "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6", + "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401", + "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6", + "sha256:ad9413ccdeda48c5afdae7e4fa2192157e991ff761e7ab8fdd8926f40b160cc3", + "sha256:b2ab587605f4ba0bf81dc0cb08a41bd1c0a5906bd59243d56bad7668a6fc6c16", + "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", + "sha256:c03e868a0b3bc35839ba98e74211ed2b05d2119be4e8a0f224fba9384f1fe02e", + "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", + "sha256:c7eac2ef9b63c79431bc4b25f1cd649d7f061a28808cbc6c47b534bd789ef964", + "sha256:c9c3d058ebabb74db66e431095118094d06abf53284d9c81f27300d0e0d8bc7c", + "sha256:ca74b8dbe6e8e8263c0ffd60277de77dcee6c837a3d0881d8c1ead7268c9e576", + "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0", + "sha256:cdf5ce3acdfd1661132f2a9c19cac174758dc2352bfe37d98aa7512c6b7178b3", + "sha256:d016c76bdd850f3c626af19b0542c9677ba156e4ee4fccfdd7848803533ef662", + "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", + "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", + "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", + "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", + "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f", + "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", + "sha256:df8b1c11f177bc2313ec4b2d46baec87a5f3e71fc8b45dab2ee7cae86d9aba14", + "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", + "sha256:e221cf152cff04059d011ee126477f0d9588303eb57e88923578ace7baad17f9", + "sha256:e31ae45bc2e29f6b2abd0de1cc3b9d5205aa847cafaecb8af1476a609a2f6eb7", + "sha256:edae79245293e15384b51f88b00613ba9f7198016a5948b5dddf4917d4d26382", + "sha256:f1e22e8c4419538cb197e4dd60acc919d7696e5ef98ee4da4e01d3f8cfa4cc5a", + "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e", + "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", + "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4", + "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", + "sha256:f7f5baafcc48261359e14bcd6d9bff6d4b28d9103847c9e136694cb0501aef87", + "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b" ], "markers": "platform_python_implementation != 'PyPy'", - "version": "==1.16.0" + "version": "==1.17.1" }, "channels": { "hashes": [ - "sha256:a3c4419307f582c3f71d67bfb6eff748ae819c2f360b9b141694d84f242baa48", - "sha256:e0ed375719f5c1851861f05ed4ce78b0166f9245ca0ecd836cb77d4bb531489d" + "sha256:8d7208e48ab8fdb972aaeae8311ce920637d97656ffc7ae5eca4f93f84bcd9a0", + "sha256:ff36a6e1576cacf40bcdc615fa7aece7a709fc4fdd2dc87f2971f4061ffdaa81" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==4.1.0" + "version": "==4.2.2" }, "channels-redis": { "hashes": [ - "sha256:01c26c4d5d3a203f104bba9e5585c0305a70df390d21792386586068162027fd", - "sha256:2c5b944a39bd984b72aa8005a3ae11637bf29b5092adeb91c9aad4ab819a8ac4" + "sha256:2ca33105b3a04b5a327a9c47dd762b546f30b76a0cd3f3f593a23d91d346b6f4", + "sha256:8375e81493e684792efe6e6eca60ef3d7782ef76c6664057d2e5c31e80d636dd" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==4.2.0" + "version": "==4.2.1" + }, + "charset-normalizer": { + "hashes": [ + "sha256:005fa3432484527f9732ebd315da8da8001593e2cf46a3d817669f062c3d9ed4", + "sha256:046595208aae0120559a67693ecc65dd75d46f7bf687f159127046628178dc45", + "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7", + "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0", + "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7", + "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d", + "sha256:1b1bde144d98e446b056ef98e59c256e9294f6b74d7af6846bf5ffdafd687a7d", + "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", + "sha256:1cad5f45b3146325bb38d6855642f6fd609c3f7cad4dbaf75549bf3b904d3184", + "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db", + "sha256:24498ba8ed6c2e0b56d4acbf83f2d989720a93b41d712ebd4f4979660db4417b", + "sha256:25a23ea5c7edc53e0f29bae2c44fcb5a1aa10591aae107f2a2b2583a9c5cbc64", + "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", + "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8", + "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", + "sha256:36b31da18b8890a76ec181c3cf44326bf2c48e36d393ca1b72b3f484113ea344", + "sha256:3c21d4fca343c805a52c0c78edc01e3477f6dd1ad7c47653241cf2a206d4fc58", + "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", + "sha256:43e0933a0eff183ee85833f341ec567c0980dae57c464d8a508e1b2ceb336471", + "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", + "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a", + "sha256:50bf98d5e563b83cc29471fa114366e6806bc06bc7a25fd59641e41445327836", + "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e", + "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", + "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c", + "sha256:6333b3aa5a12c26b2a4d4e7335a28f1475e0e5e17d69d55141ee3cab736f66d1", + "sha256:65c981bdbd3f57670af8b59777cbfae75364b483fa8a9f420f08094531d54a01", + "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366", + "sha256:6a0289e4589e8bdfef02a80478f1dfcb14f0ab696b5a00e1f4b8a14a307a3c58", + "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5", + "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", + "sha256:6fc1f5b51fa4cecaa18f2bd7a003f3dd039dd615cd69a2afd6d3b19aed6775f2", + "sha256:70f7172939fdf8790425ba31915bfbe8335030f05b9913d7ae00a87d4395620a", + "sha256:721c76e84fe669be19c5791da68232ca2e05ba5185575086e384352e2c309597", + "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b", + "sha256:75d10d37a47afee94919c4fab4c22b9bc2a8bf7d4f46f87363bcf0573f3ff4f5", + "sha256:76af085e67e56c8816c3ccf256ebd136def2ed9654525348cfa744b6802b69eb", + "sha256:770cab594ecf99ae64c236bc9ee3439c3f46be49796e265ce0cc8bc17b10294f", + "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0", + "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941", + "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", + "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86", + "sha256:8272b73e1c5603666618805fe821edba66892e2870058c94c53147602eab29c7", + "sha256:82d8fd25b7f4675d0c47cf95b594d4e7b158aca33b76aa63d07186e13c0e0ab7", + "sha256:844da2b5728b5ce0e32d863af26f32b5ce61bc4273a9c720a9f3aa9df73b1455", + "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6", + "sha256:915f3849a011c1f593ab99092f3cecfcb4d65d8feb4a64cf1bf2d22074dc0ec4", + "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", + "sha256:982bb1e8b4ffda883b3d0a521e23abcd6fd17418f6d2c4118d257a10199c0ce3", + "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", + "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6", + "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981", + "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c", + "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", + "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645", + "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", + "sha256:aaf27faa992bfee0264dc1f03f4c75e9fcdda66a519db6b957a3f826e285cf12", + "sha256:b2680962a4848b3c4f155dc2ee64505a9c57186d0d56b43123b17ca3de18f0fa", + "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd", + "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef", + "sha256:b3daeac64d5b371dea99714f08ffc2c208522ec6b06fbc7866a450dd446f5c0f", + "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2", + "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d", + "sha256:c72fbbe68c6f32f251bdc08b8611c7b3060612236e960ef848e0a517ddbe76c5", + "sha256:c9e36a97bee9b86ef9a1cf7bb96747eb7a15c2f22bdb5b516434b00f2a599f02", + "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3", + "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd", + "sha256:d11b54acf878eef558599658b0ffca78138c8c3655cf4f3a4a673c437e67732e", + "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214", + "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd", + "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a", + "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c", + "sha256:dc7039885fa1baf9be153a0626e337aa7ec8bf96b0128605fb0d77788ddc1681", + "sha256:dccab8d5fa1ef9bfba0590ecf4d46df048d18ffe3eec01eeb73a42e0d9e7a8ba", + "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f", + "sha256:e45ba65510e2647721e35323d6ef54c7974959f6081b58d4ef5d87c60c84919a", + "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28", + "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", + "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82", + "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a", + "sha256:e8323a9b031aa0393768b87f04b4164a40037fb2a3c11ac06a03ffecd3618027", + "sha256:e92fca20c46e9f5e1bb485887d074918b13543b1c2a1185e69bb8d17ab6236a7", + "sha256:eb30abc20df9ab0814b5a2524f23d75dcf83cde762c161917a2b4b7b55b1e518", + "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", + "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", + "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9", + "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544", + "sha256:f4074c5a429281bf056ddd4c5d3b740ebca4d43ffffe2ef4bf4d2d05114299da", + "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509", + "sha256:fb707f3e15060adf5b7ada797624a6c6e0138e2a26baa089df64c68ee98e040f", + "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a", + "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f" + ], + "markers": "python_version >= '3.7'", + "version": "==3.4.2" }, "constantly": { "hashes": [ @@ -164,58 +279,64 @@ }, "cryptography": { "hashes": [ - "sha256:00c0faa5b021457848d031ecff041262211cc1e2bce5f6e6e6c8108018f6b44a", - "sha256:073104df012fc815eed976cd7d0a386c8725d0d0947cf9c37f6c36a6c20feb1b", - "sha256:076c92b08dd1ab88108bc84545187e10d3693a9299c593f98c4ea195a0b0ead7", - "sha256:089aeb297ff89615934b22c7631448598495ffd775b7d540a55cfee35a677bf4", - "sha256:3b750279f3e7715df6f68050707a0cee7cbe81ba2eeb2f21d081bd205885ffed", - "sha256:43e521f21c2458038d72e8cdfd4d4d9f1d00906a7b6636c4272e35f650d1699b", - "sha256:4bdb39ecbf05626e4bfa1efd773bb10346af297af14fb3f4c7cb91a1d2f34a46", - "sha256:5967e3632f42b0c0f9dc2c9da88c79eabdda317860b246d1fbbde4a8bbbc3b44", - "sha256:65d529c31bd65d54ce6b926a01e1b66eacf770b7e87c0622516a840e400ec732", - "sha256:6981acac509cc9415344cb5bfea8130096ea6ebcc917e75503143a1e9e829160", - "sha256:81dbe47e28b703bc4711ac74a64ef8b758a0cf056ce81d08e39116ab4bc126fa", - "sha256:8b90c57b3cd6128e0863b894ce77bd36fcb5f430bf2377bc3678c2f56e232316", - "sha256:9184aff0856261ecb566a3eb26a05dfe13a292c85ce5c59b04e4aa09e5814187", - "sha256:945a43ebf036dd4b43ebfbbd6b0f2db29ad3d39df824fb77476ca5777a9dde33", - "sha256:97eeacae9aa526ddafe68b9202a535f581e21d78f16688a84c8dcc063618e121", - "sha256:9f1a3bc2747166b0643b00e0b56cd9b661afc9d5ff963acaac7a9c7b2b1ef638", - "sha256:9ff75b88a4d273c06d968ad535e6cb6a039dd32db54fe36f05ed62ac3ef64a44", - "sha256:aeb6f56b004e898df5530fa873e598ec78eb338ba35f6fa1449970800b1d97c2", - "sha256:b16b90605c62bcb3aa7755d62cf5e746828cfc3f965a65211849e00c46f8348d", - "sha256:b99831397fdc6e6e0aa088b060c278c6e635d25c0d4d14bdf045bf81792fda0a", - "sha256:bc954251edcd8a952eeaec8ae989fec7fe48109ab343138d537b7ea5bb41071a", - "sha256:c05230d8aaaa6b8ab3ab41394dc06eb3d916131df1c9dcb4c94e8f041f704b74", - "sha256:d16a310c770cc49908c500c2ceb011f2840674101a587d39fa3ea828915b7e83", - "sha256:d93080d2b01b292e7ee4d247bf93ed802b0100f5baa3fa5fd6d374716fa480d4", - "sha256:e1f5f15c5ddadf6ee4d1d624a2ae940f14bd74536230b0056ccb28bb6248e42a", - "sha256:e3442601d276bd9e961d618b799761b4e5d892f938e8a4fe1efbe2752be90455", - "sha256:e85f433230add2aa26b66d018e21134000067d210c9c68ef7544ba65fc52e3eb", - "sha256:eecca86813c6a923cabff284b82ff4d73d9e91241dc176250192c3a9b9902a54", - "sha256:f1e933b238978ccfa77b1fee0a297b3c04983f4cb84ae1c33b0ea4ae08266cc9", - "sha256:f4cece02478d73dacd52be57a521d168af64ae03d2a567c0c4eb6f189c3b9d79", - "sha256:f567a82b7c2b99257cca2a1c902c1b129787278ff67148f188784245c7ed5495", - "sha256:f987a244dfb0333fbd74a691c36000a2569eaf7c7cc2ac838f85f59f0588ddc9" + "sha256:0339a692de47084969500ee455e42c58e449461e0ec845a34a6a9b9bf7df7fb8", + "sha256:03dbff8411206713185b8cebe31bc5c0eb544799a50c09035733716b386e61a4", + "sha256:06509dc70dd71fa56eaa138336244e2fbaf2ac164fc9b5e66828fccfd2b680d6", + "sha256:0cf13c77d710131d33e63626bd55ae7c0efb701ebdc2b3a7952b9b23a0412862", + "sha256:23b9c3ea30c3ed4db59e7b9619272e94891f8a3a5591d0b656a7582631ccf750", + "sha256:25eb4d4d3e54595dc8adebc6bbd5623588991d86591a78c2548ffb64797341e2", + "sha256:2882338b2a6e0bd337052e8b9007ced85c637da19ef9ecaf437744495c8c2999", + "sha256:3530382a43a0e524bc931f187fc69ef4c42828cf7d7f592f7f249f602b5a4ab0", + "sha256:425a9a6ac2823ee6e46a76a21a4e8342d8fa5c01e08b823c1f19a8b74f096069", + "sha256:46cf7088bf91bdc9b26f9c55636492c1cce3e7aaf8041bbf0243f5e5325cfb2d", + "sha256:4828190fb6c4bcb6ebc6331f01fe66ae838bb3bd58e753b59d4b22eb444b996c", + "sha256:49fe9155ab32721b9122975e168a6760d8ce4cffe423bcd7ca269ba41b5dfac1", + "sha256:4ca0f52170e821bc8da6fc0cc565b7bb8ff8d90d36b5e9fdd68e8a86bdf72036", + "sha256:51dfbd4d26172d31150d84c19bbe06c68ea4b7f11bbc7b3a5e146b367c311349", + "sha256:5f31e6b0a5a253f6aa49be67279be4a7e5a4ef259a9f33c69f7d1b1191939872", + "sha256:627ba1bc94f6adf0b0a2e35d87020285ead22d9f648c7e75bb64f367375f3b22", + "sha256:680806cf63baa0039b920f4976f5f31b10e772de42f16310a6839d9f21a26b0d", + "sha256:6a3511ae33f09094185d111160fd192c67aa0a2a8d19b54d36e4c78f651dc5ad", + "sha256:6a5bf57554e80f75a7db3d4b1dacaa2764611ae166ab42ea9a72bcdb5d577637", + "sha256:6b613164cb8425e2f8db5849ffb84892e523bf6d26deb8f9bb76ae86181fa12b", + "sha256:7405ade85c83c37682c8fe65554759800a4a8c54b2d96e0f8ad114d31b808d57", + "sha256:7aad98a25ed8ac917fdd8a9c1e706e5a0956e06c498be1f713b61734333a4507", + "sha256:7bedbe4cc930fa4b100fc845ea1ea5788fcd7ae9562e669989c11618ae8d76ee", + "sha256:7ef2dde4fa9408475038fc9aadfc1fb2676b174e68356359632e980c661ec8f6", + "sha256:817ee05c6c9f7a69a16200f0c90ab26d23a87701e2a284bd15156783e46dbcc8", + "sha256:944e9ccf67a9594137f942d5b52c8d238b1b4e46c7a0c2891b7ae6e01e7c80a4", + "sha256:964bcc28d867e0f5491a564b7debb3ffdd8717928d315d12e0d7defa9e43b723", + "sha256:96d4819e25bf3b685199b304a0029ce4a3caf98947ce8a066c9137cc78ad2c58", + "sha256:a77c6fb8d76e9c9f99f2f3437c1a4ac287b34eaf40997cfab1e9bd2be175ac39", + "sha256:b0a97c927497e3bc36b33987abb99bf17a9a175a19af38a892dc4bbb844d7ee2", + "sha256:b97737a3ffbea79eebb062eb0d67d72307195035332501722a9ca86bab9e3ab2", + "sha256:bbc505d1dc469ac12a0a064214879eac6294038d6b24ae9f71faae1448a9608d", + "sha256:c22fe01e53dc65edd1945a2e6f0015e887f84ced233acecb64b4daadb32f5c97", + "sha256:ce1678a2ccbe696cf3af15a75bb72ee008d7ff183c9228592ede9db467e64f1b", + "sha256:e00a6c10a5c53979d6242f123c0a97cff9f3abed7f064fc412c36dc521b5f257", + "sha256:eaa3e28ea2235b33220b949c5a0d6cf79baa80eab2eb5607ca8ab7525331b9ff", + "sha256:f3fe7a5ae34d5a414957cc7f457e2b92076e72938423ac64d215722f6cf49a9e" ], - "markers": "python_version >= '3.7'", - "version": "==42.0.6" + "index": "pypi", + "markers": "python_version >= '3.7' and python_full_version not in '3.9.0, 3.9.1'", + "version": "==45.0.4" }, "daphne": { "hashes": [ - "sha256:618d1322bb4d875342b99dd2a10da2d9aae7ee3645f765965fdc1e658ea5290a", - "sha256:fcbcace38eb86624ae247c7ffdc8ac12f155d7d19eafac4247381896d6f33761" + "sha256:c055de9e685cab7aa369e25e16731baa9b310b9db1a76886dbdde0b4456fb056", + "sha256:ccc7a476c498272237e27758a02aff11c76ab777c4e20b9b6c141729db599d5d" ], "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==4.1.2" + "markers": "python_version >= '3.9'", + "version": "==4.2.0" }, "decorator": { "hashes": [ - "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330", - "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186" + "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", + "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a" ], - "markers": "python_version >= '3.5'", - "version": "==5.1.1" + "markers": "python_version >= '3.8'", + "version": "==5.2.1" }, "defusedxml": { "hashes": [ @@ -236,29 +357,29 @@ }, "dj-database-url": { "hashes": [ - "sha256:04bc34b248d4c21aaa13e4ab419ae6575ef5f10f3df735ce7da97722caa356e0", - "sha256:f2042cefe1086e539c9da39fad5ad7f61173bf79665e69bf7e4de55fa88b135f" + "sha256:749a7a42d88d6c741c1d2f4ab24c2ae0d5cd12f00f2d1d55ff9f5fadabe8a2c3", + "sha256:cbb84b2e3f372460b1e43692bf9fdc0c32e78930ee101db470cba56105fca1e5" ], "index": "pypi", - "version": "==2.1.0" + "version": "==3.0.0" }, "django": { "hashes": [ - "sha256:4bd01a8c830bb77a8a3b0e7d8b25b887e536ad17a81ba2dce5476135c73312bd", - "sha256:916423499d75d62da7aa038d19aef23d23498d8df229775eb0a6309ee1013775" + "sha256:85852e517f84435e9b13421379cd6c43ef5b48a9c8b391d29a26f7900967e952", + "sha256:997ef2162d04ead6869551b22cde4e06da1f94cf595f4af3f3d3afeae1f3f6fe" ], "index": "pypi", "markers": "python_version >= '3.10'", - "version": "==5.0.4" + "version": "==5.2.2" }, "django-appconf": { "hashes": [ - "sha256:c3ae442fba1ff7ec830412c5184b17169a7a1e71cf0864a4c3f93cf4c98a1993", - "sha256:cfe87ea827c4ee04b9a70fab90b86d704cb02f2981f89da8423cb0fabf88efbf" + "sha256:7abd5a163ff57557f216e84d3ce9dac36c37ffce1ab9a044d3d53b7c943dd10f", + "sha256:9fcead372f82a0f21ee189434e7ae9c007cbb29af1118c18251720f3d06243e4" ], "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==1.0.6" + "markers": "python_version >= '3.9'", + "version": "==1.1.0" }, "django-better-admin-arrayfield": { "hashes": [ @@ -270,29 +391,29 @@ }, "django-cors-headers": { "hashes": [ - "sha256:0b1fd19297e37417fc9f835d39e45c8c642938ddba1acce0c1753d3edef04f36", - "sha256:0bf65ef45e606aff1994d35503e6b677c0b26cafff6506f8fd7187f3be840207" + "sha256:6fdf31bf9c6d6448ba09ef57157db2268d515d94fc5c89a0a1028e1fc03ee52b", + "sha256:f1c125dcd58479fe7a67fe2499c16ee38b81b397463cf025f0e2c42937421070" ], "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==4.3.1" + "markers": "python_version >= '3.9'", + "version": "==4.7.0" }, "django-dynamic-preferences": { "hashes": [ - "sha256:0d3d456626244d0bdaf312c81f2b3e14bd16134e8fcf53a33fd12e5d0bdd88dd", - "sha256:527b943d2b5a5c1ea2de8f941778a39efddf2b18ccf44e6aaab05290256ec82f" + "sha256:1f7111e01235b6273a4038783d92a8bf2826cb4eee7e324779b2dc76e3e00b06", + "sha256:c13520107d13c696997a237f8e7731cef93642837d523b95863a40115f7f2d8f" ], "index": "pypi", - "version": "==1.16.0" + "version": "==1.17.0" }, "django-extensions": { "hashes": [ - "sha256:44d27919d04e23b3f40231c4ab7af4e61ce832ef46d610cc650d53e68328410a", - "sha256:9600b7562f79a92cbf1fde6403c04fee314608fefbb595502e34383ae8203401" + "sha256:0699a7af28f2523bf8db309a80278519362cd4b6e1fd0a8cd4bf063e1e023336", + "sha256:7b70a4d28e9b840f44694e3f7feb54f55d495f8b3fa6c5c0e5e12bcb2aa3cdeb" ], "index": "pypi", - "markers": "python_version >= '3.6'", - "version": "==3.2.3" + "markers": "python_version >= '3.9'", + "version": "==4.1" }, "django-formtools": { "hashes": [ @@ -321,18 +442,18 @@ }, "django-jet-reboot": { "hashes": [ - "sha256:e1cc40606331539106cae58701bbaf76c0a4486bbe1aee6d85b2f20ce8ad3f71" + "sha256:d54cd4fa4d22706bf48884191300e9c22c777b835993f8112c35126c2edcf135" ], "index": "pypi", - "version": "==1.3.7" + "version": "==1.3.10" }, "django-polymorphic": { "hashes": [ - "sha256:08bc4f4f4a773a19b2deced5a56deddd1ef56ebd15207bf4052e2901c25ef57e", - "sha256:d6955b5308bf6e41dcb22ba7c96f00b51dfa497a8a5ab1e9c06c7951bf417bf8" + "sha256:0ce3984999e103a0d1a434a5c5617f2c7f990dc3d5fb3585ce0fadadf9ff90ea", + "sha256:4438d95a0aef6c4307cd6c83ead387e1142ce80b65188a931ec2f0dbdd9bfc51" ], "index": "pypi", - "version": "==3.1.0" + "version": "==4.1.0" }, "django-redis": { "hashes": [ @@ -345,20 +466,20 @@ }, "django-split-settings": { "hashes": [ - "sha256:c1f57f6b54fc0d93082c12163e76fad082c214f5fa0d16d84a1226d2c9f14f26", - "sha256:c902ef60d5fe8190ff224284f68e3c9015b6f1aca9e9d6bd70bf86394ff32634" + "sha256:72bd7dd9f12602585681074d1f859643fb4f6b196b584688fab86bdd73a57dff", + "sha256:d3975aa3601e37f65c59b9977b6bcb1de8bc27496930054078589c7d56998a9d" ], "index": "pypi", "markers": "python_version >= '3.9' and python_version < '4.0'", - "version": "==1.3.1" + "version": "==1.3.2" }, "django-statici18n": { "hashes": [ - "sha256:323ebecbfa39408ad242a5e782083bac73bf0e13d93c3e2b29fd32d3379eb3c8", - "sha256:80ac9f21cb80c1cc5a60b558a104b47acc0914fe2bae9fef74f5a0100c8f3d36" + "sha256:1bedd5cbffc2cf1772808995fb818756592fc9cd88b79547b6a25a747b23087d", + "sha256:87ed5adb15d6b32d20f438df76f39f16cc5804f452a8785bebc64729ff08cfb4" ], "index": "pypi", - "version": "==2.5.0" + "version": "==2.6.0" }, "django-summernote": { "hashes": [ @@ -369,12 +490,12 @@ }, "djangorestframework": { "hashes": [ - "sha256:3ccc0475bce968608cf30d07fb17d8e52d1d7fc8bfe779c905463200750cbca6", - "sha256:f88fad74183dfc7144b2756d0d2ac716ea5b4c7c9840995ac3bfd8ec034333c1" + "sha256:bea7e9f6b96a8584c5224bfb2e4348dfb3f8b5e34edbecb98da258e892089361", + "sha256:f022ff46613584de994c0c6a4aebbace5fd700555fbe9d33b865ebf173eba6c9" ], "index": "pypi", - "markers": "python_version >= '3.6'", - "version": "==3.15.1" + "markers": "python_version >= '3.9'", + "version": "==3.16.0" }, "drf-link-header-pagination": { "hashes": [ @@ -387,37 +508,38 @@ }, "drf-spectacular": { "hashes": [ - "sha256:a199492f2163c4101055075ebdbb037d59c6e0030692fc83a1a8c0fc65929981", - "sha256:b1c04bf8b2fbbeaf6f59414b4ea448c8787aba4d32f76055c3b13335cf7ec37b" + "sha256:2c778a47a40ab2f5078a7c42e82baba07397bb35b074ae4680721b2805943061", + "sha256:856e7edf1056e49a4245e87a61e8da4baff46c83dbc25be1da2df77f354c7cb4" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==0.27.2" + "version": "==0.28.0" }, "gunicorn": { "hashes": [ - "sha256:350679f91b24062c86e386e198a15438d53a7a8207235a78ba1b53df4c4378d9", - "sha256:4a0b436239ff76fb33f11c07a16482c521a7e09c1ce3cc293c2330afe01bec63" + "sha256:ec400d38950de4dfd418cff8328b2c8faed0edb0d517d3394e457c317908ca4d", + "sha256:f014447a0101dc57e294f6c18ca6b40227a4c90e9bdb586042628030cba004ec" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==22.0.0" + "version": "==23.0.0" }, "honcho": { "hashes": [ - "sha256:a4d6e3a88a7b51b66351ecfc6e9d79d8f4b87351db9ad7e923f5632cc498122f", - "sha256:c5eca0bded4bef6697a23aec0422fd4f6508ea3581979a3485fc4b89357eb2a9" + "sha256:56dcd04fc72d362a4befb9303b1a1a812cba5da283526fbc6509be122918ddf3", + "sha256:af3815c03c634bf67d50f114253ea9fef72ecff26e4fd06b29234789ac5b8b2e" ], "index": "pypi", - "version": "==1.1.0" + "version": "==2.0.0" }, "html2text": { "hashes": [ - "sha256:05f8e367d15aaabc96415376776cdd11afd5127a77fce6e36afc60c563ca2c32" + "sha256:00569167ffdab3d7767a4cdf589b7f57e777a5ed28d12907d8c58769ec734acc", + "sha256:948a645f8f0bc3abe7fd587019a2197a12436cd73d0d4908af95bfc8da337588" ], "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==2024.2.26" + "markers": "python_version >= '3.9'", + "version": "==2025.4.15" }, "hyperlink": { "hashes": [ @@ -428,17 +550,19 @@ }, "idna": { "hashes": [ - "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc", - "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0" + "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", + "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3" ], - "version": "==3.7" + "markers": "python_version >= '3.6'", + "version": "==3.10" }, "incremental": { "hashes": [ - "sha256:912feeb5e0f7e0188e6f42241d2f450002e11bbc0937c65865045854c24c0bd0", - "sha256:b864a1f30885ee72c5ac2835a761b8fe8aa9c28b9395cacf27286602688d3e51" + "sha256:8cb2c3431530bec48ad70513931a760f446ad6c25e8333ca5d95e24b0ed7b8fe", + "sha256:fb4f1d47ee60efe87d4f6f0ebb5f70b9760db2b2574c59c8e8912be4ebd464c9" ], - "version": "==22.10.0" + "markers": "python_version >= '3.8'", + "version": "==24.7.2" }, "inflection": { "hashes": [ @@ -459,27 +583,27 @@ }, "jedi": { "hashes": [ - "sha256:cf0496f3651bc65d7174ac1b7d043eff454892c708a87d1b683e57b569927ffd", - "sha256:e983c654fe5c02867aef4cdfce5a2fbb4a50adc0af145f70504238f18ef5e7e0" + "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", + "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9" ], "markers": "python_version >= '3.6'", - "version": "==0.19.1" + "version": "==0.19.2" }, "jsonschema": { "hashes": [ - "sha256:5b22d434a45935119af990552c862e5d6d564e8f6601206b305a61fdf661a2b7", - "sha256:ff4cfd6b1367a40e7bc6411caec72effadd3db0bbe5017de188f2d6108335802" + "sha256:0b4e8069eb12aedfa881333004bccaec24ecef5a8a6a4b6df142b2cc9599d196", + "sha256:a462455f19f5faf404a7902952b6f0e3ce868f3ee09a359b05eca6673bd8412d" ], - "markers": "python_version >= '3.8'", - "version": "==4.22.0" + "markers": "python_version >= '3.9'", + "version": "==4.24.0" }, "jsonschema-specifications": { "hashes": [ - "sha256:48a76787b3e70f5ed53f1160d2b81f586e4ca6d1548c5de7085d1682674764cc", - "sha256:87e4fdf3a94858b8a2ba2778d9ba57d8a9cafca7c7489c46ba0d30a8bc6a9c3c" + "sha256:4653bffbd6584f7de83a67e0d620ef16900b390ddc7939d56684d6c81e33f1af", + "sha256:630159c9f4dbea161a6a2205c3011cc4f18ff381b189fff48bb39b9bf26ae608" ], - "markers": "python_version >= '3.8'", - "version": "==2023.12.1" + "markers": "python_version >= '3.9'", + "version": "==2025.4.1" }, "matplotlib-inline": { "hashes": [ @@ -491,65 +615,73 @@ }, "msgpack": { "hashes": [ - "sha256:00e073efcba9ea99db5acef3959efa45b52bc67b61b00823d2a1a6944bf45982", - "sha256:0726c282d188e204281ebd8de31724b7d749adebc086873a59efb8cf7ae27df3", - "sha256:0ceea77719d45c839fd73abcb190b8390412a890df2f83fb8cf49b2a4b5c2f40", - "sha256:114be227f5213ef8b215c22dde19532f5da9652e56e8ce969bf0a26d7c419fee", - "sha256:13577ec9e247f8741c84d06b9ece5f654920d8365a4b636ce0e44f15e07ec693", - "sha256:1876b0b653a808fcd50123b953af170c535027bf1d053b59790eebb0aeb38950", - "sha256:1ab0bbcd4d1f7b6991ee7c753655b481c50084294218de69365f8f1970d4c151", - "sha256:1cce488457370ffd1f953846f82323cb6b2ad2190987cd4d70b2713e17268d24", - "sha256:26ee97a8261e6e35885c2ecd2fd4a6d38252246f94a2aec23665a4e66d066305", - "sha256:3528807cbbb7f315bb81959d5961855e7ba52aa60a3097151cb21956fbc7502b", - "sha256:374a8e88ddab84b9ada695d255679fb99c53513c0a51778796fcf0944d6c789c", - "sha256:376081f471a2ef24828b83a641a02c575d6103a3ad7fd7dade5486cad10ea659", - "sha256:3923a1778f7e5ef31865893fdca12a8d7dc03a44b33e2a5f3295416314c09f5d", - "sha256:4916727e31c28be8beaf11cf117d6f6f188dcc36daae4e851fee88646f5b6b18", - "sha256:493c5c5e44b06d6c9268ce21b302c9ca055c1fd3484c25ba41d34476c76ee746", - "sha256:505fe3d03856ac7d215dbe005414bc28505d26f0c128906037e66d98c4e95868", - "sha256:5845fdf5e5d5b78a49b826fcdc0eb2e2aa7191980e3d2cfd2a30303a74f212e2", - "sha256:5c330eace3dd100bdb54b5653b966de7f51c26ec4a7d4e87132d9b4f738220ba", - "sha256:5dbf059fb4b7c240c873c1245ee112505be27497e90f7c6591261c7d3c3a8228", - "sha256:5e390971d082dba073c05dbd56322427d3280b7cc8b53484c9377adfbae67dc2", - "sha256:5fbb160554e319f7b22ecf530a80a3ff496d38e8e07ae763b9e82fadfe96f273", - "sha256:64d0fcd436c5683fdd7c907eeae5e2cbb5eb872fafbc03a43609d7941840995c", - "sha256:69284049d07fce531c17404fcba2bb1df472bc2dcdac642ae71a2d079d950653", - "sha256:6a0e76621f6e1f908ae52860bdcb58e1ca85231a9b0545e64509c931dd34275a", - "sha256:73ee792784d48aa338bba28063e19a27e8d989344f34aad14ea6e1b9bd83f596", - "sha256:74398a4cf19de42e1498368c36eed45d9528f5fd0155241e82c4082b7e16cffd", - "sha256:7938111ed1358f536daf311be244f34df7bf3cdedb3ed883787aca97778b28d8", - "sha256:82d92c773fbc6942a7a8b520d22c11cfc8fd83bba86116bfcf962c2f5c2ecdaa", - "sha256:83b5c044f3eff2a6534768ccfd50425939e7a8b5cf9a7261c385de1e20dcfc85", - "sha256:8db8e423192303ed77cff4dce3a4b88dbfaf43979d280181558af5e2c3c71afc", - "sha256:9517004e21664f2b5a5fd6333b0731b9cf0817403a941b393d89a2f1dc2bd836", - "sha256:95c02b0e27e706e48d0e5426d1710ca78e0f0628d6e89d5b5a5b91a5f12274f3", - "sha256:99881222f4a8c2f641f25703963a5cefb076adffd959e0558dc9f803a52d6a58", - "sha256:9ee32dcb8e531adae1f1ca568822e9b3a738369b3b686d1477cbc643c4a9c128", - "sha256:a22e47578b30a3e199ab067a4d43d790249b3c0587d9a771921f86250c8435db", - "sha256:b5505774ea2a73a86ea176e8a9a4a7c8bf5d521050f0f6f8426afe798689243f", - "sha256:bd739c9251d01e0279ce729e37b39d49a08c0420d3fee7f2a4968c0576678f77", - "sha256:d16a786905034e7e34098634b184a7d81f91d4c3d246edc6bd7aefb2fd8ea6ad", - "sha256:d3420522057ebab1728b21ad473aa950026d07cb09da41103f8e597dfbfaeb13", - "sha256:d56fd9f1f1cdc8227d7b7918f55091349741904d9520c65f0139a9755952c9e8", - "sha256:d661dc4785affa9d0edfdd1e59ec056a58b3dbb9f196fa43587f3ddac654ac7b", - "sha256:dfe1f0f0ed5785c187144c46a292b8c34c1295c01da12e10ccddfc16def4448a", - "sha256:e1dd7839443592d00e96db831eddb4111a2a81a46b028f0facd60a09ebbdd543", - "sha256:e2872993e209f7ed04d963e4b4fbae72d034844ec66bc4ca403329db2074377b", - "sha256:e2f879ab92ce502a1e65fce390eab619774dda6a6ff719718069ac94084098ce", - "sha256:e3aa7e51d738e0ec0afbed661261513b38b3014754c9459508399baf14ae0c9d", - "sha256:e532dbd6ddfe13946de050d7474e3f5fb6ec774fbb1a188aaf469b08cf04189a", - "sha256:e6b7842518a63a9f17107eb176320960ec095a8ee3b4420b5f688e24bf50c53c", - "sha256:e75753aeda0ddc4c28dce4c32ba2f6ec30b1b02f6c0b14e547841ba5b24f753f", - "sha256:eadb9f826c138e6cf3c49d6f8de88225a3c0ab181a9b4ba792e006e5292d150e", - "sha256:ed59dd52075f8fc91da6053b12e8c89e37aa043f8986efd89e61fae69dc1b011", - "sha256:ef254a06bcea461e65ff0373d8a0dd1ed3aa004af48839f002a0c994a6f72d04", - "sha256:f3709997b228685fe53e8c433e2df9f0cdb5f4542bd5114ed17ac3c0129b0480", - "sha256:f51bab98d52739c50c56658cc303f190785f9a2cd97b823357e7aeae54c8f68a", - "sha256:f9904e24646570539a8950400602d66d2b2c492b9010ea7e965025cb71d0c86d", - "sha256:f9af38a89b6a5c04b7d18c492c8ccf2aee7048aff1ce8437c4683bb5a1df893d" + "sha256:06f5fd2f6bb2a7914922d935d3b8bb4a7fff3a9a91cfce6d06c13bc42bec975b", + "sha256:071603e2f0771c45ad9bc65719291c568d4edf120b44eb36324dcb02a13bfddf", + "sha256:0907e1a7119b337971a689153665764adc34e89175f9a34793307d9def08e6ca", + "sha256:0f92a83b84e7c0749e3f12821949d79485971f087604178026085f60ce109330", + "sha256:115a7af8ee9e8cddc10f87636767857e7e3717b7a2e97379dc2054712693e90f", + "sha256:13599f8829cfbe0158f6456374e9eea9f44eee08076291771d8ae93eda56607f", + "sha256:17fb65dd0bec285907f68b15734a993ad3fc94332b5bb21b0435846228de1f39", + "sha256:2137773500afa5494a61b1208619e3871f75f27b03bcfca7b3a7023284140247", + "sha256:3180065ec2abbe13a4ad37688b61b99d7f9e012a535b930e0e683ad6bc30155b", + "sha256:398b713459fea610861c8a7b62a6fec1882759f308ae0795b5413ff6a160cf3c", + "sha256:3d364a55082fb2a7416f6c63ae383fbd903adb5a6cf78c5b96cc6316dc1cedc7", + "sha256:3df7e6b05571b3814361e8464f9304c42d2196808e0119f55d0d3e62cd5ea044", + "sha256:41c991beebf175faf352fb940bf2af9ad1fb77fd25f38d9142053914947cdbf6", + "sha256:42f754515e0f683f9c79210a5d1cad631ec3d06cea5172214d2176a42e67e19b", + "sha256:452aff037287acb1d70a804ffd022b21fa2bb7c46bee884dbc864cc9024128a0", + "sha256:4676e5be1b472909b2ee6356ff425ebedf5142427842aa06b4dfd5117d1ca8a2", + "sha256:46c34e99110762a76e3911fc923222472c9d681f1094096ac4102c18319e6468", + "sha256:471e27a5787a2e3f974ba023f9e265a8c7cfd373632247deb225617e3100a3c7", + "sha256:4a1964df7b81285d00a84da4e70cb1383f2e665e0f1f2a7027e683956d04b734", + "sha256:4b51405e36e075193bc051315dbf29168d6141ae2500ba8cd80a522964e31434", + "sha256:4d1b7ff2d6146e16e8bd665ac726a89c74163ef8cd39fa8c1087d4e52d3a2325", + "sha256:53258eeb7a80fc46f62fd59c876957a2d0e15e6449a9e71842b6d24419d88ca1", + "sha256:534480ee5690ab3cbed89d4c8971a5c631b69a8c0883ecfea96c19118510c846", + "sha256:58638690ebd0a06427c5fe1a227bb6b8b9fdc2bd07701bec13c2335c82131a88", + "sha256:58dfc47f8b102da61e8949708b3eafc3504509a5728f8b4ddef84bd9e16ad420", + "sha256:59caf6a4ed0d164055ccff8fe31eddc0ebc07cf7326a2aaa0dbf7a4001cd823e", + "sha256:5dbad74103df937e1325cc4bfeaf57713be0b4f15e1c2da43ccdd836393e2ea2", + "sha256:5e1da8f11a3dd397f0a32c76165cf0c4eb95b31013a94f6ecc0b280c05c91b59", + "sha256:646afc8102935a388ffc3914b336d22d1c2d6209c773f3eb5dd4d6d3b6f8c1cb", + "sha256:64fc9068d701233effd61b19efb1485587560b66fe57b3e50d29c5d78e7fef68", + "sha256:65553c9b6da8166e819a6aa90ad15288599b340f91d18f60b2061f402b9a4915", + "sha256:685ec345eefc757a7c8af44a3032734a739f8c45d1b0ac45efc5d8977aa4720f", + "sha256:6ad622bf7756d5a497d5b6836e7fc3752e2dd6f4c648e24b1803f6048596f701", + "sha256:73322a6cc57fcee3c0c57c4463d828e9428275fb85a27aa2aa1a92fdc42afd7b", + "sha256:74bed8f63f8f14d75eec75cf3d04ad581da6b914001b474a5d3cd3372c8cc27d", + "sha256:79ec007767b9b56860e0372085f8504db5d06bd6a327a335449508bbee9648fa", + "sha256:7a946a8992941fea80ed4beae6bff74ffd7ee129a90b4dd5cf9c476a30e9708d", + "sha256:7ad442d527a7e358a469faf43fda45aaf4ac3249c8310a82f0ccff9164e5dccd", + "sha256:7c9a35ce2c2573bada929e0b7b3576de647b0defbd25f5139dcdaba0ae35a4cc", + "sha256:7e7b853bbc44fb03fbdba34feb4bd414322180135e2cb5164f20ce1c9795ee48", + "sha256:879a7b7b0ad82481c52d3c7eb99bf6f0645dbdec5134a4bddbd16f3506947feb", + "sha256:8a706d1e74dd3dea05cb54580d9bd8b2880e9264856ce5068027eed09680aa74", + "sha256:8a84efb768fb968381e525eeeb3d92857e4985aacc39f3c47ffd00eb4509315b", + "sha256:8cf9e8c3a2153934a23ac160cc4cba0ec035f6867c8013cc6077a79823370346", + "sha256:8da4bf6d54ceed70e8861f833f83ce0814a2b72102e890cbdfe4b34764cdd66e", + "sha256:8e59bca908d9ca0de3dc8684f21ebf9a690fe47b6be93236eb40b99af28b6ea6", + "sha256:914571a2a5b4e7606997e169f64ce53a8b1e06f2cf2c3a7273aa106236d43dd5", + "sha256:a51abd48c6d8ac89e0cfd4fe177c61481aca2d5e7ba42044fd218cfd8ea9899f", + "sha256:a52a1f3a5af7ba1c9ace055b659189f6c669cf3657095b50f9602af3a3ba0fe5", + "sha256:ad33e8400e4ec17ba782f7b9cf868977d867ed784a1f5f2ab46e7ba53b6e1e1b", + "sha256:b4c01941fd2ff87c2a934ee6055bda4ed353a7846b8d4f341c428109e9fcde8c", + "sha256:bce7d9e614a04d0883af0b3d4d501171fbfca038f12c77fa838d9f198147a23f", + "sha256:c40ffa9a15d74e05ba1fe2681ea33b9caffd886675412612d93ab17b58ea2fec", + "sha256:c5a91481a3cc573ac8c0d9aace09345d989dc4a0202b7fcb312c88c26d4e71a8", + "sha256:c921af52214dcbb75e6bdf6a661b23c3e6417f00c603dd2070bccb5c3ef499f5", + "sha256:d46cf9e3705ea9485687aa4001a76e44748b609d260af21c4ceea7f2212a501d", + "sha256:d8ce0b22b890be5d252de90d0e0d119f363012027cf256185fc3d474c44b1b9e", + "sha256:dd432ccc2c72b914e4cb77afce64aab761c1137cc698be3984eee260bcb2896e", + "sha256:e0856a2b7e8dcb874be44fea031d22e5b3a19121be92a1e098f46068a11b0870", + "sha256:e1f3c3d21f7cf67bcf2da8e494d30a75e4cf60041d98b3f79875afb5b96f3a3f", + "sha256:f1ba6136e650898082d9d5a5217d5906d1e138024f836ff48691784bbe1adf96", + "sha256:f3e9b4936df53b970513eac1758f3882c88658a220b58dcc1e39606dccaaf01c", + "sha256:f80bc7d47f76089633763f952e67f8214cb7b3ee6bfa489b3cb6a84cfac114cd", + "sha256:fd2906780f25c8ed5d7b323379f6138524ba793428db5d0e9d226d3fa6aa1788" ], "markers": "python_version >= '3.8'", - "version": "==1.0.8" + "version": "==1.1.0" }, "munkres": { "hashes": [ @@ -561,20 +693,20 @@ }, "networkx": { "hashes": [ - "sha256:0c127d8b2f4865f59ae9cb8aafcd60b5c70f3241ebd66f7defad7c4ab90126c9", - "sha256:28575580c6ebdaf4505b22c6256a2b9de86b316dc63ba9e93abde3d78dfdbcf2" + "sha256:0030d386a9a06dee3565298b4a734b68589749a544acbb6c412dc9e2489ec6ec", + "sha256:d4c6f9cf81f52d69230866796b82afbccdec3db7ae4fbd1b65ea750feed50037" ], "index": "pypi", - "markers": "python_version >= '3.10'", - "version": "==3.3" + "markers": "python_version >= '3.11'", + "version": "==3.5" }, "packaging": { "hashes": [ - "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5", - "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9" + "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", + "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f" ], - "markers": "python_version >= '3.7'", - "version": "==24.0" + "markers": "python_version >= '3.8'", + "version": "==25.0" }, "parso": { "hashes": [ @@ -608,90 +740,86 @@ }, "prompt-toolkit": { "hashes": [ - "sha256:3527b7af26106cbc65a040bcc84839a3566ec1b051bb0bfe953631e704b0ff7d", - "sha256:a11a29cb3bf0a28a387fe5122cdb649816a957cd9261dcedf8c9f1fef33eacf6" + "sha256:52742911fde84e2d423e2f9a4cf1de7d7ac4e51958f648d9540e0fb8db077b07", + "sha256:931a162e3b27fc90c86f1b48bb1fb2c528c2761475e57c9c06de13311c7b54ed" ], - "markers": "python_full_version >= '3.7.0'", - "version": "==3.0.43" + "markers": "python_version >= '3.8'", + "version": "==3.0.51" }, "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:27422aa5f11fbcd9b18da48373eb67081243662f9b46e6fd07c3eb46e4535142", + "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": [ @@ -702,19 +830,19 @@ }, "pyasn1": { "hashes": [ - "sha256:3a35ab2c4b5ef98e17dfdec8ab074046fbda76e281c5a706ccd82328cfc8f64c", - "sha256:cca4bb0f2df5504f02f6f8a775b6e416ff9b0b3b16f7ee80b5a3153d9b804473" + "sha256:0d632f46f2ba09143da3a8afe9e33fb6f92fa2320ab7e886e2d0f7672af84629", + "sha256:6f580d2bdd84365380830acf45550f2511469f673cb4a5ae3857a3170128b034" ], "markers": "python_version >= '3.8'", - "version": "==0.6.0" + "version": "==0.6.1" }, "pyasn1-modules": { "hashes": [ - "sha256:831dbcea1b177b28c9baddf4c6d1013c24c3accd14a1873fffaa6a2e905f17b6", - "sha256:be04f15b66c206eed667e0bb5ab27e2b1855ea54a842e5037738099e8ca4ae0b" + "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", + "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6" ], "markers": "python_version >= '3.8'", - "version": "==0.4.0" + "version": "==0.4.2" }, "pycparser": { "hashes": [ @@ -726,25 +854,18 @@ }, "pygments": { "hashes": [ - "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199", - "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a" + "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", + "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c" ], "markers": "python_version >= '3.8'", - "version": "==2.18.0" + "version": "==2.19.1" }, "pyopenssl": { "hashes": [ - "sha256:17ed5be5936449c5418d1cd269a1a9e9081bc54c17aed272b45856a3d3dc86ad", - "sha256:cabed4bfaa5df9f1a16c0ef64a0cb65318b5cd077a7eda7d6970131ca2f41a6f" + "sha256:2b11f239acc47ac2e5aca04fd7fa829800aeee22a2eb30d744572a157bd8a1ab", + "sha256:8d031884482e0c67ee92bf9a4d8cceb08d92aba7136432ffb0703c5280fc205b" ], - "version": "==24.1.0" - }, - "pypng": { - "hashes": [ - "sha256:4a43e969b8f5aaafb2a415536c1a8ec7e341cd6a3f957fd5b5f32a4cfeed902c", - "sha256:739c433ba96f078315de54c0db975aee537cbc3e1d0ae4ed9aab0ca1e427e2c1" - ], - "version": "==0.20220715.0" + "version": "==25.1.0" }, "python-ipware": { "hashes": [ @@ -756,231 +877,261 @@ }, "pyyaml": { "hashes": [ - "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5", - "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc", - "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df", - "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741", - "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206", - "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27", - "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595", - "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62", - "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98", - "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696", - "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290", - "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9", - "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d", - "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6", - "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867", - "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47", - "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486", - "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6", - "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3", - "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007", - "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938", - "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0", - "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c", - "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735", - "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d", - "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28", - "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4", - "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba", - "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8", - "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef", - "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5", - "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd", - "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3", - "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0", - "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515", - "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c", - "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c", - "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924", - "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34", - "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43", - "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859", - "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673", - "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54", - "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a", - "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b", - "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab", - "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa", - "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c", - "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585", - "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d", - "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f" + "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff", + "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", + "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", + "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", + "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", + "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", + "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", + "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", + "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", + "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", + "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a", + "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", + "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", + "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", + "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", + "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", + "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", + "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a", + "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", + "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", + "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", + "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631", + "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", + "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", + "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", + "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", + "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", + "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", + "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", + "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706", + "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", + "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", + "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", + "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083", + "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", + "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", + "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", + "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", + "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", + "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", + "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", + "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", + "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", + "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", + "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5", + "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d", + "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", + "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", + "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", + "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", + "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", + "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", + "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4" ], - "markers": "python_version >= '3.6'", - "version": "==6.0.1" + "markers": "python_version >= '3.8'", + "version": "==6.0.2" }, "qrcode": { "hashes": [ - "sha256:581dca7a029bcb2deef5d01068e39093e80ef00b4a61098a2182eac59d01643a", - "sha256:9dd969454827e127dbd93696b20747239e6d540e082937c90f14ac95b30f5845" + "sha256:16e64e0716c14960108e85d853062c9e8bba5ca8252c0b4d0231b9df4060ff4f", + "sha256:35c3f2a4172b33136ab9f6b3ef1c00260dd2f66f858f24d88418a015f446506c" ], "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==7.4.2" + "markers": "python_version >= '3.9' and python_version < '4.0'", + "version": "==8.2" }, "redis": { "hashes": [ - "sha256:7adc2835c7a9b5033b7ad8f8918d09b7344188228809c98df07af226d39dec91", - "sha256:ec31f2ed9675cc54c21ba854cfe0462e6faf1d83c8ce5944709db8a4700b9c61" + "sha256:c8ddf316ee0aab65f04a11229e94a64b2618451dab7a67cb2f77eb799d872d5e", + "sha256:e821f129b75dde6cb99dd35e5c76e8c49512a5a0d8dfdc560b2fbd44b85ca977" ], "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==5.0.4" + "markers": "python_version >= '3.9'", + "version": "==6.2.0" }, "referencing": { "hashes": [ - "sha256:25b42124a6c8b632a425174f24087783efb348a6f1e0008e63cd4466fedf703c", - "sha256:eda6d3234d62814d1c64e305c1331c9a3a6132da475ab6382eaa997b21ee75de" + "sha256:df2e89862cd09deabbdba16944cc3f10feb6b3e6f18e902f7cc25609a34775aa", + "sha256:e8699adbbf8b5c7de96d8ffa0eb5c158b3beafce084968e2ea8bb08c6794dcd0" ], + "markers": "python_version >= '3.9'", + "version": "==0.36.2" + }, + "requests": { + "hashes": [ + "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", + "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422" + ], + "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==0.35.1" + "version": "==2.32.4" }, "rpds-py": { "hashes": [ - "sha256:01e36a39af54a30f28b73096dd39b6802eddd04c90dbe161c1b8dbe22353189f", - "sha256:044a3e61a7c2dafacae99d1e722cc2d4c05280790ec5a05031b3876809d89a5c", - "sha256:08231ac30a842bd04daabc4d71fddd7e6d26189406d5a69535638e4dcb88fe76", - "sha256:08f9ad53c3f31dfb4baa00da22f1e862900f45908383c062c27628754af2e88e", - "sha256:0ab39c1ba9023914297dd88ec3b3b3c3f33671baeb6acf82ad7ce883f6e8e157", - "sha256:0af039631b6de0397ab2ba16eaf2872e9f8fca391b44d3d8cac317860a700a3f", - "sha256:0b8612cd233543a3781bc659c731b9d607de65890085098986dfd573fc2befe5", - "sha256:11a8c85ef4a07a7638180bf04fe189d12757c696eb41f310d2426895356dcf05", - "sha256:1374f4129f9bcca53a1bba0bb86bf78325a0374577cf7e9e4cd046b1e6f20e24", - "sha256:1d4acf42190d449d5e89654d5c1ed3a4f17925eec71f05e2a41414689cda02d1", - "sha256:1d9a5be316c15ffb2b3c405c4ff14448c36b4435be062a7f578ccd8b01f0c4d8", - "sha256:1df3659d26f539ac74fb3b0c481cdf9d725386e3552c6fa2974f4d33d78e544b", - "sha256:22806714311a69fd0af9b35b7be97c18a0fc2826e6827dbb3a8c94eac6cf7eeb", - "sha256:2644e47de560eb7bd55c20fc59f6daa04682655c58d08185a9b95c1970fa1e07", - "sha256:2e6d75ab12b0bbab7215e5d40f1e5b738aa539598db27ef83b2ec46747df90e1", - "sha256:30f43887bbae0d49113cbaab729a112251a940e9b274536613097ab8b4899cf6", - "sha256:34b18ba135c687f4dac449aa5157d36e2cbb7c03cbea4ddbd88604e076aa836e", - "sha256:36b3ee798c58ace201289024b52788161e1ea133e4ac93fba7d49da5fec0ef9e", - "sha256:39514da80f971362f9267c600b6d459bfbbc549cffc2cef8e47474fddc9b45b1", - "sha256:39f5441553f1c2aed4de4377178ad8ff8f9d733723d6c66d983d75341de265ab", - "sha256:3a96e0c6a41dcdba3a0a581bbf6c44bb863f27c541547fb4b9711fd8cf0ffad4", - "sha256:3f26b5bd1079acdb0c7a5645e350fe54d16b17bfc5e71f371c449383d3342e17", - "sha256:41ef53e7c58aa4ef281da975f62c258950f54b76ec8e45941e93a3d1d8580594", - "sha256:42821446ee7a76f5d9f71f9e33a4fb2ffd724bb3e7f93386150b61a43115788d", - "sha256:43fbac5f22e25bee1d482c97474f930a353542855f05c1161fd804c9dc74a09d", - "sha256:4457a94da0d5c53dc4b3e4de1158bdab077db23c53232f37a3cb7afdb053a4e3", - "sha256:465a3eb5659338cf2a9243e50ad9b2296fa15061736d6e26240e713522b6235c", - "sha256:482103aed1dfe2f3b71a58eff35ba105289b8d862551ea576bd15479aba01f66", - "sha256:4832d7d380477521a8c1644bbab6588dfedea5e30a7d967b5fb75977c45fd77f", - "sha256:4901165d170a5fde6f589acb90a6b33629ad1ec976d4529e769c6f3d885e3e80", - "sha256:5307def11a35f5ae4581a0b658b0af8178c65c530e94893345bebf41cc139d33", - "sha256:5417558f6887e9b6b65b4527232553c139b57ec42c64570569b155262ac0754f", - "sha256:56a737287efecafc16f6d067c2ea0117abadcd078d58721f967952db329a3e5c", - "sha256:586f8204935b9ec884500498ccc91aa869fc652c40c093bd9e1471fbcc25c022", - "sha256:5b4e7d8d6c9b2e8ee2d55c90b59c707ca59bc30058269b3db7b1f8df5763557e", - "sha256:5ddcba87675b6d509139d1b521e0c8250e967e63b5909a7e8f8944d0f90ff36f", - "sha256:618a3d6cae6ef8ec88bb76dd80b83cfe415ad4f1d942ca2a903bf6b6ff97a2da", - "sha256:635dc434ff724b178cb192c70016cc0ad25a275228f749ee0daf0eddbc8183b1", - "sha256:661d25cbffaf8cc42e971dd570d87cb29a665f49f4abe1f9e76be9a5182c4688", - "sha256:66e6a3af5a75363d2c9a48b07cb27c4ea542938b1a2e93b15a503cdfa8490795", - "sha256:67071a6171e92b6da534b8ae326505f7c18022c6f19072a81dcf40db2638767c", - "sha256:685537e07897f173abcf67258bee3c05c374fa6fff89d4c7e42fb391b0605e98", - "sha256:69e64831e22a6b377772e7fb337533c365085b31619005802a79242fee620bc1", - "sha256:6b0817e34942b2ca527b0e9298373e7cc75f429e8da2055607f4931fded23e20", - "sha256:6c81e5f372cd0dc5dc4809553d34f832f60a46034a5f187756d9b90586c2c307", - "sha256:6d7faa6f14017c0b1e69f5e2c357b998731ea75a442ab3841c0dbbbfe902d2c4", - "sha256:6ef0befbb5d79cf32d0266f5cff01545602344eda89480e1dd88aca964260b18", - "sha256:6ef687afab047554a2d366e112dd187b62d261d49eb79b77e386f94644363294", - "sha256:7223a2a5fe0d217e60a60cdae28d6949140dde9c3bcc714063c5b463065e3d66", - "sha256:77f195baa60a54ef9d2de16fbbfd3ff8b04edc0c0140a761b56c267ac11aa467", - "sha256:793968759cd0d96cac1e367afd70c235867831983f876a53389ad869b043c948", - "sha256:7bd339195d84439cbe5771546fe8a4e8a7a045417d8f9de9a368c434e42a721e", - "sha256:7cd863afe7336c62ec78d7d1349a2f34c007a3cc6c2369d667c65aeec412a5b1", - "sha256:7f2facbd386dd60cbbf1a794181e6aa0bd429bd78bfdf775436020172e2a23f0", - "sha256:84ffab12db93b5f6bad84c712c92060a2d321b35c3c9960b43d08d0f639d60d7", - "sha256:8c8370641f1a7f0e0669ddccca22f1da893cef7628396431eb445d46d893e5cd", - "sha256:8db715ebe3bb7d86d77ac1826f7d67ec11a70dbd2376b7cc214199360517b641", - "sha256:8e8916ae4c720529e18afa0b879473049e95949bf97042e938530e072fde061d", - "sha256:8f03bccbd8586e9dd37219bce4d4e0d3ab492e6b3b533e973fa08a112cb2ffc9", - "sha256:8f2fc11e8fe034ee3c34d316d0ad8808f45bc3b9ce5857ff29d513f3ff2923a1", - "sha256:923d39efa3cfb7279a0327e337a7958bff00cc447fd07a25cddb0a1cc9a6d2da", - "sha256:93df1de2f7f7239dc9cc5a4a12408ee1598725036bd2dedadc14d94525192fc3", - "sha256:998e33ad22dc7ec7e030b3df701c43630b5bc0d8fbc2267653577e3fec279afa", - "sha256:99f70b740dc04d09e6b2699b675874367885217a2e9f782bdf5395632ac663b7", - "sha256:9a00312dea9310d4cb7dbd7787e722d2e86a95c2db92fbd7d0155f97127bcb40", - "sha256:9d54553c1136b50fd12cc17e5b11ad07374c316df307e4cfd6441bea5fb68496", - "sha256:9dbbeb27f4e70bfd9eec1be5477517365afe05a9b2c441a0b21929ee61048124", - "sha256:a1ce3ba137ed54f83e56fb983a5859a27d43a40188ba798993812fed73c70836", - "sha256:a34d557a42aa28bd5c48a023c570219ba2593bcbbb8dc1b98d8cf5d529ab1434", - "sha256:a5f446dd5055667aabaee78487f2b5ab72e244f9bc0b2ffebfeec79051679984", - "sha256:ad36cfb355e24f1bd37cac88c112cd7730873f20fb0bdaf8ba59eedf8216079f", - "sha256:aec493917dd45e3c69d00a8874e7cbed844efd935595ef78a0f25f14312e33c6", - "sha256:b316144e85316da2723f9d8dc75bada12fa58489a527091fa1d5a612643d1a0e", - "sha256:b34ae4636dfc4e76a438ab826a0d1eed2589ca7d9a1b2d5bb546978ac6485461", - "sha256:b34b7aa8b261c1dbf7720b5d6f01f38243e9b9daf7e6b8bc1fd4657000062f2c", - "sha256:bc362ee4e314870a70f4ae88772d72d877246537d9f8cb8f7eacf10884862432", - "sha256:bed88b9a458e354014d662d47e7a5baafd7ff81c780fd91584a10d6ec842cb73", - "sha256:c0013fe6b46aa496a6749c77e00a3eb07952832ad6166bd481c74bda0dcb6d58", - "sha256:c0b5dcf9193625afd8ecc92312d6ed78781c46ecbf39af9ad4681fc9f464af88", - "sha256:c4325ff0442a12113a6379af66978c3fe562f846763287ef66bdc1d57925d337", - "sha256:c463ed05f9dfb9baebef68048aed8dcdc94411e4bf3d33a39ba97e271624f8f7", - "sha256:c8362467a0fdeccd47935f22c256bec5e6abe543bf0d66e3d3d57a8fb5731863", - "sha256:cd5bf1af8efe569654bbef5a3e0a56eca45f87cfcffab31dd8dde70da5982475", - "sha256:cf1ea2e34868f6fbf070e1af291c8180480310173de0b0c43fc38a02929fc0e3", - "sha256:d62dec4976954a23d7f91f2f4530852b0c7608116c257833922a896101336c51", - "sha256:d68c93e381010662ab873fea609bf6c0f428b6d0bb00f2c6939782e0818d37bf", - "sha256:d7c36232a90d4755b720fbd76739d8891732b18cf240a9c645d75f00639a9024", - "sha256:dd18772815d5f008fa03d2b9a681ae38d5ae9f0e599f7dda233c439fcaa00d40", - "sha256:ddc2f4dfd396c7bfa18e6ce371cba60e4cf9d2e5cdb71376aa2da264605b60b9", - "sha256:e003b002ec72c8d5a3e3da2989c7d6065b47d9eaa70cd8808b5384fbb970f4ec", - "sha256:e32a92116d4f2a80b629778280103d2a510a5b3f6314ceccd6e38006b5e92dcb", - "sha256:e4461d0f003a0aa9be2bdd1b798a041f177189c1a0f7619fe8c95ad08d9a45d7", - "sha256:e541ec6f2ec456934fd279a3120f856cd0aedd209fc3852eca563f81738f6861", - "sha256:e546e768d08ad55b20b11dbb78a745151acbd938f8f00d0cfbabe8b0199b9880", - "sha256:ea7d4a99f3b38c37eac212dbd6ec42b7a5ec51e2c74b5d3223e43c811609e65f", - "sha256:ed4eb745efbff0a8e9587d22a84be94a5eb7d2d99c02dacf7bd0911713ed14dd", - "sha256:f8a2f084546cc59ea99fda8e070be2fd140c3092dc11524a71aa8f0f3d5a55ca", - "sha256:fcb25daa9219b4cf3a0ab24b0eb9a5cc8949ed4dc72acb8fa16b7e1681aa3c58", - "sha256:fdea4952db2793c4ad0bdccd27c1d8fdd1423a92f04598bc39425bcc2b8ee46e" + "sha256:0317177b1e8691ab5879f4f33f4b6dc55ad3b344399e23df2e499de7b10a548d", + "sha256:036ded36bedb727beeabc16dc1dad7cb154b3fa444e936a03b67a86dc6a5066e", + "sha256:048893e902132fd6548a2e661fb38bf4896a89eea95ac5816cf443524a85556f", + "sha256:0701942049095741a8aeb298a31b203e735d1c61f4423511d2b1a41dcd8a16da", + "sha256:083a9513a33e0b92cf6e7a6366036c6bb43ea595332c1ab5c8ae329e4bcc0a9c", + "sha256:09eab132f41bf792c7a0ea1578e55df3f3e7f61888e340779b06050a9a3f16e9", + "sha256:0e6a327af8ebf6baba1c10fadd04964c1965d375d318f4435d5f3f9651550f4a", + "sha256:0eb90e94f43e5085623932b68840b6f379f26db7b5c2e6bcef3179bd83c9330f", + "sha256:114a07e85f32b125404f28f2ed0ba431685151c037a26032b213c882f26eb908", + "sha256:115874ae5e2fdcfc16b2aedc95b5eef4aebe91b28e7e21951eda8a5dc0d3461b", + "sha256:140f61d9bed7839446bdd44852e30195c8e520f81329b4201ceead4d64eb3a9f", + "sha256:1521031351865e0181bc585147624d66b3b00a84109b57fcb7a779c3ec3772cd", + "sha256:1c0c434a53714358532d13539272db75a5ed9df75a4a090a753ac7173ec14e11", + "sha256:1d1fadd539298e70cac2f2cb36f5b8a65f742b9b9f1014dd4ea1f7785e2470bf", + "sha256:1de336a4b164c9188cb23f3703adb74a7623ab32d20090d0e9bf499a2203ad65", + "sha256:1ee3e26eb83d39b886d2cb6e06ea701bba82ef30a0de044d34626ede51ec98b0", + "sha256:245550f5a1ac98504147cba96ffec8fabc22b610742e9150138e5d60774686d7", + "sha256:2a40046a529cc15cef88ac5ab589f83f739e2d332cb4d7399072242400ed68c9", + "sha256:2c2cd1a4b0c2b8c5e31ffff50d09f39906fe351389ba143c195566056c13a7ea", + "sha256:2cb9e5b5e26fc02c8a4345048cd9998c2aca7c2712bd1b36da0c72ee969a3523", + "sha256:33358883a4490287e67a2c391dfaea4d9359860281db3292b6886bf0be3d8692", + "sha256:35634369325906bcd01577da4c19e3b9541a15e99f31e91a02d010816b49bfda", + "sha256:35a8d1a24b5936b35c5003313bc177403d8bdef0f8b24f28b1c4a255f94ea992", + "sha256:3af5b4cc10fa41e5bc64e5c198a1b2d2864337f8fcbb9a67e747e34002ce812b", + "sha256:3bcce0edc1488906c2d4c75c94c70a0417e83920dd4c88fec1078c94843a6ce9", + "sha256:3c5b317ecbd8226887994852e85de562f7177add602514d4ac40f87de3ae45a8", + "sha256:3c6564c0947a7f52e4792983f8e6cf9bac140438ebf81f527a21d944f2fd0a40", + "sha256:3ebd879ab996537fc510a2be58c59915b5dd63bccb06d1ef514fee787e05984a", + "sha256:3f0b1798cae2bbbc9b9db44ee068c556d4737911ad53a4e5093d09d04b3bbc24", + "sha256:401ca1c4a20cc0510d3435d89c069fe0a9ae2ee6495135ac46bdd49ec0495763", + "sha256:454601988aab2c6e8fd49e7634c65476b2b919647626208e376afcd22019eeb8", + "sha256:4593c4eae9b27d22df41cde518b4b9e4464d139e4322e2127daa9b5b981b76be", + "sha256:45e484db65e5380804afbec784522de84fa95e6bb92ef1bd3325d33d13efaebd", + "sha256:48d64155d02127c249695abb87d39f0faf410733428d499867606be138161d65", + "sha256:4fbb0dbba559959fcb5d0735a0f87cdbca9e95dac87982e9b95c0f8f7ad10255", + "sha256:4fd52d3455a0aa997734f3835cbc4c9f32571345143960e7d7ebfe7b5fbfa3b2", + "sha256:50f2c501a89c9a5f4e454b126193c5495b9fb441a75b298c60591d8a2eb92e1b", + "sha256:58f77c60956501a4a627749a6dcb78dac522f249dd96b5c9f1c6af29bfacfb66", + "sha256:5a3ddb74b0985c4387719fc536faced33cadf2172769540c62e2a94b7b9be1c4", + "sha256:5c4a128527fe415d73cf1f70a9a688d06130d5810be69f3b553bf7b45e8acf79", + "sha256:5d473be2b13600b93a5675d78f59e63b51b1ba2d0476893415dfbb5477e65b31", + "sha256:5d9e40f32745db28c1ef7aad23f6fc458dc1e29945bd6781060f0d15628b8ddf", + "sha256:5f048bbf18b1f9120685c6d6bb70cc1a52c8cc11bdd04e643d28d3be0baf666d", + "sha256:605ffe7769e24b1800b4d024d24034405d9404f0bc2f55b6db3362cd34145a6f", + "sha256:6099263f526efff9cf3883dfef505518730f7a7a93049b1d90d42e50a22b4793", + "sha256:659d87430a8c8c704d52d094f5ba6fa72ef13b4d385b7e542a08fc240cb4a559", + "sha256:666fa7b1bd0a3810a7f18f6d3a25ccd8866291fbbc3c9b912b917a6715874bb9", + "sha256:68f6f060f0bbdfb0245267da014d3a6da9be127fe3e8cc4a68c6f833f8a23bb1", + "sha256:6d273f136e912aa101a9274c3145dcbddbe4bac560e77e6d5b3c9f6e0ed06d34", + "sha256:6d50841c425d16faf3206ddbba44c21aa3310a0cebc3c1cdfc3e3f4f9f6f5728", + "sha256:771c16060ff4e79584dc48902a91ba79fd93eade3aa3a12d6d2a4aadaf7d542b", + "sha256:785ffacd0ee61c3e60bdfde93baa6d7c10d86f15655bd706c89da08068dc5038", + "sha256:796ad874c89127c91970652a4ee8b00d56368b7e00d3477f4415fe78164c8000", + "sha256:79dc317a5f1c51fd9c6a0c4f48209c6b8526d0524a6904fc1076476e79b00f98", + "sha256:7c9409b47ba0650544b0bb3c188243b83654dfe55dcc173a86832314e1a6a35d", + "sha256:7d779b325cc8238227c47fbc53964c8cc9a941d5dbae87aa007a1f08f2f77b23", + "sha256:816568614ecb22b18a010c7a12559c19f6fe993526af88e95a76d5a60b8b75fb", + "sha256:8378fa4a940f3fb509c081e06cb7f7f2adae8cf46ef258b0e0ed7519facd573e", + "sha256:85608eb70a659bf4c1142b2781083d4b7c0c4e2c90eff11856a9754e965b2540", + "sha256:85fc223d9c76cabe5d0bff82214459189720dc135db45f9f66aa7cffbf9ff6c1", + "sha256:88ec04afe0c59fa64e2f6ea0dd9657e04fc83e38de90f6de201954b4d4eb59bd", + "sha256:8960b6dac09b62dac26e75d7e2c4a22efb835d827a7278c34f72b2b84fa160e3", + "sha256:89706d0683c73a26f76a5315d893c051324d771196ae8b13e6ffa1ffaf5e574f", + "sha256:89c24300cd4a8e4a51e55c31a8ff3918e6651b241ee8876a42cc2b2a078533ba", + "sha256:8c742af695f7525e559c16f1562cf2323db0e3f0fbdcabdf6865b095256b2d40", + "sha256:8dbd586bfa270c1103ece2109314dd423df1fa3d9719928b5d09e4840cec0d72", + "sha256:8eb8c84ecea987a2523e057c0d950bcb3f789696c0499290b8d7b3107a719d78", + "sha256:921954d7fbf3fccc7de8f717799304b14b6d9a45bbeec5a8d7408ccbf531faf5", + "sha256:9a46c2fb2545e21181445515960006e85d22025bd2fe6db23e76daec6eb689fe", + "sha256:9c006f3aadeda131b438c3092124bd196b66312f0caa5823ef09585a669cf449", + "sha256:9ceca1cf097ed77e1a51f1dbc8d174d10cb5931c188a4505ff9f3e119dfe519b", + "sha256:9e5fc7484fa7dce57e25063b0ec9638ff02a908304f861d81ea49273e43838c1", + "sha256:9f2f48ab00181600ee266a095fe815134eb456163f7d6699f525dee471f312cf", + "sha256:9fca84a15333e925dd59ce01da0ffe2ffe0d6e5d29a9eeba2148916d1824948c", + "sha256:a49e1d7a4978ed554f095430b89ecc23f42014a50ac385eb0c4d163ce213c325", + "sha256:a58d1ed49a94d4183483a3ce0af22f20318d4a1434acee255d683ad90bf78129", + "sha256:a61d0b2c7c9a0ae45732a77844917b427ff16ad5464b4d4f5e4adb955f582890", + "sha256:a714bf6e5e81b0e570d01f56e0c89c6375101b8463999ead3a93a5d2a4af91fa", + "sha256:a7b74e92a3b212390bdce1d93da9f6488c3878c1d434c5e751cbc202c5e09500", + "sha256:a8bd2f19e312ce3e1d2c635618e8a8d8132892bb746a7cf74780a489f0f6cdcb", + "sha256:b0be9965f93c222fb9b4cc254235b3b2b215796c03ef5ee64f995b1b69af0762", + "sha256:b24bf3cd93d5b6ecfbedec73b15f143596c88ee249fa98cefa9a9dc9d92c6f28", + "sha256:b5ffe453cde61f73fea9430223c81d29e2fbf412a6073951102146c84e19e34c", + "sha256:bc120d1132cff853ff617754196d0ac0ae63befe7c8498bd67731ba368abe451", + "sha256:bd035756830c712b64725a76327ce80e82ed12ebab361d3a1cdc0f51ea21acb0", + "sha256:bffcf57826d77a4151962bf1701374e0fc87f536e56ec46f1abdd6a903354042", + "sha256:c2013ee878c76269c7b557a9a9c042335d732e89d482606990b70a839635feb7", + "sha256:c4feb9211d15d9160bc85fa72fed46432cdc143eb9cf6d5ca377335a921ac37b", + "sha256:c8980cde3bb8575e7c956a530f2c217c1d6aac453474bf3ea0f9c89868b531b6", + "sha256:c98f126c4fc697b84c423e387337d5b07e4a61e9feac494362a59fd7a2d9ed80", + "sha256:ccc6f3ddef93243538be76f8e47045b4aad7a66a212cd3a0f23e34469473d36b", + "sha256:ccfa689b9246c48947d31dd9d8b16d89a0ecc8e0e26ea5253068efb6c542b76e", + "sha256:cda776f1967cb304816173b30994faaf2fd5bcb37e73118a47964a02c348e1bc", + "sha256:ce4c8e485a3c59593f1a6f683cf0ea5ab1c1dc94d11eea5619e4fb5228b40fbd", + "sha256:d3c10228d6cf6fe2b63d2e7985e94f6916fa46940df46b70449e9ff9297bd3d1", + "sha256:d4ca54b9cf9d80b4016a67a0193ebe0bcf29f6b0a96f09db942087e294d3d4c2", + "sha256:d4cb2b3ddc16710548801c6fcc0cfcdeeff9dafbc983f77265877793f2660309", + "sha256:d50e4864498a9ab639d6d8854b25e80642bd362ff104312d9770b05d66e5fb13", + "sha256:d74ec9bc0e2feb81d3f16946b005748119c0f52a153f6db6a29e8cd68636f295", + "sha256:d8222acdb51a22929c3b2ddb236b69c59c72af4019d2cba961e2f9add9b6e634", + "sha256:db58483f71c5db67d643857404da360dce3573031586034b7d59f245144cc192", + "sha256:dc3c1ff0abc91444cd20ec643d0f805df9a3661fcacf9c95000329f3ddf268a4", + "sha256:dd326a81afe332ede08eb39ab75b301d5676802cdffd3a8f287a5f0b694dc3f5", + "sha256:dec21e02e6cc932538b5203d3a8bd6aa1480c98c4914cb88eea064ecdbc6396a", + "sha256:e1dafef8df605fdb46edcc0bf1573dea0d6d7b01ba87f85cd04dc855b2b4479e", + "sha256:e2f6a2347d3440ae789505693a02836383426249d5293541cd712e07e7aecf54", + "sha256:e37caa8cdb3b7cf24786451a0bdb853f6347b8b92005eeb64225ae1db54d1c2b", + "sha256:e43a005671a9ed5a650f3bc39e4dbccd6d4326b24fb5ea8be5f3a43a6f576c72", + "sha256:e5e2f7280d8d0d3ef06f3ec1b4fd598d386cc6f0721e54f09109a8132182fbfe", + "sha256:e87798852ae0b37c88babb7f7bbbb3e3fecc562a1c340195b44c7e24d403e380", + "sha256:ee86d81551ec68a5c25373c5643d343150cc54672b5e9a0cafc93c1870a53954", + "sha256:f251bf23deb8332823aef1da169d5d89fa84c89f67bdfb566c49dea1fccfd50d", + "sha256:f3d86373ff19ca0441ebeb696ef64cb58b8b5cbacffcda5a0ec2f3911732a194", + "sha256:f4ad628b5174d5315761b67f212774a32f5bad5e61396d38108bd801c0a8f5d9", + "sha256:f70316f760174ca04492b5ab01be631a8ae30cadab1d1081035136ba12738cfa", + "sha256:f73ce1512e04fbe2bc97836e89830d6b4314c171587a99688082d090f934d20a", + "sha256:ff7c23ba0a88cb7b104281a99476cccadf29de2a0ef5ce864959a52675b1ca83" ], - "markers": "python_version >= '3.8'", - "version": "==0.18.0" + "markers": "python_version >= '3.9'", + "version": "==0.25.1" }, "sentry-sdk": { "hashes": [ - "sha256:b54c54a2160f509cf2757260d0cf3885b608c6192c2555a3857e3a4d0f84bdb3", - "sha256:c278e0f523f6f0ee69dc43ad26dcdb1202dffe5ac326ae31472e012d941bee21" + "sha256:8d4a0206b95fa5fe85e5e7517ed662e3888374bdc342c00e435e10e6d831aa6d", + "sha256:90862fe0616ded4572da6c9dadb363121a1ae49a49e21c418f0634e9d10b4c19" ], "index": "pypi", "markers": "python_version >= '3.6'", - "version": "==2.0.1" + "version": "==2.29.1" }, "service-identity": { "hashes": [ - "sha256:6829c9d62fb832c2e1c435629b0a8c476e1929881f28bee4d20bc24161009221", - "sha256:a28caf8130c8a5c1c7a6f5293faaf239bbfb7751e4862436920ee6f2616f568a" + "sha256:6b047fbd8a84fd0bb0d55ebce4031e400562b9196e1e0d3e0fe2b8a59f6d4a85", + "sha256:b8683ba13f0d39c6cd5d625d2c5f65421d6d707b013b375c355751557cbe8e09" ], - "version": "==24.1.0" + "version": "==24.2.0" }, "setuptools": { "hashes": [ - "sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987", - "sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32" + "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", + "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c" ], - "markers": "python_version >= '3.8'", - "version": "==69.5.1" + "markers": "python_version >= '3.9'", + "version": "==80.9.0" }, - "six": { + "sqlparse": { "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" + "sha256:09f67787f56a0b16ecdbde1bfc7f5d9c3371ca683cfeaa8e6ff60b4807ec9272", + "sha256:cf2196ed3418f3ba5de6af7e82c694a9fbdbfecccdfc72e281548517081f16ca" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.16.0" + "markers": "python_version >= '3.8'", + "version": "==0.5.3" }, - "sqlparse": { + "stripe": { "hashes": [ - "sha256:714d0a4932c059d16189f58ef5411ec2287a4360f17cdd0edd2d09d4c5087c93", - "sha256:c204494cd97479d0e39f28c93d46c0b2d5959c7b9ab904762ea6c7af211c8663" + "sha256:1ac2a4abba371acb3f99ff1c4a8748929862ad42d0cdee488298982f1be8d56e", + "sha256:cc9086d162e65e32893e4a03c31194e36e07870653a5f30aacc62da61e548cb9" ], - "markers": "python_version >= '3.8'", - "version": "==0.5.0" + "index": "pypi", + "markers": "python_version >= '3.6'", + "version": "==12.2.0" }, "traitlets": { "hashes": [ @@ -995,11 +1146,11 @@ "tls" ], "hashes": [ - "sha256:039f2e6a49ab5108abd94de187fa92377abe5985c7a72d68d0ad266ba19eae63", - "sha256:6b38b6ece7296b5e122c9eb17da2eeab3d98a198f50ca9efd00fb03e5b4fd4ae" + "sha256:1deb272358cb6be1e3e8fc6f9c8b36f78eb0fa7c2233d2dbe11ec6fee04ea316", + "sha256:8559f654d01a54a8c3efe66d533d43f383531ebf8d81d9f9ab4769d91ca15df7" ], "markers": "python_full_version >= '3.8.0'", - "version": "==24.3.0" + "version": "==25.5.0" }, "txaio": { "hashes": [ @@ -1011,27 +1162,27 @@ }, "typing-extensions": { "hashes": [ - "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0", - "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a" + "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4", + "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af" ], - "markers": "python_version >= '3.8'", - "version": "==4.11.0" + "markers": "python_version >= '3.9'", + "version": "==4.14.0" }, "uritemplate": { "hashes": [ - "sha256:4346edfc5c3b79f694bccd6d6099a322bbeb628dbf2cd86eea55a456ce5124f0", - "sha256:830c08b8d99bdd312ea4ead05994a38e8936266f84b9a7878232db50b044e02e" + "sha256:480c2ed180878955863323eea31b0ede668795de182617fef9c6ca09e6ec9d0e", + "sha256:962201ba1c4edcab02e60f9a0d3821e82dfc5d2d6662a21abd533879bdb8a686" ], - "markers": "python_version >= '3.6'", - "version": "==4.1.1" + "markers": "python_version >= '3.9'", + "version": "==4.2.0" }, "urllib3": { "hashes": [ - "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d", - "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19" + "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466", + "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813" ], - "markers": "python_version >= '3.8'", - "version": "==2.2.1" + "markers": "python_version >= '3.9'", + "version": "==2.4.0" }, "wcwidth": { "hashes": [ @@ -1049,54 +1200,55 @@ }, "whitenoise": { "hashes": [ - "sha256:8998f7370973447fac1e8ef6e8ded2c5209a7b1f67c1012866dbcd09681c3251", - "sha256:b1f9db9bf67dc183484d760b99f4080185633136a273a03f6436034a41064146" + "sha256:8c4a7c9d384694990c26f3047e118c691557481d624f069b7f7752a2f735d609", + "sha256:c8a489049b7ee9889617bb4c274a153f3d979e8f51d2efd0f5b403caf41c57df" ], "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==6.6.0" + "markers": "python_version >= '3.9'", + "version": "==6.9.0" }, "zope-interface": { "hashes": [ - "sha256:014bb94fe6bf1786da1aa044eadf65bc6437bcb81c451592987e5be91e70a91e", - "sha256:01a0b3dd012f584afcf03ed814bce0fc40ed10e47396578621509ac031be98bf", - "sha256:10cde8dc6b2fd6a1d0b5ca4be820063e46ddba417ab82bcf55afe2227337b130", - "sha256:187f7900b63845dcdef1be320a523dbbdba94d89cae570edc2781eb55f8c2f86", - "sha256:1b0c4c90e5eefca2c3e045d9f9ed9f1e2cdbe70eb906bff6b247e17119ad89a1", - "sha256:22e8a218e8e2d87d4d9342aa973b7915297a08efbebea5b25900c73e78ed468e", - "sha256:26c9a37fb395a703e39b11b00b9e921c48f82b6e32cc5851ad5d0618cd8876b5", - "sha256:2bb78c12c1ad3a20c0d981a043d133299117b6854f2e14893b156979ed4e1d2c", - "sha256:2c3cfb272bcb83650e6695d49ae0d14dd06dc694789a3d929f23758557a23d92", - "sha256:2f32010ffb87759c6a3ad1c65ed4d2e38e51f6b430a1ca11cee901ec2b42e021", - "sha256:3c8731596198198746f7ce2a4487a0edcbc9ea5e5918f0ab23c4859bce56055c", - "sha256:40aa8c8e964d47d713b226c5baf5f13cdf3a3169c7a2653163b17ff2e2334d10", - "sha256:4137025731e824eee8d263b20682b28a0bdc0508de9c11d6c6be54163e5b7c83", - "sha256:46034be614d1f75f06e7dcfefba21d609b16b38c21fc912b01a99cb29e58febb", - "sha256:483e118b1e075f1819b3c6ace082b9d7d3a6a5eb14b2b375f1b80a0868117920", - "sha256:4d6b229f5e1a6375f206455cc0a63a8e502ed190fe7eb15e94a312dc69d40299", - "sha256:567d54c06306f9c5b6826190628d66753b9f2b0422f4c02d7c6d2b97ebf0a24e", - "sha256:5683aa8f2639016fd2b421df44301f10820e28a9b96382a6e438e5c6427253af", - "sha256:600101f43a7582d5b9504a7c629a1185a849ce65e60fca0f6968dfc4b76b6d39", - "sha256:62e32f02b3f26204d9c02c3539c802afc3eefb19d601a0987836ed126efb1f21", - "sha256:69dedb790530c7ca5345899a1b4cb837cc53ba669051ea51e8c18f82f9389061", - "sha256:72d5efecad16c619a97744a4f0b67ce1bcc88115aa82fcf1dc5be9bb403bcc0b", - "sha256:8d407e0fd8015f6d5dfad481309638e1968d70e6644e0753f229154667dd6cd5", - "sha256:a058e6cf8d68a5a19cb5449f42a404f0d6c2778b897e6ce8fadda9cea308b1b0", - "sha256:a1adc14a2a9d5e95f76df625a9b39f4709267a483962a572e3f3001ef90ea6e6", - "sha256:a56fe1261230093bfeedc1c1a6cd6f3ec568f9b07f031c9a09f46b201f793a85", - "sha256:ad4524289d8dbd6fb5aa17aedb18f5643e7d48358f42c007a5ee51a2afc2a7c5", - "sha256:afa0491a9f154cf8519a02026dc85a416192f4cb1efbbf32db4a173ba28b289a", - "sha256:bf34840e102d1d0b2d39b1465918d90b312b1119552cebb61a242c42079817b9", - "sha256:c40df4aea777be321b7e68facb901bc67317e94b65d9ab20fb96e0eb3c0b60a1", - "sha256:d0e7321557c702bd92dac3c66a2f22b963155fdb4600133b6b29597f62b71b12", - "sha256:d165d7774d558ea971cb867739fb334faf68fc4756a784e689e11efa3becd59e", - "sha256:e78a183a3c2f555c2ad6aaa1ab572d1c435ba42f1dc3a7e8c82982306a19b785", - "sha256:e8fa0fb05083a1a4216b4b881fdefa71c5d9a106e9b094cd4399af6b52873e91", - "sha256:f83d6b4b22262d9a826c3bd4b2fbfafe1d0000f085ef8e44cd1328eea274ae6a", - "sha256:f95bebd0afe86b2adc074df29edb6848fc4d474ff24075e2c263d698774e108d" + "sha256:033b3923b63474800b04cba480b70f6e6243a62208071fc148354f3f89cc01b7", + "sha256:05b910a5afe03256b58ab2ba6288960a2892dfeef01336dc4be6f1b9ed02ab0a", + "sha256:086ee2f51eaef1e4a52bd7d3111a0404081dadae87f84c0ad4ce2649d4f708b7", + "sha256:0ef9e2f865721553c6f22a9ff97da0f0216c074bd02b25cf0d3af60ea4d6931d", + "sha256:1090c60116b3da3bfdd0c03406e2f14a1ff53e5771aebe33fec1edc0a350175d", + "sha256:144964649eba4c5e4410bb0ee290d338e78f179cdbfd15813de1a664e7649b3b", + "sha256:15398c000c094b8855d7d74f4fdc9e73aa02d4d0d5c775acdef98cdb1119768d", + "sha256:1909f52a00c8c3dcab6c4fad5d13de2285a4b3c7be063b239b8dc15ddfb73bd2", + "sha256:21328fcc9d5b80768bf051faa35ab98fb979080c18e6f84ab3f27ce703bce465", + "sha256:224b7b0314f919e751f2bca17d15aad00ddbb1eadf1cb0190fa8175edb7ede62", + "sha256:25e6a61dcb184453bb00eafa733169ab6d903e46f5c2ace4ad275386f9ab327a", + "sha256:27f926f0dcb058211a3bb3e0e501c69759613b17a553788b2caeb991bed3b61d", + "sha256:29caad142a2355ce7cfea48725aa8bcf0067e2b5cc63fcf5cd9f97ad12d6afb5", + "sha256:2ad9913fd858274db8dd867012ebe544ef18d218f6f7d1e3c3e6d98000f14b75", + "sha256:31d06db13a30303c08d61d5fb32154be51dfcbdb8438d2374ae27b4e069aac40", + "sha256:3e0350b51e88658d5ad126c6a57502b19d5f559f6cb0a628e3dc90442b53dd98", + "sha256:3f6771d1647b1fc543d37640b45c06b34832a943c80d1db214a37c31161a93f1", + "sha256:4893395d5dd2ba655c38ceb13014fd65667740f09fa5bb01caa1e6284e48c0cd", + "sha256:52e446f9955195440e787596dccd1411f543743c359eeb26e9b2c02b077b0519", + "sha256:550f1c6588ecc368c9ce13c44a49b8d6b6f3ca7588873c679bd8fd88a1b557b6", + "sha256:72cd1790b48c16db85d51fbbd12d20949d7339ad84fd971427cf00d990c1f137", + "sha256:7bd449c306ba006c65799ea7912adbbfed071089461a19091a228998b82b1fdb", + "sha256:7dc5016e0133c1a1ec212fc87a4f7e7e562054549a99c73c8896fa3a9e80cbc7", + "sha256:802176a9f99bd8cc276dcd3b8512808716492f6f557c11196d42e26c01a69a4c", + "sha256:80ecf2451596f19fd607bb09953f426588fc1e79e93f5968ecf3367550396b22", + "sha256:8b49f1a3d1ee4cdaf5b32d2e738362c7f5e40ac8b46dd7d1a65e82a4872728fe", + "sha256:8e7da17f53e25d1a3bde5da4601e026adc9e8071f9f6f936d0fe3fe84ace6d54", + "sha256:a102424e28c6b47c67923a1f337ede4a4c2bba3965b01cf707978a801fc7442c", + "sha256:a19a6cc9c6ce4b1e7e3d319a473cf0ee989cbbe2b39201d7c19e214d2dfb80c7", + "sha256:a71a5b541078d0ebe373a81a3b7e71432c61d12e660f1d67896ca62d9628045b", + "sha256:baf95683cde5bc7d0e12d8e7588a3eb754d7c4fa714548adcd96bdf90169f021", + "sha256:cab15ff4832580aa440dc9790b8a6128abd0b88b7ee4dd56abacbc52f212209d", + "sha256:ce290e62229964715f1011c3dbeab7a4a1e4971fd6f31324c4519464473ef9f2", + "sha256:d3a8ffec2a50d8ec470143ea3d15c0c52d73df882eef92de7537e8ce13475e8a", + "sha256:e204937f67b28d2dca73ca936d3039a144a081fc47a07598d44854ea2a106239", + "sha256:eb23f58a446a7f09db85eda09521a498e109f137b85fb278edb2e34841055398", + "sha256:f6dd02ec01f4468da0f234da9d9c8545c5412fef80bc590cc51d8dd084138a89" ], - "markers": "python_version >= '3.7'", - "version": "==6.3" + "markers": "python_version >= '3.8'", + "version": "==7.2" } }, "develop": { @@ -1135,11 +1287,11 @@ }, "certifi": { "hashes": [ - "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", - "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe" + "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6", + "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3" ], "markers": "python_version >= '3.6'", - "version": "==2025.1.31" + "version": "==2025.4.26" }, "cfgv": { "hashes": [ @@ -1151,109 +1303,109 @@ }, "charset-normalizer": { "hashes": [ - "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537", - "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa", - "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a", - "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294", - "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b", - "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd", - "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601", - "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd", - "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4", - "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d", - "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2", - "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313", - "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd", - "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa", - "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8", - "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1", - "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2", - "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496", - "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d", - "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b", - "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e", - "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a", - "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4", - "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca", - "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78", - "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408", - "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5", - "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3", - "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f", - "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a", - "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765", - "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6", - "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146", - "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6", - "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9", - "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd", - "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c", - "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f", - "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545", - "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176", - "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770", - "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824", - "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f", - "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf", - "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487", - "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d", - "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd", - "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b", - "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534", - "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f", - "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b", - "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9", - "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd", - "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125", - "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9", - "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de", - "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11", - "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d", - "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35", - "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f", - "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda", - "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7", - "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a", - "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971", - "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8", - "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41", - "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d", - "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f", - "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757", - "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a", - "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886", - "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77", - "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76", - "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247", - "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85", - "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb", - "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7", - "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e", - "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6", - "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037", - "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1", - "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e", - "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807", - "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407", - "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c", - "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12", - "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3", - "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089", - "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd", - "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e", - "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00", - "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616" + "sha256:005fa3432484527f9732ebd315da8da8001593e2cf46a3d817669f062c3d9ed4", + "sha256:046595208aae0120559a67693ecc65dd75d46f7bf687f159127046628178dc45", + "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7", + "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0", + "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7", + "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d", + "sha256:1b1bde144d98e446b056ef98e59c256e9294f6b74d7af6846bf5ffdafd687a7d", + "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0", + "sha256:1cad5f45b3146325bb38d6855642f6fd609c3f7cad4dbaf75549bf3b904d3184", + "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db", + "sha256:24498ba8ed6c2e0b56d4acbf83f2d989720a93b41d712ebd4f4979660db4417b", + "sha256:25a23ea5c7edc53e0f29bae2c44fcb5a1aa10591aae107f2a2b2583a9c5cbc64", + "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b", + "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8", + "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff", + "sha256:36b31da18b8890a76ec181c3cf44326bf2c48e36d393ca1b72b3f484113ea344", + "sha256:3c21d4fca343c805a52c0c78edc01e3477f6dd1ad7c47653241cf2a206d4fc58", + "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e", + "sha256:43e0933a0eff183ee85833f341ec567c0980dae57c464d8a508e1b2ceb336471", + "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148", + "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a", + "sha256:50bf98d5e563b83cc29471fa114366e6806bc06bc7a25fd59641e41445327836", + "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e", + "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63", + "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c", + "sha256:6333b3aa5a12c26b2a4d4e7335a28f1475e0e5e17d69d55141ee3cab736f66d1", + "sha256:65c981bdbd3f57670af8b59777cbfae75364b483fa8a9f420f08094531d54a01", + "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366", + "sha256:6a0289e4589e8bdfef02a80478f1dfcb14f0ab696b5a00e1f4b8a14a307a3c58", + "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5", + "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c", + "sha256:6fc1f5b51fa4cecaa18f2bd7a003f3dd039dd615cd69a2afd6d3b19aed6775f2", + "sha256:70f7172939fdf8790425ba31915bfbe8335030f05b9913d7ae00a87d4395620a", + "sha256:721c76e84fe669be19c5791da68232ca2e05ba5185575086e384352e2c309597", + "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b", + "sha256:75d10d37a47afee94919c4fab4c22b9bc2a8bf7d4f46f87363bcf0573f3ff4f5", + "sha256:76af085e67e56c8816c3ccf256ebd136def2ed9654525348cfa744b6802b69eb", + "sha256:770cab594ecf99ae64c236bc9ee3439c3f46be49796e265ce0cc8bc17b10294f", + "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0", + "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941", + "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0", + "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86", + "sha256:8272b73e1c5603666618805fe821edba66892e2870058c94c53147602eab29c7", + "sha256:82d8fd25b7f4675d0c47cf95b594d4e7b158aca33b76aa63d07186e13c0e0ab7", + "sha256:844da2b5728b5ce0e32d863af26f32b5ce61bc4273a9c720a9f3aa9df73b1455", + "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6", + "sha256:915f3849a011c1f593ab99092f3cecfcb4d65d8feb4a64cf1bf2d22074dc0ec4", + "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0", + "sha256:982bb1e8b4ffda883b3d0a521e23abcd6fd17418f6d2c4118d257a10199c0ce3", + "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1", + "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6", + "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981", + "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c", + "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980", + "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645", + "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7", + "sha256:aaf27faa992bfee0264dc1f03f4c75e9fcdda66a519db6b957a3f826e285cf12", + "sha256:b2680962a4848b3c4f155dc2ee64505a9c57186d0d56b43123b17ca3de18f0fa", + "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd", + "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef", + "sha256:b3daeac64d5b371dea99714f08ffc2c208522ec6b06fbc7866a450dd446f5c0f", + "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2", + "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d", + "sha256:c72fbbe68c6f32f251bdc08b8611c7b3060612236e960ef848e0a517ddbe76c5", + "sha256:c9e36a97bee9b86ef9a1cf7bb96747eb7a15c2f22bdb5b516434b00f2a599f02", + "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3", + "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd", + "sha256:d11b54acf878eef558599658b0ffca78138c8c3655cf4f3a4a673c437e67732e", + "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214", + "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd", + "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a", + "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c", + "sha256:dc7039885fa1baf9be153a0626e337aa7ec8bf96b0128605fb0d77788ddc1681", + "sha256:dccab8d5fa1ef9bfba0590ecf4d46df048d18ffe3eec01eeb73a42e0d9e7a8ba", + "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f", + "sha256:e45ba65510e2647721e35323d6ef54c7974959f6081b58d4ef5d87c60c84919a", + "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28", + "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691", + "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82", + "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a", + "sha256:e8323a9b031aa0393768b87f04b4164a40037fb2a3c11ac06a03ffecd3618027", + "sha256:e92fca20c46e9f5e1bb485887d074918b13543b1c2a1185e69bb8d17ab6236a7", + "sha256:eb30abc20df9ab0814b5a2524f23d75dcf83cde762c161917a2b4b7b55b1e518", + "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf", + "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b", + "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9", + "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544", + "sha256:f4074c5a429281bf056ddd4c5d3b740ebca4d43ffffe2ef4bf4d2d05114299da", + "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509", + "sha256:fb707f3e15060adf5b7ada797624a6c6e0138e2a26baa089df64c68ee98e040f", + "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a", + "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f" ], "markers": "python_version >= '3.7'", - "version": "==3.4.1" + "version": "==3.4.2" }, "click": { "hashes": [ - "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", - "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a" + "sha256:27c491cc05d968d271d5a1db13e3b5a184636d9d930f148c50b038f0d0646202", + "sha256:61a3265b914e850b85317d0b3109c7f8cd35a670f963866005d6ef1d5175a12b" ], - "markers": "python_version >= '3.7'", - "version": "==8.1.8" + "markers": "python_version >= '3.10'", + "version": "==8.2.1" }, "colorama": { "hashes": [ @@ -1265,28 +1417,28 @@ }, "distlib": { "hashes": [ - "sha256:034db59a0b96f8ca18035f36290806a9a6e6bd9d1ff91e45a7f172eb17e51784", - "sha256:1530ea13e350031b6312d8580ddb6b27a104275a31106523b8f123787f494f64" + "sha256:47f8c22fd27c27e25a65601af709b38e4f0a45ea4fc2e710f65755fa8caaaf87", + "sha256:a60f20dea646b8a33f3e7772f74dc0b2d0772d2837ee1342a00645c81edf9403" ], - "version": "==0.3.8" + "version": "==0.3.9" }, "django": { "hashes": [ - "sha256:4bd01a8c830bb77a8a3b0e7d8b25b887e536ad17a81ba2dce5476135c73312bd", - "sha256:916423499d75d62da7aa038d19aef23d23498d8df229775eb0a6309ee1013775" + "sha256:85852e517f84435e9b13421379cd6c43ef5b48a9c8b391d29a26f7900967e952", + "sha256:997ef2162d04ead6869551b22cde4e06da1f94cf595f4af3f3d3afeae1f3f6fe" ], "index": "pypi", "markers": "python_version >= '3.10'", - "version": "==5.0.4" + "version": "==5.2.2" }, "django-debug-toolbar": { "hashes": [ - "sha256:0b0dddee5ea29b9cb678593bc0d7a6d76b21d7799cb68e091a2148341a80f3c4", - "sha256:e09b7dcb8417b743234dfc57c95a7c1d1d87a88844abd13b4c5387f807b31bf6" + "sha256:15627f4c2836a9099d795e271e38e8cf5204ccd79d5dbcd748f8a6c284dcd195", + "sha256:9e7f0145e1a1b7d78fcc3b53798686170a5b472d9cf085d88121ff823e900821" ], "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==4.3.0" + "markers": "python_version >= '3.9'", + "version": "==5.2.0" }, "docutils": { "hashes": [ @@ -1298,27 +1450,27 @@ }, "filelock": { "hashes": [ - "sha256:43339835842f110ca7ae60f1e1c160714c5a6afd15a2873419ab185334975c0f", - "sha256:6ea72da3be9b8c82afd3edcf99f2fffbb5076335a5ae4d03248bb5b6c3eae78a" + "sha256:adbc88eabb99d2fec8c9c1b229b171f18afa655400173ddc653d5d01501fb9f2", + "sha256:c401f4f8377c4464e6db25fff06205fd89bdd83b65eb0488ed1b160f780e21de" ], - "markers": "python_version >= '3.8'", - "version": "==3.14.0" + "markers": "python_version >= '3.9'", + "version": "==3.18.0" }, "h11": { "hashes": [ - "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", - "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761" + "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", + "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86" ], - "markers": "python_version >= '3.7'", - "version": "==0.14.0" + "markers": "python_version >= '3.8'", + "version": "==0.16.0" }, "identify": { "hashes": [ - "sha256:37d93f380f4de590500d9dba7db359d0d3da95ffe7f9de1753faa159e71e7dfa", - "sha256:e5e00f54165f9047fbebeb4a560f9acfb8af4c88232be60a488e9b68d122745d" + "sha256:ad9672d5a72e0d2ff7c5c8809b62dfa60458626352fb0eb7b55e69bdc45334a2", + "sha256:d8de45749f1efb108badef65ee8386f0f7bb19a7f26185f74de6367bffbaf0e6" ], - "markers": "python_version >= '3.8'", - "version": "==2.5.36" + "markers": "python_version >= '3.9'", + "version": "==2.6.12" }, "idna": { "hashes": [ @@ -1413,36 +1565,36 @@ }, "nodeenv": { "hashes": [ - "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2", - "sha256:df865724bb3c3adc86b3876fa209771517b0cfe596beff01a92700e0e8be4cec" + "sha256:6ec12890a2dab7946721edbfbcd91f3319c6ccc9aec47be7c7e6b7011ee6645f", + "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6'", - "version": "==1.8.0" + "version": "==1.9.1" }, "packaging": { "hashes": [ - "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", - "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f" + "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", + "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f" ], "markers": "python_version >= '3.8'", - "version": "==24.2" + "version": "==25.0" }, "platformdirs": { "hashes": [ - "sha256:031cd18d4ec63ec53e82dceaac0417d218a6863f7745dfcc9efe7793b7039bdf", - "sha256:17d5a1161b3fd67b390023cb2d3b026bbd40abde6fdb052dfbd3a29c3ba22ee1" + "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc", + "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4" ], - "markers": "python_version >= '3.8'", - "version": "==4.2.1" + "markers": "python_version >= '3.9'", + "version": "==4.3.8" }, "pre-commit": { "hashes": [ - "sha256:5eae9e10c2b5ac51577c3452ec0a490455c45a0533f7960f993a0d01e59decab", - "sha256:e209d61b8acdcf742404408531f0c37d49d2c734fd7cff2d6076083d191cb060" + "sha256:601283b9757afd87d40c4c4a9b2b5de9637a8ea02eaff7adc2d0fb4e04841146", + "sha256:a009ca7205f1eb497d10b845e52c838a98b6cdd2102a6c8e4540e94ee75c58bd" ], "index": "pypi", "markers": "python_version >= '3.9'", - "version": "==3.7.0" + "version": "==4.2.0" }, "pygments": { "hashes": [ @@ -1454,68 +1606,71 @@ }, "pyyaml": { "hashes": [ - "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5", - "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc", - "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df", - "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741", - "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206", - "sha256:18aeb1bf9a78867dc38b259769503436b7c72f7a1f1f4c93ff9a17de54319b27", - "sha256:1d4c7e777c441b20e32f52bd377e0c409713e8bb1386e1099c2415f26e479595", - "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62", - "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98", - "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696", - "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290", - "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9", - "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d", - "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6", - "sha256:4fb147e7a67ef577a588a0e2c17b6db51dda102c71de36f8549b6816a96e1867", - "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47", - "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486", - "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6", - "sha256:596106435fa6ad000c2991a98fa58eeb8656ef2325d7e158344fb33864ed87e3", - "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007", - "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938", - "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0", - "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c", - "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735", - "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d", - "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28", - "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4", - "sha256:9046c58c4395dff28dd494285c82ba00b546adfc7ef001486fbf0324bc174fba", - "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8", - "sha256:a08c6f0fe150303c1c6b71ebcd7213c2858041a7e01975da3a99aed1e7a378ef", - "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5", - "sha256:afd7e57eddb1a54f0f1a974bc4391af8bcce0b444685d936840f125cf046d5bd", - "sha256:b1275ad35a5d18c62a7220633c913e1b42d44b46ee12554e5fd39c70a243d6a3", - "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0", - "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515", - "sha256:baa90d3f661d43131ca170712d903e6295d1f7a0f595074f151c0aed377c9b9c", - "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c", - "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924", - "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34", - "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43", - "sha256:c8098ddcc2a85b61647b2590f825f3db38891662cfc2fc776415143f599bb859", - "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673", - "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54", - "sha256:d858aa552c999bc8a8d57426ed01e40bef403cd8ccdd0fc5f6f04a00414cac2a", - "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b", - "sha256:f003ed9ad21d6a4713f0a9b5a7a0a79e08dd0f221aff4525a2be4c346ee60aab", - "sha256:f22ac1c3cac4dbc50079e965eba2c1058622631e526bd9afd45fedd49ba781fa", - "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c", - "sha256:fca0e3a251908a499833aa292323f32437106001d436eca0e6e7833256674585", - "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d", - "sha256:fd66fc5d0da6d9815ba2cebeb4205f95818ff4b79c3ebe268e75d961704af52f" + "sha256:01179a4a8559ab5de078078f37e5c1a30d76bb88519906844fd7bdea1b7729ff", + "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", + "sha256:0a9a2848a5b7feac301353437eb7d5957887edbf81d56e903999a75a3d743086", + "sha256:0b69e4ce7a131fe56b7e4d770c67429700908fc0752af059838b1cfb41960e4e", + "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", + "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", + "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", + "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", + "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", + "sha256:23502f431948090f597378482b4812b0caae32c22213aecf3b55325e049a6c68", + "sha256:24471b829b3bf607e04e88d79542a9d48bb037c2267d7927a874e6c205ca7e9a", + "sha256:29717114e51c84ddfba879543fb232a6ed60086602313ca38cce623c1d62cfbf", + "sha256:2e99c6826ffa974fe6e27cdb5ed0021786b03fc98e5ee3c5bfe1fd5015f42b99", + "sha256:39693e1f8320ae4f43943590b49779ffb98acb81f788220ea932a6b6c51004d8", + "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", + "sha256:3b1fdb9dc17f5a7677423d508ab4f243a726dea51fa5e70992e59a7411c89d19", + "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", + "sha256:43fa96a3ca0d6b1812e01ced1044a003533c47f6ee8aca31724f78e93ccc089a", + "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", + "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", + "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", + "sha256:6395c297d42274772abc367baaa79683958044e5d3835486c16da75d2a694631", + "sha256:688ba32a1cffef67fd2e9398a2efebaea461578b0923624778664cc1c914db5d", + "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", + "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", + "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", + "sha256:7c36280e6fb8385e520936c3cb3b8042851904eba0e58d277dca80a5cfed590b", + "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", + "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", + "sha256:82d09873e40955485746739bcb8b4586983670466c23382c19cffecbf1fd8706", + "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", + "sha256:8824b5a04a04a047e72eea5cec3bc266db09e35de6bdfe34c9436ac5ee27d237", + "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", + "sha256:9056c1ecd25795207ad294bcf39f2db3d845767be0ea6e6a34d856f006006083", + "sha256:936d68689298c36b53b29f23c6dbb74de12b4ac12ca6cfe0e047bedceea56180", + "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", + "sha256:a4d3091415f010369ae4ed1fc6b79def9416358877534caf6a0fdd2146c87a3e", + "sha256:a8786accb172bd8afb8be14490a16625cbc387036876ab6ba70912730faf8e1f", + "sha256:a9f8c2e67970f13b16084e04f134610fd1d374bf477b17ec1599185cf611d725", + "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", + "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", + "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", + "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", + "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", + "sha256:d7fded462629cfa4b685c5416b949ebad6cec74af5e2d42905d41e257e0869f5", + "sha256:d84a1718ee396f54f3a086ea0a66d8e552b2ab2017ef8b420e92edbc841c352d", + "sha256:d8e03406cac8513435335dbab54c0d385e4a49e4945d2909a581c83647ca0290", + "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", + "sha256:ec031d5d2feb36d1d1a24380e4db6d43695f3748343d99434e6f5f9156aaa2ed", + "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", + "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", + "sha256:f753120cb8181e736c57ef7636e83f31b9c0d1722c516f7e86cf15b7aa57ff12", + "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4" ], - "markers": "python_version >= '3.6'", - "version": "==6.0.1" + "markers": "python_version >= '3.8'", + "version": "==6.0.2" }, "requests": { "hashes": [ - "sha256:55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760", - "sha256:70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6" + "sha256:27babd3cda2a6d50b30443204ee89830707d396671944c998b5975b031ac2b2c", + "sha256:27d0316682c8a29834d3264820024b62a36942083d52caf2f14c0591336d3422" ], + "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==2.32.3" + "version": "==2.32.4" }, "roman-numerals-py": { "hashes": [ @@ -1535,11 +1690,11 @@ }, "setuptools": { "hashes": [ - "sha256:18fd474d4a82a5f83dac888df697af65afa82dec7323d09c3e37d1f14288da54", - "sha256:3e386e96793c8702ae83d17b853fb93d3e09ef82ec62722e61da5cd22376dcd8" + "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", + "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c" ], "markers": "python_version >= '3.9'", - "version": "==78.1.0" + "version": "==80.9.0" }, "sniffio": { "hashes": [ @@ -1551,10 +1706,11 @@ }, "snowballstemmer": { "hashes": [ - "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1", - "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a" + "sha256:6cd7b3897da8d6c9ffb968a6781fa6532dce9c3618a4b127d920dab764a19064", + "sha256:6d5eeeec8e9f84d4d56b847692bacf79bc2c8e90c7f80ca4444ff8b6f2e52895" ], - "version": "==2.2.0" + "markers": "python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==3.0.1" }, "sphinx": { "hashes": [ @@ -1650,27 +1806,27 @@ }, "sqlparse": { "hashes": [ - "sha256:714d0a4932c059d16189f58ef5411ec2287a4360f17cdd0edd2d09d4c5087c93", - "sha256:c204494cd97479d0e39f28c93d46c0b2d5959c7b9ab904762ea6c7af211c8663" + "sha256:09f67787f56a0b16ecdbde1bfc7f5d9c3371ca683cfeaa8e6ff60b4807ec9272", + "sha256:cf2196ed3418f3ba5de6af7e82c694a9fbdbfecccdfc72e281548517081f16ca" ], "markers": "python_version >= '3.8'", - "version": "==0.5.0" + "version": "==0.5.3" }, "starlette": { "hashes": [ - "sha256:3c88d58ee4bd1bb807c0d1acb381838afc7752f9ddaec81bbe4383611d833230", - "sha256:77c74ed9d2720138b25875133f3a2dae6d854af2ec37dceb56aef370c1d8a227" + "sha256:1f64887e94a447fed5f23309fb6890ef23349b7e478faa7b24a851cd4eb844af", + "sha256:9d052d4933683af40ffd47c7465433570b4949dc937e20ad1d73b34e72f10c37" ], "markers": "python_version >= '3.9'", - "version": "==0.46.1" + "version": "==0.47.0" }, "typing-extensions": { "hashes": [ - "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", - "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef" + "sha256:8676b788e32f02ab42d9e7c61324048ae4c6d844a399eebace3d4979d75ceef4", + "sha256:a1514509136dd0b477638fc68d6a91497af5076466ad0fa6c338e44e359944af" ], - "markers": "python_version < '3.13'", - "version": "==4.13.2" + "markers": "python_version >= '3.9'", + "version": "==4.14.0" }, "urllib3": { "hashes": [ @@ -1682,19 +1838,19 @@ }, "uvicorn": { "hashes": [ - "sha256:023dc038422502fa28a09c7a30bf2b6991512da7dcdb8fd35fe57cfc154126f4", - "sha256:404051050cd7e905de2c9a7e61790943440b3416f49cb409f965d9dcd0fa73e9" + "sha256:16246631db62bdfbf069b0645177d6e8a77ba950cfedbfd093acef9444e4d885", + "sha256:35919a9a979d7a59334b6b10e05d77c1d0d574c50e0fc98b8b1a0f165708b55a" ], "markers": "python_version >= '3.9'", - "version": "==0.34.0" + "version": "==0.34.3" }, "virtualenv": { "hashes": [ - "sha256:604bfdceaeece392802e6ae48e69cec49168b9c5f4a44e483963f9242eb0e78b", - "sha256:7aa9982a728ae5892558bff6a2839c00b9ed145523ece2274fad6f414690ae75" + "sha256:36efd0d9650ee985f0cad72065001e66d49a6f24eb44d98980f630686243cf11", + "sha256:e10c0a9d02835e592521be48b332b6caee6887f332c111aa79a09b9e79efc2af" ], - "markers": "python_version >= '3.7'", - "version": "==20.26.1" + "markers": "python_version >= '3.8'", + "version": "==20.31.2" }, "watchfiles": { "hashes": [ diff --git a/tabbycat/adjfeedback/models.py b/tabbycat/adjfeedback/models.py index df6371f43f3..ac1cd2716e6 100644 --- a/tabbycat/adjfeedback/models.py +++ b/tabbycat/adjfeedback/models.py @@ -7,7 +7,7 @@ from adjallocation.models import DebateAdjudicator from registration.models import Answer, Question -from results.models import Submission +from results.submission_model import Submission from utils.models import UniqueConstraint diff --git a/tabbycat/options/preferences.py b/tabbycat/options/preferences.py index 5f4edf491bb..4420888954a 100644 --- a/tabbycat/options/preferences.py +++ b/tabbycat/options/preferences.py @@ -9,6 +9,7 @@ from dynamic_preferences.types import (BooleanPreference, ChoicePreference, DecimalPreference, FloatPreference, IntegerPreference, LongStringPreference, MultipleChoicePreference, StringPreference) +from registration.types import Currency from standings.speakers import SpeakerStandingsGenerator from standings.teams import TeamStandingsGenerator from tournaments.utils import get_side_name_choices @@ -1765,3 +1766,56 @@ class TeamRegisterMessage(LongStringPreference): default = "" widget = SummernoteWidget(attrs={'height': 150, 'class': 'form-summernote'}) field_kwargs = {'required': False} + + +@tournament_preferences_registry.register +class MissingAdjudicatorRule(ChoicePreference): + help_text = _("If charging a missing adjudicator fee per institution, how to determine the fee") + verbose_name = _("Missing adjudicator fee rule") + section = registration + name = 'missing_adj_rule' + choices = ( + ('', _("None")), + ('N', _("N=N (Equal number of teams and adjudicators)")), + ('N-1', _("N-1 (One fewer adjudicators required than teams)")), + ('N/2', _("N/2 (Require half the adjudicators to the number of teams, rounded down)")), + ) + default = '' + functions = { + '': lambda a, t: 0, + 'N': lambda a, t: t - a, + 'N-1': lambda a, t: t - a - 1, + 'N/2': lambda a, t: t // 2 - a, + } + + +@tournament_preferences_registry.register +class BillingStandard(ChoicePreference): + help_text = _("How should institutions be billed for their participants") + verbose_name = _("Institutional billing standard") + section = registration + name = 'inst_billing_standard' + choices = ( + ('active_participants', _("Bill based on the number of participants registered")), + ('allocated', _("Bill based on the number of participants allocated")), + ) + default = 'active_participants' + + +@tournament_preferences_registry.register +class BillingCurrency(ChoicePreference): + help_text = _("In which currency should items be charged?") + verbose_name = _("Billing currency") + section = registration + name = 'billing_currency' + choices = Currency.choices + default = Currency.cad + + +@tournament_preferences_registry.register +class AllowPartialPayments(BooleanPreference): + help_text = _("Whether people paying an invoice can input how much they'll pay at a time") + verbose_name = _("Allow partial payments") + section = registration + name = 'partial_payments' + default = False diff --git a/tabbycat/participants/migrations/0028_alter_tournamentinstitution_adjudicators_requested_and_more.py b/tabbycat/participants/migrations/0028_alter_tournamentinstitution_adjudicators_requested_and_more.py index cc1649b9b5a..fbbdfc7a79c 100644 --- a/tabbycat/participants/migrations/0028_alter_tournamentinstitution_adjudicators_requested_and_more.py +++ b/tabbycat/participants/migrations/0028_alter_tournamentinstitution_adjudicators_requested_and_more.py @@ -13,7 +13,7 @@ class Migration(migrations.Migration): migrations.AddField( model_name='person', name='last_name', - field=models.CharField(help_text='The last name may be used for team names or code names', max_length=70, null=True, verbose_name='last name'), + field=models.CharField(blank=True, help_text='The last name may be used for team names or code names', max_length=70, null=True, verbose_name='last name'), ), migrations.AlterField( model_name='tournamentinstitution', diff --git a/tabbycat/registration/forms.py b/tabbycat/registration/forms.py index 1c3237176db..7fbf7a1bb30 100644 --- a/tabbycat/registration/forms.py +++ b/tabbycat/registration/forms.py @@ -1,14 +1,18 @@ import random +from math import log10 from django import forms from django.utils.text import capfirst from django.utils.translation import gettext_lazy as _ +from options.preferences import MissingAdjudicatorRule from participants.emoji import EMOJI_RANDOM_FIELD_CHOICES, pick_unused_emoji from participants.models import Adjudicator, Coach, Institution, Speaker, Team, TournamentInstitution from privateurls.utils import populate_url_keys from .form_utils import CustomQuestionsFormMixin +from .models import LineItem, PriceItem +from .payment_utils import add_item class TournamentInstitutionForm(CustomQuestionsFormMixin, forms.ModelForm): @@ -260,8 +264,62 @@ def _create_and_initialise_fields(self): def save(self): qs = self.tournament.tournamentinstitution_set.select_related('institution').all() + + items = self.tournament.priceitem_set.filter(active=True, item_type__in=[ + PriceItem.ItemType.PARTICIPANT, PriceItem.ItemType.TEAM, PriceItem.ItemType.SPEAKER, + PriceItem.ItemType.ADJUDICATOR, PriceItem.ItemType.MISSING_ADJ, + ]) + new_cart_items = [] for t_inst in qs: institution = t_inst.institution + + if self.tournament.pref('inst_billing_standard') == 'allocated': + n_adjs = self.cleaned_data[self._fieldname_adjs_allocated(institution)] - t_inst.adjudicators_allocated + n_teams = self.cleaned_data[self._fieldname_teams_allocated(institution)] - t_inst.teams_allocated + + missing_adj_rule = MissingAdjudicatorRule.functions[self.tournament.pref('missing_adj_rule')] + before_missing = max(missing_adj_rule(t_inst.adjudicators_allocated, t_inst.teams_allocated), 0) + after_missing = missing_adj_rule(self.cleaned_data[self._fieldname_adjs_allocated(institution)], self.cleaned_data[self._fieldname_teams_allocated(institution)]) + new_missing = max(after_missing - before_missing, 0) + + cart_items = [ + ci for ci in + [ + add_item(items, PriceItem.ItemType.ADJUDICATOR, n_adjs), + add_item(items, PriceItem.ItemType.TEAM, n_teams), + add_item(items, PriceItem.ItemType.SPEAKER, n_teams * self.tournament.pref('speakers_in_team')), + add_item(items, PriceItem.ItemType.PARTICIPANT, n_adjs + n_teams * self.tournament.pref('speakers_in_team')), + add_item(items, PriceItem.ItemType.MISSING_ADJ, new_missing), + ] + if ci is not None and ci.quantity + ] + for ci in cart_items: + ci.institution = institution + new_cart_items.extend(cart_items) + t_inst.teams_allocated = self.cleaned_data[self._fieldname_teams_allocated(institution)] t_inst.adjudicators_allocated = self.cleaned_data[self._fieldname_adjs_allocated(institution)] TournamentInstitution.objects.bulk_update(qs, ['teams_allocated', 'adjudicators_allocated']) + LineItem.objects.bulk_create(new_cart_items) + + +class PriceItemForm(forms.ModelForm): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + if self.instance.pk: + self.fields['item_type'].disabled = True + + +class CreatePaymentForm(forms.Form): + def __init__(self, currency, max_value, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fields['amount'] = forms.DecimalField(min_value=0, max_value=max_value, decimal_places=int(log10(currency.minor_unit))) + + +class FinishPayPalIntegrationForm(forms.Form): + tracking_id = forms.CharField(widget=forms.HiddenInput(), required=True) + merchant_id = forms.CharField(widget=forms.HiddenInput(), required=True) + + +class PayPalCaptureOrderForm(forms.Form): + order_id = forms.CharField() diff --git a/tabbycat/registration/migrations/0003_alter_question_answer_type_discount_invoice_and_more.py b/tabbycat/registration/migrations/0003_alter_question_answer_type_discount_invoice_and_more.py new file mode 100644 index 00000000000..33240cff586 --- /dev/null +++ b/tabbycat/registration/migrations/0003_alter_question_answer_type_discount_invoice_and_more.py @@ -0,0 +1,205 @@ +# Generated by Django 5.2.2 on 2025-09-30 14:50 + +import django.db.models.deletion +import registration.models +import utils.models +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + ('participants', '0028_alter_tournamentinstitution_adjudicators_requested_and_more'), + ('registration', '0002_invitation_and_more'), + ('tournaments', '0013_scheduleevent'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AlterField( + model_name='question', + name='answer_type', + field=models.CharField(choices=[('bc', 'checkbox'), ('bs', 'yes/no (dropdown)'), ('i', 'integer (textbox)'), ('is', 'integer scale'), ('f', 'float'), ('t', 'text'), ('tl', 'long text'), ('ss', 'select one'), ('ms', 'select multiple'), ('dt', 'date + time')], max_length=2, verbose_name='answer type'), + ), + migrations.CreateModel( + name='Discount', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('timestamp', models.DateTimeField(auto_now_add=True, verbose_name='timestamp')), + ('version', models.PositiveIntegerField(verbose_name='version')), + ('submitter_type', models.CharField(choices=[('T', 'Tab room'), ('P', 'Public'), ('A', 'Automation')], max_length=1, verbose_name='submitter type')), + ('confirmed', models.BooleanField(default=False, verbose_name='confirmed')), + ('private_url', models.BooleanField(default=False, verbose_name='from private URL')), + ('confirm_timestamp', models.DateTimeField(blank=True, null=True, verbose_name='confirm timestamp')), + ('ip_address', models.GenericIPAddressField(blank=True, null=True, verbose_name='IP address')), + ('amount', registration.models.ArbitraryDecimalField()), + ('currency', models.CharField(choices=[('cad', 'Canadian Dollar'), ('eur', 'Euro'), ('gbp', 'British Pound'), ('usd', 'United States Dollar')])), + ('note', models.TextField(blank=True)), + ('confirmer', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='%(app_label)s_%(class)s_confirmed', to=settings.AUTH_USER_MODEL, verbose_name='confirmer')), + ('institution', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='participants.institution')), + ('participant_submitter', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='%(app_label)s_%(class)s_participant_submitted', to='participants.person', verbose_name='from participant')), + ('person', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='participants.person')), + ('submitter', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='%(app_label)s_%(class)s_submitted', to=settings.AUTH_USER_MODEL, verbose_name='submitter')), + ('team', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='participants.team')), + ('tournament', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='tournaments.tournament')), + ], + options={ + 'verbose_name': 'discount', + 'verbose_name_plural': 'discounts', + }, + ), + migrations.CreateModel( + name='Invoice', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('amount', registration.models.ArbitraryDecimalField()), + ('currency', models.CharField(choices=[('cad', 'Canadian Dollar'), ('eur', 'Euro'), ('gbp', 'British Pound'), ('usd', 'United States Dollar')])), + ('date_posted', models.DateTimeField(auto_now_add=True)), + ('note', models.TextField()), + ('from_registration', models.BooleanField(default=True)), + ('status', models.CharField(choices=[('v', 'Void'), ('o', 'Open'), ('p', 'Paid')], max_length=1)), + ('amount_paid', registration.models.ArbitraryDecimalField()), + ('institution', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='participants.institution')), + ('person', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='participants.person')), + ('team', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='participants.team')), + ('tournament', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='tournaments.tournament')), + ], + options={ + 'verbose_name': 'invoice', + 'verbose_name_plural': 'invoices', + }, + ), + migrations.CreateModel( + name='InvoicePayment', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('amount', registration.models.ArbitraryDecimalField()), + ('currency', models.CharField(choices=[('cad', 'Canadian Dollar'), ('eur', 'Euro'), ('gbp', 'British Pound'), ('usd', 'United States Dollar')])), + ('invoice', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='registration.invoice')), + ], + options={ + 'verbose_name': 'invoice payment', + 'verbose_name_plural': 'invoice payments', + }, + ), + migrations.CreateModel( + name='LineDiscount', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('discount', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='registration.discount')), + ('invoice', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='registration.invoice')), + ], + ), + migrations.CreateModel( + name='Payment', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('version', models.PositiveIntegerField(verbose_name='version')), + ('submitter_type', models.CharField(choices=[('T', 'Tab room'), ('P', 'Public'), ('A', 'Automation')], max_length=1, verbose_name='submitter type')), + ('confirmed', models.BooleanField(default=False, verbose_name='confirmed')), + ('private_url', models.BooleanField(default=False, verbose_name='from private URL')), + ('confirm_timestamp', models.DateTimeField(blank=True, null=True, verbose_name='confirm timestamp')), + ('ip_address', models.GenericIPAddressField(blank=True, null=True, verbose_name='IP address')), + ('reference', models.CharField()), + ('timestamp', models.DateTimeField(auto_now_add=True)), + ('amount', registration.models.ArbitraryDecimalField()), + ('currency', models.CharField(choices=[('cad', 'Canadian Dollar'), ('eur', 'Euro'), ('gbp', 'British Pound'), ('usd', 'United States Dollar')])), + ('method', models.CharField(choices=[('c', 'cash'), ('w', 'wire'), ('', 'other'), ('s', 'Stripe')], max_length=1)), + ('status', models.CharField(choices=[('c', 'Completed'), ('r', 'Refunded'), ('f', 'Failed'), ('h', 'On hold'), ('e', 'Expired')], max_length=1)), + ('confirmer', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='%(app_label)s_%(class)s_confirmed', to=settings.AUTH_USER_MODEL, verbose_name='confirmer')), + ('institution', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='participants.institution')), + ('invoices', models.ManyToManyField(through='registration.InvoicePayment', to='registration.invoice')), + ('participant_submitter', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='%(app_label)s_%(class)s_participant_submitted', to='participants.person', verbose_name='from participant')), + ('person', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='participants.person')), + ('submitter', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='%(app_label)s_%(class)s_submitted', to=settings.AUTH_USER_MODEL, verbose_name='submitter')), + ('team', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='participants.team')), + ('tournament', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='tournaments.tournament')), + ], + options={ + 'verbose_name': 'payment', + 'verbose_name_plural': 'payments', + }, + ), + migrations.AddField( + model_name='invoicepayment', + name='payment', + field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='registration.payment'), + ), + migrations.CreateModel( + name='PaymentScheduleEvent', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('due_date', models.DateTimeField(verbose_name='due date')), + ('percentage_required', models.FloatField(help_text='The cumulative percentage of the invoice to be paid by the time', verbose_name='percentage required')), + ('tournament', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='tournaments.tournament', verbose_name='tournament')), + ], + options={ + 'verbose_name': 'payment schedule event', + 'verbose_name_plural': 'payment schedule events', + }, + ), + migrations.CreateModel( + name='PriceItem', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('item_type', models.CharField(blank=True, choices=[('p', 'participant registration'), ('a', 'adjudicator registration'), ('t', 'team registration'), ('s', 'speaker registration'), ('o', 'observer registration'), ('m', 'missing adjudicator fee'), ('', 'custom fee')], help_text='If an option is selected, the item will be auto-populated in invoices where applicable', max_length=1, null=True, verbose_name='item type')), + ('name', models.CharField(blank=True, help_text="Optional; overrides the default name from the item's type", max_length=100, verbose_name='name')), + ('amount', registration.models.ArbitraryDecimalField(verbose_name='amount')), + ('currency', models.CharField(choices=[('cad', 'Canadian Dollar'), ('eur', 'Euro'), ('gbp', 'British Pound'), ('usd', 'United States Dollar')], max_length=3, verbose_name='currency')), + ('stripe_product_id', models.CharField(blank=True, null=True, unique=True, verbose_name='Stripe product ID')), + ('stripe_price_id', models.CharField(blank=True, null=True, unique=True, verbose_name='Stripe price ID')), + ('active', models.BooleanField(blank=True, default=True, help_text='Should the item be applied to future invoices?', verbose_name='active')), + ('tournament', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='tournaments.tournament', verbose_name='tournament')), + ], + options={ + 'verbose_name': 'item', + 'verbose_name_plural': 'items', + }, + ), + migrations.CreateModel( + name='LineItem', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('quantity', models.PositiveIntegerField()), + ('note', models.TextField()), + ('date_added', models.DateTimeField(auto_now_add=True)), + ('institution', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='participants.institution')), + ('invoice', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='registration.invoice')), + ('person', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='participants.person')), + ('team', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='participants.team')), + ('item', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='registration.priceitem')), + ], + ), + migrations.CreateModel( + name='SlotTransfer', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('quantity', models.PositiveIntegerField(default=1)), + ('for_content_type', models.ForeignKey(limit_choices_to=models.Q(models.Q(('app_label', 'adjfeedback'), ('model', 'adjudicatorfeedback')), models.Q(('app_label', 'participants'), ('model', 'tournamentinstitution')), models.Q(('app_label', 'participants'), ('model', 'speaker')), models.Q(('app_label', 'participants'), ('model', 'adjudicator')), models.Q(('app_label', 'participants'), ('model', 'coach')), models.Q(('app_label', 'participants'), ('model', 'person')), models.Q(('app_label', 'participants'), ('model', 'team')), _connector='OR'), on_delete=django.db.models.deletion.PROTECT, to='contenttypes.contenttype', verbose_name='for content type')), + ('from_institution', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='from_institution_set', to='participants.institution', verbose_name='from institution')), + ('to_institution', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='to_institution_set', to='participants.institution', verbose_name='to institution')), + ('tournament', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='tournaments.tournament', verbose_name='tournament')), + ], + options={ + 'verbose_name': 'slot transfer', + 'verbose_name_plural': 'slot transfers', + }, + ), + migrations.CreateModel( + name='PaymentConnection', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('platform', models.CharField(choices=[('s', 'Stripe'), ('p', 'PayPal')], max_length=1)), + ('external_id', models.CharField()), + ('status', models.CharField(choices=[('c', 'connected'), ('d', 'disconnected'), ('p', 'pending')], max_length=1)), + ('tournament', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='tournaments.tournament')), + ], + options={ + 'verbose_name': 'payment connection', + 'verbose_name_plural': 'payment connections', + 'constraints': [utils.models.UniqueConstraint(fields=('tournament', 'platform'), name='registr_paymentconnection_tournament__platform_uniq')], + }, + ), + ] diff --git a/tabbycat/registration/models.py b/tabbycat/registration/models.py index abc4f2025c5..b1874f9ec60 100644 --- a/tabbycat/registration/models.py +++ b/tabbycat/registration/models.py @@ -6,8 +6,11 @@ from django.utils.translation import gettext_lazy as _ from django_better_admin_arrayfield.models.fields import ArrayField +from results.submission_model import Submission from utils.models import UniqueConstraint +from .types import Currency + CONTENT_TYPE_CHOICES = models.Q(app_label='adjfeedback', model='adjudicatorfeedback') | \ models.Q(app_label='participants', model='tournamentinstitution') | \ @@ -154,3 +157,201 @@ class Meta: def __str__(self): return '%s: %s invitation (%s)' % (self.tournament.name, self.for_content_type, self.team or self.institution) + + +class ArbitraryDecimalField(models.DecimalField): + def _check_decimal_places(self, **kwargs): + return [] + + def _check_max_digits(self, **kwargs): + return [] + + def _check_decimal_places_and_max_digits(self, **kwargs): + return [] + + def db_type(self, connection): + # pg or bust + assert connection.settings_dict["ENGINE"] == "django.db.backends.postgresql" + return "numeric" + + +class PaymentConnection(models.Model): + class Platform(models.TextChoices): + STRIPE = 's', "Stripe" + PAYPAL = 'p', "PayPal" + + class Status(models.TextChoices): + CONNECTED = 'c', _("connected") + DISCONNECTED = 'd', _("disconnected") + PENDING = 'p', _("pending") + + tournament = models.ForeignKey('tournaments.Tournament', models.CASCADE) + platform = models.CharField(max_length=1, choices=Platform.choices) + external_id = models.CharField() + status = models.CharField(max_length=1, choices=Status.choices) + + class Meta: + verbose_name = _("payment connection") + verbose_name_plural = _("payment connections") + constraints = [UniqueConstraint(fields=['tournament', 'platform'])] + + +class PriceItem(models.Model): + + class ItemType(models.TextChoices): + PARTICIPANT = 'p', _("participant registration") + ADJUDICATOR = 'a', _("adjudicator registration") + TEAM = 't', _("team registration") + SPEAKER = 's', _("speaker registration") + OBSERVER = 'o', _("observer registration") + MISSING_ADJ = 'm', _("missing adjudicator fee") + OTHER = '', _("custom fee") + + tournament = models.ForeignKey('tournaments.Tournament', models.CASCADE, verbose_name=_("tournament")) + item_type = models.CharField(max_length=1, choices=ItemType.choices, null=True, blank=True, + verbose_name=_("item type"), + help_text=_("If an option is selected, the item will be auto-populated in invoices where applicable")) + name = models.CharField(max_length=100, blank=True, + verbose_name=_("name"), + help_text=_("Optional; overrides the default name from the item's type")) + amount = ArbitraryDecimalField(verbose_name=_("amount")) + currency = models.CharField(max_length=3, choices=Currency.choices, verbose_name=_("currency")) + stripe_product_id = models.CharField(null=True, blank=True, unique=True, verbose_name=_("Stripe product ID")) + stripe_price_id = models.CharField(null=True, blank=True, unique=True, verbose_name=_("Stripe price ID")) + active = models.BooleanField(default=True, blank=True, verbose_name=_("active"), help_text=_("Should the item be applied to future invoices?")) + + class Meta: + verbose_name = _("item") + verbose_name_plural = _("items") + + +class PaymentScheduleEvent(models.Model): + tournament = models.ForeignKey('tournaments.Tournament', models.CASCADE, + verbose_name=_("tournament")) + due_date = models.DateTimeField(auto_now=False, verbose_name=_("due date")) + percentage_required = models.FloatField( + verbose_name=_("percentage required"), + help_text=_("The cumulative percentage of the invoice to be paid by the time")) + + class Meta: + verbose_name = _("payment schedule event") + verbose_name_plural = _("payment schedule events") + + +class Invoice(models.Model): + class Status(models.TextChoices): + VOID = 'v', _("Void") + OPEN = 'o', _("Open") + PAID = 'p', _("Paid") + + amount = ArbitraryDecimalField() + currency = models.CharField(choices=Currency.choices) + tournament = models.ForeignKey('tournaments.Tournament', models.PROTECT) + date_posted = models.DateTimeField(auto_now_add=True) + note = models.TextField() + from_registration = models.BooleanField(default=True) + + status = models.CharField(max_length=1, choices=Status.choices) + amount_paid = ArbitraryDecimalField() + + institution = models.ForeignKey('participants.Institution', models.SET_NULL, null=True, blank=True) + team = models.ForeignKey('participants.Team', models.SET_NULL, null=True, blank=True) + person = models.ForeignKey('participants.Person', models.SET_NULL, null=True, blank=True) + + class Meta: + verbose_name = _("invoice") + verbose_name_plural = _("invoices") + + +class LineItem(models.Model): + invoice = models.ForeignKey(Invoice, models.CASCADE, null=True, blank=True) + item = models.ForeignKey(PriceItem, models.PROTECT) + quantity = models.PositiveIntegerField() + note = models.TextField() + + date_added = models.DateTimeField(auto_now_add=True) + + institution = models.ForeignKey('participants.Institution', models.SET_NULL, null=True, blank=True) + team = models.ForeignKey('participants.Team', models.SET_NULL, null=True, blank=True) + person = models.ForeignKey('participants.Person', models.SET_NULL, null=True, blank=True) + + +class Payment(Submission): + class Method(models.TextChoices): + CASH = 'c', _("cash") + WIRE = 'w', _("wire") + OTHER = '', _("other") + STRIPE = 's', "Stripe" + PAYPAL = 'p', "PayPal" + + class Status(models.TextChoices): + COMPLETED = 'c', _("Completed") + REFUNDED = 'r', _("Refunded") + FAILED = 'f', _("Failed") + ON_HOLD = 'h', _("On hold") + EXPIRED = 'e', _("Expired") + PENDING = 'p', _("Pending") + + reference = models.CharField() + timestamp = models.DateTimeField(auto_now_add=True) + amount = ArbitraryDecimalField() + tournament = models.ForeignKey('tournaments.Tournament', models.PROTECT) + currency = models.CharField(choices=Currency.choices) + method = models.CharField(max_length=1, choices=Method.choices) + status = models.CharField(max_length=1, choices=Status.choices) + + institution = models.ForeignKey('participants.Institution', models.SET_NULL, null=True, blank=True) + team = models.ForeignKey('participants.Team', models.SET_NULL, null=True, blank=True) + person = models.ForeignKey('participants.Person', models.SET_NULL, null=True, blank=True) + + invoices = models.ManyToManyField(Invoice, through='InvoicePayment') + + class Meta: + verbose_name = _("payment") + verbose_name_plural = _("payments") + constraints = [] + + +class InvoicePayment(models.Model): + invoice = models.ForeignKey(Invoice, models.PROTECT) + payment = models.ForeignKey(Payment, models.PROTECT) + amount = ArbitraryDecimalField() + currency = models.CharField(choices=Currency.choices) + + class Meta: + verbose_name = _("invoice payment") + verbose_name_plural = _("invoice payments") + + +class Discount(Submission): + tournament = models.ForeignKey('tournaments.Tournament', models.PROTECT) + amount = ArbitraryDecimalField() + currency = models.CharField(choices=Currency.choices) + note = models.TextField(blank=True) + + institution = models.ForeignKey('participants.Institution', models.SET_NULL, null=True, blank=True) + team = models.ForeignKey('participants.Team', models.SET_NULL, null=True, blank=True) + person = models.ForeignKey('participants.Person', models.SET_NULL, null=True, blank=True) + + class Meta: + verbose_name = _("discount") + verbose_name_plural = _("discounts") + + +class LineDiscount(models.Model): + invoice = models.ForeignKey(Invoice, models.CASCADE) + discount = models.ForeignKey(Discount, models.PROTECT) + + +class SlotTransfer(models.Model): + tournament = models.ForeignKey('tournaments.Tournament', models.CASCADE, verbose_name=_("tournament")) + from_institution = models.ForeignKey('participants.Institution', models.CASCADE, related_name="from_institution_set", verbose_name=_("from institution")) + to_institution = models.ForeignKey('participants.Institution', models.CASCADE, related_name='to_institution_set', verbose_name=_("to institution")) + for_content_type = models.ForeignKey(ContentType, models.PROTECT, + limit_choices_to=CONTENT_TYPE_CHOICES, + verbose_name=_("for content type")) + quantity = models.PositiveIntegerField(default=1) + + class Meta: + verbose_name = _("slot transfer") + verbose_name_plural = _("slot transfers") diff --git a/tabbycat/registration/payment_utils.py b/tabbycat/registration/payment_utils.py new file mode 100644 index 00000000000..cee5f1c3909 --- /dev/null +++ b/tabbycat/registration/payment_utils.py @@ -0,0 +1,71 @@ +import json +from base64 import b64encode +from decimal import Decimal, ROUND_DOWN +from typing import Optional + +import requests +from django.conf import settings +from requests.auth import HTTPBasicAuth + +from .models import LineItem, PriceItem + + +def add_item(items: list[PriceItem], item_type: PriceItem.ItemType, quantity: int) -> Optional[LineItem]: + if ( + item := next((i for i in items if i.item_type == item_type), None) + ) and quantity > 0: + return LineItem(item=item, quantity=quantity) + + +def get_application_fee(amount, currency): + # settings.PAYMENT_FEE_PERCENTAGE + return Decimal(amount * Decimal('0.025') + Decimal(settings.PAYMENT_FEE_FIXED[currency])).quantize(Decimal(1) / Decimal(currency.minor_unit), rounding=ROUND_DOWN) + + +def generate_paypal_access_token(): + basic = HTTPBasicAuth(settings.PAYPAL_CLIENT_ID, settings.PAYPAL_CLIENT_SECRET) + req = requests.post('https://api-m.sandbox.paypal.com/v1/oauth2/token', auth=basic, data={'grant_type': 'client_credentials'}) + return req.json()['access_token'] + + +def generate_auth_assertion(merchant_id): + def b64_dict(d): + return b64encode(json.dumps(d).encode('utf-8')) + return b64_dict({"alg": "none"}) + b'.' + b64_dict({"iss": settings.PAYPAL_CLIENT_ID, "payer_id": merchant_id}) + b'.' + + +def generate_paypal_order(connection, amount, line_items, payment): + merchant_id = connection.external_id + req = requests.post('https://api-m.sandbox.paypal.com/v2/checkout/orders', headers={ + 'Authorization': f'Bearer {generate_paypal_access_token()}', + 'PayPal-Partner-Attribution-Id': settings.PAYPAL_BUILD_NOTATION_CODE, + 'PayPal-Auth-Assertion': generate_auth_assertion(merchant_id), + }, json={ + 'intent': 'CAPTURE', + 'purchase_units': [{ + 'custom_id': str(payment.id), + 'invoice_id': str(payment.invoices[0].id) if len(payment.invoices) == 1 else None, + 'items': [ + {'name': li.item.name, 'quantity': li.quantity, 'unit_amount': {'currency_code': payment.currency, 'value': li.item.amount}} + for li in line_items + ], + 'amount': { + 'currency_code': payment.currency, + 'value': str(payment.amount), + }, + 'payment_instruction': { + 'platform_fees': [{'amount': {'currency': payment.currency, 'value': str(get_application_fee(payment.amount, payment.currency))}}], + }, + }], + }) + return req.json() + + +def complete_paypal_order(connection, order_id): + merchant_id = connection.external_id + req = requests.post(f'https://api-m.sandbox.paypal.com/v2/checkout/orders/{order_id}/capture', headers={ + 'Content-Type': 'application/json', + 'Authorization': f'Bearer {generate_paypal_access_token()}', + 'PayPal-Auth-Assertion': generate_auth_assertion(merchant_id), + }) + return req.json() diff --git a/tabbycat/registration/paypal.py b/tabbycat/registration/paypal.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tabbycat/registration/templates/account_balance.html b/tabbycat/registration/templates/account_balance.html new file mode 100644 index 00000000000..1fb05582eae --- /dev/null +++ b/tabbycat/registration/templates/account_balance.html @@ -0,0 +1,26 @@ +{% extends "tables/base_vue_table.html" %} +{% load debate_tags i18n %} + +{% block content %} + +{{ block.super }} + +
+ {% csrf_token %} + + {% include "components/form-main.html" %} + +
+
+ +
+
+
+{% endblock %} + +{% block js %} + {{ block.super }} + +{% endblock js %} diff --git a/tabbycat/registration/templates/answer_tables/payments.html b/tabbycat/registration/templates/answer_tables/payments.html new file mode 100644 index 00000000000..02cf1441d02 --- /dev/null +++ b/tabbycat/registration/templates/answer_tables/payments.html @@ -0,0 +1,28 @@ +{% extends "tables/base_vue_table.html" %} +{% load debate_tags i18n %} + +{% block page-subnav-sections %} +
+ + {% trans "Connections" %} + +
+{% endblock %} +{% block page-subnav-actions %} +
+
{% trans "Create" %}
+ + {% trans "Products" %} + + + {% trans "Discount" %} + + + {% trans "Invoice" %} + + + {% trans "Payment" %} + +
+{% endblock %} diff --git a/tabbycat/registration/templates/coach_private_url.html b/tabbycat/registration/templates/coach_private_url.html index ad9be0b9a59..5d21b0e0963 100644 --- a/tabbycat/registration/templates/coach_private_url.html +++ b/tabbycat/registration/templates/coach_private_url.html @@ -62,13 +62,11 @@ - {% if False %} -
- {% tournamenturl 'reg-inst-create-team' url_key as url %} - {% blocktrans trimmed asvar text %}View invoice{% endblocktrans %} - {% include "components/item-action.html" with icon="dollar-sign" %} -
- {% endif %} +
+ {% tournamenturl 'reg-inst-invoice-balance' url_key as url %} + {% blocktrans trimmed asvar text %}View invoice{% endblocktrans %} + {% include "components/item-action.html" with icon="dollar-sign" %} +
{{ block.super }} {# this is the Vue table, which is populated with the registered participants #} diff --git a/tabbycat/registration/templates/payment_connections.html b/tabbycat/registration/templates/payment_connections.html new file mode 100644 index 00000000000..cfdc1ebb0d6 --- /dev/null +++ b/tabbycat/registration/templates/payment_connections.html @@ -0,0 +1,26 @@ +{% extends "base.html" %} +{% load add_field_css debate_tags i18n %} + +{% block content %} + +
+ {% csrf_token %} + + +
+
+ + {% if has_stripe_connection %} +
+ {% trans "Stripe account already linked." %} +
+ {% else %} + {% trans "Connect Stripe" as title %} + {% include "components/form-submit.html" %} + {% endif %} + +
+
+
+ +{% endblock content %} diff --git a/tabbycat/registration/templates/products_edit.html b/tabbycat/registration/templates/products_edit.html new file mode 100644 index 00000000000..72ea5935960 --- /dev/null +++ b/tabbycat/registration/templates/products_edit.html @@ -0,0 +1,12 @@ +{% extends "base.html" %} +{% load add_field_css debate_tags i18n %} + +{% block head-title %}🏷️ {% trans "Products" %}{% endblock %} +{% block page-title %}🏷️{% trans "Products" %}{% endblock %} + +{% block content %} + + {% trans "Save Products" as save_text %} + {% include "components/formset.html" with double=True %} + +{% endblock content %} diff --git a/tabbycat/registration/types.py b/tabbycat/registration/types.py new file mode 100644 index 00000000000..819d2908528 --- /dev/null +++ b/tabbycat/registration/types.py @@ -0,0 +1,20 @@ +from django.db.models import TextChoices +from django.utils.translation import gettext_lazy as _ + + +class Currency(TextChoices): + cad = 'cad', _("Canadian Dollar") + eur = 'eur', _("Euro") + gbp = 'gbp', _("British Pound") + usd = 'usd', _("United States Dollar") + + @property + def minor_unit(self): + units = { + 100: (Currency.cad, Currency.eur, Currency.gbp, Currency.usd), + } + for val, currencies in units.items(): + if self in currencies: + return val + else: + return ValueError diff --git a/tabbycat/registration/urls_admin.py b/tabbycat/registration/urls_admin.py index 8296d625244..07d95f4d74b 100644 --- a/tabbycat/registration/urls_admin.py +++ b/tabbycat/registration/urls_admin.py @@ -30,4 +30,32 @@ path('speakers/questions/', views.CustomQuestionFormsetView.as_view(question_model=Speaker, success_url='reg-team-list'), name='reg-speaker-questions'), + path('payments/', include([ + path('', views.AdminPaymentsIndexView.as_view(), name='reg-payments-admin-list'), + path('connect/', include([ + path('', views.PaymentConnectionView.as_view(), name='reg-payments-connect'), + path('stripe/', include([ + path('refresh/', views.StripeAccountRefreshView.as_view(), name='stripe-connect-refresh'), + path('return/', views.StripeAccountReturnView.as_view(), name='stripe-connect-return'), + ])), + path('paypal', include([ + path('return/', views.PayPalConnectReturnView.as_view(), name='paypal-connect-return'), + ])), + ])), + path('products/', views.CreateProductsView.as_view(), name='reg-products-edit'), + path('discount/', views.CreateDiscountView.as_view(), name='reg-create-discount'), + path('invoice/', include([ + path('new/', views.CreateInvoiceView.as_view(), name='reg-create-invoice'), + path('/', views.AdminInvoiceView.as_view(), name='reg-admin-invoice-view'), + ])), + path('payment/', include([ + path('new/', views.CreatePaymentView.as_view(), name='reg-create-payment'), + path('/', views.AdminPaymentView.as_view(), name='reg-admin-payment-view'), + ])), + path('balance/', include([ + path('institution//', views.AdminInstitutionBalanceSummaryView.as_view(), name='reg-admin-institution-balance'), + path('team//', views.AdminTeamBalanceSummaryView.as_view(), name='reg-admin-team-balance'), + path('person//', views.AdminPersonBalanceSummaryView.as_view(), name='reg-admin-person-balance'), + ])), + ])), ] diff --git a/tabbycat/registration/urls_global.py b/tabbycat/registration/urls_global.py new file mode 100644 index 00000000000..eb48657843b --- /dev/null +++ b/tabbycat/registration/urls_global.py @@ -0,0 +1,12 @@ +from django.urls import include, path +from django.views.decorators.csrf import csrf_exempt + +from . import views + + +urlpatterns = [ + path('webhooks/', include([ + path('paypal/', csrf_exempt(views.PayPalWebhookView.as_view()), name='paypal-webhook'), + path('stripe/', csrf_exempt(views.StripeWebhookView.as_view()), name='stripe-webhook'), + ])), +] diff --git a/tabbycat/registration/urls_public.py b/tabbycat/registration/urls_public.py index 494e60c48cc..3522b35e00e 100644 --- a/tabbycat/registration/urls_public.py +++ b/tabbycat/registration/urls_public.py @@ -17,6 +17,12 @@ path('team/', views.InstitutionalCreateTeamFormView.as_view(), name='reg-inst-create-team'), + path('balance/', + views.InstitutionBalanceSummaryView.as_view(), + name='reg-inst-invoice-balance'), + path('invoices//', + views.InstitutionInvoiceView.as_view(), + name='reg-inst-invoice'), ])), ])), path('adjudicator/', @@ -30,4 +36,13 @@ views.CreateSpeakerFormView.as_view(), name='reg-create-speaker'), ])), + path('payments/', include([ + path('paypal/', include([ + path('capture/', views.PayPalCaptureOrderView.as_view(), name='reg-paypal-capture'), + ])), + path('/', include([ + path('balance/', views.PrivateUrlBalanceSummaryView.as_view(), name='private-url-payment-balance'), + path('invoices//', views.PrivateUrlInvoiceView.as_view(), name='private-url-invoice'), + ])), + ])), ] diff --git a/tabbycat/registration/views.py b/tabbycat/registration/views.py index ee3515c8dcf..e040c545df0 100644 --- a/tabbycat/registration/views.py +++ b/tabbycat/registration/views.py @@ -1,29 +1,48 @@ +import base64 +import logging +import zlib +from decimal import Decimal + +import requests +import stripe +from cryptography import x509 +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import hashes +from cryptography.hazmat.primitives.asymmetric import padding +from django.conf import settings from django.contrib import messages from django.contrib.contenttypes.models import ContentType from django.contrib.postgres.forms import SimpleArrayField -from django.db.models import Count, Prefetch, Sum +from django.core.cache import cache +from django.db.models import Count, Prefetch, Q, Sum from django.forms import HiddenInput, modelformset_factory -from django.http import HttpResponseRedirect +from django.http import HttpResponse, HttpResponseRedirect, JsonResponse from django.shortcuts import get_object_or_404 from django.utils.translation import gettext as _, gettext_lazy, ngettext -from django.views.generic.edit import FormView +from django.views.generic.base import TemplateView, View +from django.views.generic.edit import CreateView, FormView from formtools.wizard.views import SessionWizardView from actionlog.mixins import LogActionMixin from actionlog.models import ActionLogEntry from participants.emoji import EMOJI_NAMES -from participants.models import Adjudicator, Coach, Speaker, Team, TournamentInstitution +from participants.models import Adjudicator, Coach, Person, Speaker, Team, TournamentInstitution from tournaments.mixins import PublicTournamentPageMixin, TournamentMixin from users.permissions import Permission -from utils.misc import reverse_tournament +from utils.misc import redirect_tournament, reverse_tournament from utils.mixins import AdministratorMixin from utils.tables import TabbycatTableBuilder from utils.views import ModelFormSetView, VueTableTemplateView -from .forms import AdjudicatorForm, InstitutionCoachForm, ParticipantAllocationForm, SpeakerForm, TeamForm, TournamentInstitutionForm -from .models import Invitation, Question +from .forms import (AdjudicatorForm, CreatePaymentForm, FinishPayPalIntegrationForm, InstitutionCoachForm, + ParticipantAllocationForm, PayPalCaptureOrderForm, PriceItemForm, SpeakerForm, TeamForm, TournamentInstitutionForm) +from .models import Discount, Invitation, Invoice, InvoicePayment, LineItem, Payment, PaymentConnection, PriceItem, Question +from .payment_utils import add_item, complete_paypal_order, generate_paypal_order, get_application_fee +from .types import Currency from .utils import populate_invitation_url_keys +logger = logging.getLogger(__name__) + class CustomQuestionFormMixin: @@ -173,6 +192,26 @@ def get_form(self, step=None, data=None, files=None): form.max_num = self.tournament.pref('speakers_in_team') return form + def populate_cart_object(self, cart_item): + cart_item.team = self.object + + def add_to_cart(self, team, speakers): + items = self.tournament.priceitem_set.filter(active=True, item_type__in=[PriceItem.ItemType.PARTICIPANT, PriceItem.ItemType.TEAM, PriceItem.ItemType.SPEAKER]) + + cart_items = [ + ci for ci in + [ + add_item(items, PriceItem.ItemType.PARTICIPANT, len(speakers)), + add_item(items, PriceItem.ItemType.TEAM, 1), + add_item(items, PriceItem.ItemType.SPEAKER, len(speakers)), + ] + if ci is not None and ci.quantity + ] + for ci in cart_items: + self.populate_cart_object(ci) + + LineItem.objects.bulk_create(cart_items) + def done(self, form_list, form_dict, **kwargs): team = form_dict['team'].save() if self.tournament.pref('team_name_generator') != 'user': @@ -202,6 +241,8 @@ def done(self, form_list, form_dict, **kwargs): len(self.speakers), ) % {'num': len(self.speakers), 'link': invite_url}) + self.add_to_cart(team, form_dict['speaker']) + messages.success(self.request, _("Your team %s has been registered!") % team.short_name) self.log_action() return HttpResponseRedirect(self.get_success_url()) @@ -301,9 +342,29 @@ def get_success_url(self): def form_valid(self, form): self.object = form.save() + self.add_to_cart(self.object) messages.success(self.request, _("You have been registered as an adjudicator!")) return super().form_valid(form) + def populate_cart_object(self, cart_item, adjudicator): + cart_item.person = adjudicator + + def add_to_cart(self, adjudicator): + items = self.tournament.priceitem_set.filter(active=True, item_type__in=[PriceItem.ItemType.PARTICIPANT, PriceItem.ItemType.ADJUDICATOR]) + + cart_items = [ + ci for ci in + [ + add_item(items, PriceItem.ItemType.PARTICIPANT, 1), + add_item(items, PriceItem.ItemType.ADJUDICATOR, 1), + ] + if ci is not None + ] + for ci in cart_items: + self.populate_cart_object(ci, adjudicator) + + LineItem.objects.bulk_create(cart_items) + class PublicCreateAdjudicatorFormView(BaseCreateAdjudicatorFormView): @@ -385,6 +446,30 @@ def form_valid(self, form): team.save() return super().form_valid(form) + def populate_cart_object(self, cart_item, speaker): + if speaker.team.institution is not None: + if self.tournament.pref('inst_billing_standard') == 'active_participants': + cart_item.institution = speaker.team.institution + else: + cart_item.team = speaker.team + cart_item.person = speaker + + def add_to_cart(self, speaker): + items = self.tournament.priceitem_set.filter(active=True, item_type__in=[PriceItem.ItemType.PARTICIPANT, PriceItem.ItemType.SPEAKER]) + + cart_items = [ + ci for ci in + [ + add_item(items, PriceItem.ItemType.PARTICIPANT, 1), + add_item(items, PriceItem.ItemType.SPEAKER, 1), + ] + if ci is not None + ] + for ci in cart_items: + self.populate_cart_object(ci, speaker) + + LineItem.objects.bulk_create(cart_items) + class InstitutionalLandingPageView(TournamentMixin, InstitutionalRegistrationMixin, VueTableTemplateView): @@ -431,10 +516,27 @@ def get_form_kwargs(self, step=None): kwargs.pop('institution') return kwargs + def populate_cart_object(self, cart_item): + cart_item.institution = self.institution + + def add_to_cart(self, team, speakers): + if self.tournament.pref('inst_billing_standard') == 'active_participants': + return super().add_to_cart(team, speakers) + class InstitutionalCreateAdjudicatorFormView(InstitutionalRegistrationMixin, BaseCreateAdjudicatorFormView): public_page_preference = 'institution_participant_registration' + def get_page_subtitle(self): + return _("from %s") % self.institution.name + + def populate_cart_object(self, cart_item, adjudicator): + cart_item.team = self.institution + + def add_to_cart(self, adjudicator): + if self.tournament.pref('inst_billing_standard') == 'active_participants': + return super().add_to_cart(adjudicator) + def handle_question_columns(table: TabbycatTableBuilder, objects, questions=None, suffix=0) -> None: if questions is None: @@ -632,3 +734,558 @@ def formset_valid(self, formset): def get_success_url(self, *args, **kwargs): return reverse_tournament(self.success_url, self.tournament) + + +class BasePaymentsIndexView(TournamentMixin, VueTableTemplateView): + page_emoji = '💰' + page_title = gettext_lazy("Payment Status") + + def get_table(self): + table = TabbycatTableBuilder(view=self, title=_("Payment Summary")) + return table + + +class AdminPaymentsIndexView(AdministratorMixin, BasePaymentsIndexView): + template_name = 'answer_tables/payments.html' + + def get_inst_table(self): + table = TabbycatTableBuilder(view=self, title=_("Institution Payments")) + return table + + def get_team_table(self): + table = TabbycatTableBuilder(view=self, title=_("Team Payments")) + return table + + def get_adj_table(self): + table = TabbycatTableBuilder(view=self, title=_("Adjudicator Payments")) + return table + + def get_tables(self): + return [self.get_inst_table(), self.get_adj_table(), self.get_team_table()] + + +class CreateProductsView(TournamentMixin, AdministratorMixin, ModelFormSetView): + template_name = 'products_edit.html' + + formset_model = PriceItem + formset_factory_kwargs = { + 'fields': ('tournament', 'item_type', 'name', 'amount', 'active'), + 'extra': 2, + 'widgets': { + 'tournament': HiddenInput(), + }, + 'form': PriceItemForm, + } + + url_name = 'reg-products-edit' + success_url = 'reg-payments-admin-list' + + def get_formset_queryset(self): + return super().get_formset_queryset().filter(tournament=self.tournament) + + def get_formset_kwargs(self): + return { + 'initial': [{'tournament': self.tournament}] * self.formset_factory_kwargs['extra'], + } + + def formset_valid(self, formset): + items = formset.save(commit=False) + + stripe_connection = self.tournament.paymentconnection_set.filter(platform=PaymentConnection.Platform.STRIPE).first() + + for item, fields in formset.changed_objects: + if stripe_connection: + new_data = { + 'name': item.name or item.get_item_type_display().capitalize(), + } + if 'amount' in fields: + new_price = stripe.Price.create( + product=item.stripe_product_id, + currency=item.currency.lower(), + unit_amount=item.amount * Currency[item.currency].minor_unit, + api_key=settings.STRIPE_PRIVATE_API_KEY, + stripe_account=stripe_connection.external_id, + ) + new_data['default_price'] = new_price['id'] + item.stripe_price_id = new_price['id'] + + stripe.Product.modify( + item.stripe_product_id, + **new_data, + api_key=settings.STRIPE_PRIVATE_API_KEY, + stripe_account=stripe_connection.external_id, + ) + item.save() + + for item in formset.new_objects: + item.tournament = self.tournament # Even with the tournament in the form, avoid it being changed + item.currency = self.tournament.pref('billing_currency') + + if stripe_connection: + stripe_product = stripe.Product.create( + name=item.name or item.get_item_type_display().capitalize(), + default_price_data={ + 'currency': item.currency.lower(), + 'unit_amount': item.amount * Currency[item.currency].minor_unit, + }, + api_key=settings.STRIPE_PRIVATE_API_KEY, + stripe_account=stripe_connection.external_id, + ) + item.stripe_product_id = stripe_product['id'] + item.stripe_price_id = stripe_product['default_price'] + item.save() + + if items: + message = ngettext("Saved item: %(list)s", + "Saved items: %(list)s", + len(items), + ) % {'list': ", ".join(item.name or item.get_item_type_display().capitalize() for item in items)} + messages.success(self.request, message) + else: + messages.success(self.request, _("No changes were made to the items.")) + if "add_more" in self.request.POST: + return redirect_tournament(self.url_name, self.tournament) + return HttpResponseRedirect(self.get_success_url()) + + def get_success_url(self, *args, **kwargs): + return reverse_tournament(self.success_url, self.tournament) + + +class PaymentConnectionView(TournamentMixin, AdministratorMixin, TemplateView): + template_name = 'payment_connections.html' + + def get_context_data(self, **kwargs): + kwargs['has_stripe_connection'] = self.tournament.paymentconnection_set.filter(platform=PaymentConnection.Platform.STRIPE).exists() + return super().get_context_data(**kwargs) + + def post(self, request, *args, **kwargs): + match request.POST.get('platform'): + case 'stripe': + return self.connect_stripe() + case 'paypal': + return self.connect_paypal() + + def connect_stripe(self): + if not (connection := self.tournament.paymentconnection_set.filter(platform=PaymentConnection.Platform.STRIPE).first()): + account = stripe.Account.create(api_key=settings.STRIPE_PRIVATE_API_KEY) + connection = PaymentConnection(tournament=self.tournament, platform=PaymentConnection.Platform.STRIPE, external_id=account['id'], status=PaymentConnection.Status.PENDING) + connection.save() + + account_link = stripe.AccountLink.create( + api_key=settings.STRIPE_PRIVATE_API_KEY, + account=connection.external_id, + refresh_url=self.request.build_absolute_uri(reverse_tournament('stripe-connect-refresh', self.tournament)), + return_url=self.request.build_absolute_uri(reverse_tournament('stripe-connect-return', self.tournament)), + type="account_onboarding", + ) + return HttpResponseRedirect(account_link['url']) + + def connect_paypal(self): + if not (connection := self.tournament.paymentconnection_set.filter(platform=PaymentConnection.Platform.PAYPAL).first()): + connection = PaymentConnection( + tournament=self.tournament, + platform=PaymentConnection.Platform.PAYPAL, + external_id=f'temp_pp_{self.tournament.id}', + status=PaymentConnection.Status.PENDING, + ) + connection.save() + + req = requests.post('https://api-m.sandbox.paypal.com/v2/customer/partner-referrals', headers={'Authorization': 'Bearer x'}, json={ + "tracking_id": str(connection.id), + "operations": [{ + "operation": "API_INTEGRATION", + "api_integration_preference": { + "rest_api_integration": { + "integration_method": "PAYPAL", + "integration_type": "THIRD_PARTY", + "third_party_details": { + "features": [ + "PAYMENT", + "REFUND", + ], + }, + }, + }, + }], + "products": [ + "EXPRESS_CHECKOUT", + ], + "legal_consents": [{ + "type": "SHARE_DATA_CONSENT", + "granted": True, + }], + "partner_config_override": { + 'return_url': self.request.build_absolute_uri(reverse_tournament('paypal-connect-return', self.tournament)), + }, + }) + return HttpResponseRedirect(next(link['url'] for link in req.json()['links'] if link['rel'] == 'action_url')) + + +class StripeAccountRefreshView(TournamentMixin, AdministratorMixin, View): + def get(self, request, *args, **kwargs): + messages.error(request, _("Your Stripe could not be linked. Please try again.")) + return HttpResponseRedirect(reverse_tournament('reg-payments-connect', self.tournament)) + + +class StripeAccountReturnView(TournamentMixin, AdministratorMixin, View): + def get(self, request, *args, **kwargs): + messages.success(request, _("Your Stripe account has been linked")) + return HttpResponseRedirect(reverse_tournament('reg-payments-admin-list', self.tournament)) + + +class PayPalConnectReturnView(TournamentMixin, AdministratorMixin, FormView): + form_class = FinishPayPalIntegrationForm + + def get_initial(self): + initial = super().get_initial() + initial['tracking_id'] = self.request.GET.get('merchantId') + initial['merchant_id'] = self.request.GET.get('merchantIdInPayPal') + return initial + + def get_success_url(self): + return reverse_tournament('reg-payments-admin-list', self.tournament) + + def form_valid(self, form): + connection = PaymentConnection.objects.get(platform=PaymentConnection.Platform.PAYPAL, id=form.cleaned_data['tracking_id']) + connection.status = PaymentConnection.Status.CONNECTED + connection.external_id = form.cleaned_data['merchant_id'] + connection.save() + + messages.success(self.request, _("Your PayPal account has been linked")) + return super.form_valid(form) + + +class StripeWebhookView(View): + def post(self, request, *args, **kwargs): + event = None + account = request.headers['STRIPE_ACCOUNT'] + + try: + event = stripe.Webhook.construct_event( + request.body, + request.headers['STRIPE_SIGNATURE'], + settings.STRIPE_WEBHOOK_SECRET, + api_key=settings.STRIPE_PRIVATE_API_KEY, + stripe_account=account, + ) + except ValueError: + return HttpResponse(status=400) + except stripe.error.SignatureVerificationError: + return HttpResponse(status=400) + + # Handle the event + match event.type: + case 'account.updated': + connections = PaymentConnection.objects.filter(platform=PaymentConnection.Platform.STRIPE, external_id=event.data.object['id']) + if len(event.data.object['requirements']['past_due']) > 1: + connections.update(status=PaymentConnection.Status.DISCONNECTED) + else: + connections.update(status=PaymentConnection.Status.CONNECTED) + case 'checkout.session.completed': + pass + case 'checkout.session.expired': + pass + case _: + print('Unhandled event type {}'.format(event['type'])) + + return HttpResponse(status=200) + + +class PayPalWebhookView(View): + def post(self, request, *args, **kwargs): + # data = request.data + + if not PayPalWebhookView.verify_signature(request): + return HttpResponse(status=400) + + match request.data.get("event_type"): + case 'MERCHANT.ONBOARDING.COMPLETED': + self.handle_onboarding_completed(request.data) + case 'MERCHANT.PARTNER-CONSENT.REVOKED': + pass + + return HttpResponse(status=200) + + @staticmethod + def get_certificate(url): + return cache.get_or_set(url, requests.get(url).text) + + @staticmethod + def verify_signature(request): + # Create the validation message + transmission_id = request.headers.get("paypal-transmission-id") + timestamp = request.headers.get("paypal-transmission-time") + crc = zlib.crc32(request.body) + message = f"{transmission_id}|{timestamp}|{settings.PAYPAL_WEBHOOK_ID}|{crc}" + + # Decode the base64-encoded signature from the header + signature = base64.b64decode(request.headers.get("paypal-transmission-sig")) + + # Load the certificate and extract the public key + certificate = PayPalWebhookView.get_certificate(request.headers.get("paypal-cert-url")) + cert = x509.load_pem_x509_certificate(certificate.encode("utf-8"), default_backend()) + public_key = cert.public_key() + + # Validate the message using the signature + try: + public_key.verify(signature, message.encode("utf-8"), padding.PKCS1v15(), hashes.SHA256()) + return True + except Exception: + # Validation failed + return False + + def handle_onboarding_completed(self, data): + connection = PaymentConnection.objects.get(platform=PaymentConnection.Platform.PAYPAL, external_id=data['resource']['merchant_id']) + connection.status = PaymentConnection.Status.CONNECTED + connection.save() + + +class CreateInvoiceView(TournamentMixin, AdministratorMixin, CreateView): + pass + + +class BaseInvoiceView(TournamentMixin, VueTableTemplateView): + + def get_queryset(self): + return self.tournament.invoice_set.prefetch_related('lineitem_set', 'linediscount_set__discount', 'invoicepayment_set__payment') + + @property + def invoice(self): + return get_object_or_404(self.get_queryset(), pk=self.kwargs['invoice_id']) + + def get_items_table(self): + items = self.invoice.lineitem_set.all() + + table = TabbycatTableBuilder(view=self, title=_("Items")) + table.add_column({'key': 'item', 'title': _("Item")}, [li.item.name for li in items]) + table.add_column({'key': 'quantity', 'title': _("Quantity")}, [li.quantity for li in items]) + table.add_column({'key': 'price', 'title': _("Price")}, [li.item.amount * li.quantity for li in items]) + return table + + def get_discounts_table(self): + discounts = self.invoice.linediscount_set.all() + + table = TabbycatTableBuilder(view=self, title=_("Discounts")) + table.add_column({'key': 'note', 'title': _("Note")}, [d.note for d in discounts]) + table.add_column({'key': 'amount', 'title': _("Amount")}, [d.amount for d in discounts]) + return table + + def get_payments_table(self): + payments = self.invoice.invoicepayment_set.all() + + table = TabbycatTableBuilder(view=self, title=_("Payments")) + table.add_column({'key': 'timestamp', 'title': _("Date")}, [p.payment.timestamp for p in payments]) + table.add_column({'key': 'reference', 'title': _("Reference")}, [p.payment.reference for p in payments]) + table.add_column({'key': 'status', 'title': _("Status")}, [p.payment.get_status_display() for p in payments]) + table.add_column({'key': 'amount', 'title': _("Amount")}, [p.amount for p in payments]) + return table + + def get_tables(self): + [self.get_items_table(), self.get_discounts_table(), self.get_payments_table()] + + +class InstitutionInvoiceView(InstitutionalRegistrationMixin, BaseInvoiceView): + def get_queryset(self): + return super().get_queryset().filter(institution=self.institution) + + +class PrivateUrlInvoiceView(BaseInvoiceView): + def get_queryset(self): + return super().get_queryset().filter(Q(team__speaker__url_key=self.kwargs['url_key']) | Q(person__url_key=self.kwargs['url_key'])) + + +class AdminInvoiceView(AdministratorMixin, BaseInvoiceView): + view_permission = Permission.VIEW_INVOICES + + +class BasePaymentView(TournamentMixin, VueTableTemplateView): + def get_table(self): + table = TabbycatTableBuilder(view=self, title=_("Invoices")) + return table + + +class AdminPaymentView(AdministratorMixin, BasePaymentView): + view_permission = Permission.VIEW_PAYMENTS + + +class CreateDiscountView(TournamentMixin, AdministratorMixin, CreateView): + model = Discount + + +class CreatePaymentView(TournamentMixin, AdministratorMixin, CreateView): + model = Payment + + +class BaseBalanceSummaryView(TournamentMixin, VueTableTemplateView, FormView): + template_name = 'account_balance.html' + form_class = CreatePaymentForm + + tables_orientation = 'rows' + + def dispatch(self, request, *args, **kwargs): + self.line_items = LineItem.objects.filter(item__tournament=self.tournament, **self.participant_kwargs) + self.invoices = self.tournament.invoice_set.filter(**self.participant_kwargs) + self.discounts = self.tournament.discount_set.filter(**self.participant_kwargs) + self.payments = Payment.objects.filter(tournament=self.tournament, status=Payment.Status.COMPLETED, **self.participant_kwargs) + self.line_items_total = sum([li.item.amount * li.quantity for li in self.line_items]) + self.amount_total = self.line_items_total - sum([d.amount for d in self.discounts]) - sum([p.amount for p in self.payments]) + return super().dispatch(request, *args, **kwargs) + + def get_context_data(self, **kwargs): + kwargs['total'] = self.amount_total + return super().get_context_data(**kwargs) + + def get_invoices_table(self): + table = TabbycatTableBuilder(view=self, title=_("Invoices")) + table.add_column({'key': 'name', 'title': _("ID")}, [i.pk for i in self.invoices]) + table.add_column({'key': 'status', 'title': _("Status")}, [i.get_status_display() for i in self.invoices]) + table.add_column({'key': 'total', 'title': _("Total")}, [i.amount for i in self.invoices]) + table.add_column({'key': 'due', 'title': _("Amount Due")}, [i.amount - i.amount_paid for i in self.invoices]) + return table + + def get_items_table(self): + table = TabbycatTableBuilder(view=self, title=_("Payment Summary")) + table.add_column({'key': 'item', 'title': _("Item")}, [li.item.name or li.item.get_item_type_display().capitalize() for li in self.line_items]) + table.add_column({'key': 'quantity', 'title': _("Quantity")}, [li.quantity for li in self.line_items]) + table.add_column({'key': 'invoice', 'title': _("Invoice")}, [li.invoice_id for li in self.line_items]) + table.add_column({'key': 'price', 'title': _("Price")}, [li.item.amount * li.quantity for li in self.line_items]) + return table + + def get_discounts_table(self): + table = TabbycatTableBuilder(view=self, title=_("Discounts")) + table.add_column({'key': 'name', 'title': _("Name")}, [d.note for d in self.discounts]) + table.add_column({'key': 'amount', 'title': _("Amount")}, [d.amount for d in self.discounts]) + return table + + def get_tables(self): + return [self.get_invoices_table(), self.get_items_table(), self.get_discounts_table()] + + def get_initial(self): + initial = super().get_initial() + initial['amount'] = self.amount_total + return initial + + def get_form_kwargs(self): + kwargs = super().get_form_kwargs() + kwargs['currency'] = Currency[self.tournament.pref('billing_currency')] + kwargs['max_value'] = self.amount_total + return kwargs + + def form_valid(self, form): + currency = Currency[self.tournament.pref('billing_currency')] + payment = Payment.objects.create( + reference='', + tournament=self.tournament, + amount=form.cleaned_data['amount'], + currency=currency, + method=Payment.Method.OTHER, + status=Payment.Status.PENDING, + **self.participant_kwargs, + ) + if new_invoice_items := self.line_items.filter(invoice=None): + invoice = Invoice( + amount=sum(ii.item.amount * ii.quantity for ii in new_invoice_items), + currency=currency, + tournament=self.tournament, + status=Invoice.Status.OPEN, + amount_paid=Decimal('0'), + **self.participant_kwargs, + ) + invoice.save() + InvoicePayment.objects.create(payment=payment, invoice=invoice, amount=invoice.amount, currency=currency) + new_invoice_items.update(invoice=invoice) + + payment_connections = {pc.platform: pc for pc in self.tournament.paymentconnection_set.filter(status=PaymentConnection.Status.CONNECTED)} + if PaymentConnection.Platform.STRIPE in payment_connections: + payment.method = Payment.Method.STRIPE + payment.save() + return self.create_stripe_checkout(payment_connections[PaymentConnection.Platform.STRIPE], form.cleaned_data['amount'], payment) + if PaymentConnection.Platform.PAYPAL in payment_connections: + payment.method = Payment.Method.PAYPAL + payment.save() + return self.create_paypal_checkout(payment_connections[PaymentConnection.Platform.PAYPAL], form.cleaned_data['amount'], payment) + + def create_stripe_checkout(self, connection, amount, payment): + use_currency = Currency[self.tournament.pref('billing_currency')] + discounts = [] + if amount < self.line_items_total: + coupon = stripe.Coupon.create( + name='For partial payment / discounts', + amount_off=(self.line_items_total - amount) * use_currency.minor_unit, + currency=self.tournament.pref('billing_currency'), + max_redemptions=1, + api_key=settings.STRIPE_PRIVATE_API_KEY, + stripe_account=connection.external_id, + ) + discounts.append({'coupon': coupon['id']}) + + session = stripe.checkout.Session.create( + success_url=self.request.build_absolute_uri(), + line_items=[{"price": li.item.stripe_price_id, "quantity": li.quantity} for li in self.line_items], + discounts=discounts, + mode="payment", + client_reference_id=payment.id, + payment_intent_data={'application_fee_amount': int(get_application_fee(amount, use_currency) * use_currency.minor_unit)}, + api_key=settings.STRIPE_PRIVATE_API_KEY, + stripe_account=connection.external_id, + ) + return HttpResponseRedirect(session['url']) + + def create_paypal_checkout(self, connection, amount, payment): + return JsonResponse(generate_paypal_order(connection, amount, self.line_items, payment)) + + +class InstitutionBalanceSummaryView(BaseBalanceSummaryView): + + def get_t_institution(self): + ti = TournamentInstitution.objects.filter(tournament=self.tournament, coach__url_key=self.kwargs['url_key']).select_related('institution') + return get_object_or_404(ti) + + @property + def institution(self): + return self.get_t_institution().institution + + @property + def participant_kwargs(self): + return {'institution': self.institution} + + +class AdminInstitutionBalanceSummaryView(AdministratorMixin, BaseBalanceSummaryView): + view_permission = Permission.VIEW_INVOICES + + @property + def participant_kwargs(self): + return {'institution': self.kwargs['pk']} + + +class AdminTeamBalanceSummaryView(AdministratorMixin, BaseBalanceSummaryView): + view_permission = Permission.VIEW_INVOICES + + @property + def participant_kwargs(self): + return {'team': self.kwargs['pk']} + + +class AdminPersonBalanceSummaryView(AdministratorMixin, BaseBalanceSummaryView): + view_permission = Permission.VIEW_INVOICES + + @property + def participant_kwargs(self): + return {'person': self.kwargs['pk']} + + +class PrivateUrlBalanceSummaryView(BaseBalanceSummaryView): + @property + def participant_kwargs(self): + person = Person.objects.filter(url_key=self.kwargs['url_key']).select_related('speaker__team', 'adjudicator').get() + if person.speaker is not None: + return {'team': person.speaker.team} + return {'person': person} + + +class PayPalCaptureOrderView(TournamentMixin, FormView): + form_class = PayPalCaptureOrderForm + + def form_valid(self, form): + connection = self.tournament.paymentconnection_set.filter(status=PaymentConnection.Status.CONNECTED, platform=PaymentConnection.Platform.STRIPE) + return JsonResponse(complete_paypal_order(connection, form.cleaned_data['order_id'])) diff --git a/tabbycat/results/models.py b/tabbycat/results/models.py index 6baac407a2f..bfd002ca96b 100644 --- a/tabbycat/results/models.py +++ b/tabbycat/results/models.py @@ -1,18 +1,16 @@ import logging -from threading import Lock -from django.conf import settings from django.core.exceptions import ValidationError from django.db import models from django.utils import timezone from django.utils.translation import gettext_lazy as _ from motions.models import RoundMotion -from tournaments.models import Tournament from utils.misc import badge_datetime_format, reverse_tournament from utils.models import UniqueConstraint from .result import DebateResult +from .submission_model import Submission from .utils import readable_ballotsub_result logger = logging.getLogger(__name__) @@ -22,87 +20,6 @@ class ScoreField(models.FloatField): pass -class Submission(models.Model): - """Abstract base class to provide functionality common to different - types of submissions. - - The unique_together class attribute of the Meta class MUST be set in - all subclasses.""" - - class Submitter(models.TextChoices): - TABROOM = 'T', _("Tab room") - PUBLIC = 'P', _("Public") - AUTOMATION = 'A', _("Automation") - - timestamp = models.DateTimeField(auto_now_add=True, - verbose_name=_("timestamp")) - version = models.PositiveIntegerField( - verbose_name=_("version")) - submitter_type = models.CharField(max_length=1, choices=Submitter.choices, - verbose_name=_("submitter type")) - confirmed = models.BooleanField(default=False, - verbose_name=_("confirmed")) - - # relevant for private URL submissions - private_url = models.BooleanField(default=False, - verbose_name=_("from private URL")) - participant_submitter = models.ForeignKey('participants.Person', models.PROTECT, - blank=True, null=True, related_name="%(app_label)s_%(class)s_participant_submitted", - verbose_name=_("from participant")) - - # only relevant if submitter was in tab room - submitter = models.ForeignKey(settings.AUTH_USER_MODEL, models.PROTECT, - blank=True, null=True, related_name="%(app_label)s_%(class)s_submitted", - verbose_name=_("submitter")) - confirmer = models.ForeignKey(settings.AUTH_USER_MODEL, models.PROTECT, - blank=True, null=True, related_name="%(app_label)s_%(class)s_confirmed", - verbose_name=_("confirmer")) - confirm_timestamp = models.DateTimeField(blank=True, null=True, - verbose_name=_("confirm timestamp")) - ip_address = models.GenericIPAddressField(blank=True, null=True, - verbose_name=_("IP address")) - - save_lock = Lock() - - class Meta: - abstract = True - - @property - def _unique_filter_args(self): - return dict((arg, getattr(self, arg)) for arg in self._meta.constraints[0].fields - if arg != 'version') - - def _unique_unconfirm_args(self): - return self._unique_filter_args - - def save(self, *args, **kwargs): - # Use a lock to protect against the possibility that two submissions do this - # at the same time and get the same version number or both be confirmed. - with self.save_lock: - - # Assign the version field to one more than the current maximum version. - if self.pk is None: - existing = self.__class__.objects.filter(**self._unique_filter_args) - if existing.exists(): - self.version = existing.aggregate(models.Max('version'))['version__max'] + 1 - else: - self.version = 1 - - # Check for uniqueness. - if self.confirmed: - unconfirmed = self.__class__.objects.filter(confirmed=True, - **self._unique_unconfirm_args()).exclude(pk=self.pk).update(confirmed=False) - if unconfirmed > 0: - logger.info("Unconfirmed %d %s so that %s could be confirmed", unconfirmed, self._meta.verbose_name_plural, self) - - super(Submission, self).save(*args, **kwargs) - - def clean(self): - super().clean() - if self.submitter_type == self.Submitter.TABROOM and self.submitter is None: - raise ValidationError(_("A tab room ballot must have a user associated.")) - - class BallotSubmission(Submission): """Represents a single submission of ballots for a debate. (Not a single motion, but a single submission of all ballots for a debate.)""" @@ -388,7 +305,7 @@ def clean(self): class ScoreCriterion(models.Model): """Score criterion for speaker score""" - tournament = models.ForeignKey(Tournament, models.CASCADE, + tournament = models.ForeignKey('tournaments.Tournament', models.CASCADE, verbose_name=_("tournament")) name = models.CharField(max_length=20, verbose_name=("name")) diff --git a/tabbycat/results/submission_model.py b/tabbycat/results/submission_model.py new file mode 100644 index 00000000000..830dd430326 --- /dev/null +++ b/tabbycat/results/submission_model.py @@ -0,0 +1,92 @@ +import logging +from threading import Lock + +from django.conf import settings +from django.core.exceptions import ValidationError +from django.db import models +from django.utils.translation import gettext_lazy as _ + +logger = logging.getLogger(__name__) + + +class Submission(models.Model): + """Abstract base class to provide functionality common to different + types of submissions. + + The unique_together class attribute of the Meta class MUST be set in + all subclasses.""" + + class Submitter(models.TextChoices): + TABROOM = 'T', _("Tab room") + PUBLIC = 'P', _("Public") + AUTOMATION = 'A', _("Automation") + + timestamp = models.DateTimeField(auto_now_add=True, + verbose_name=_("timestamp")) + version = models.PositiveIntegerField( + verbose_name=_("version")) + submitter_type = models.CharField(max_length=1, choices=Submitter.choices, + verbose_name=_("submitter type")) + confirmed = models.BooleanField(default=False, + verbose_name=_("confirmed")) + + # relevant for private URL submissions + private_url = models.BooleanField(default=False, + verbose_name=_("from private URL")) + participant_submitter = models.ForeignKey('participants.Person', models.PROTECT, + blank=True, null=True, related_name="%(app_label)s_%(class)s_participant_submitted", + verbose_name=_("from participant")) + + # only relevant if submitter was in tab room + submitter = models.ForeignKey(settings.AUTH_USER_MODEL, models.PROTECT, + blank=True, null=True, related_name="%(app_label)s_%(class)s_submitted", + verbose_name=_("submitter")) + confirmer = models.ForeignKey(settings.AUTH_USER_MODEL, models.PROTECT, + blank=True, null=True, related_name="%(app_label)s_%(class)s_confirmed", + verbose_name=_("confirmer")) + confirm_timestamp = models.DateTimeField(blank=True, null=True, + verbose_name=_("confirm timestamp")) + ip_address = models.GenericIPAddressField(blank=True, null=True, + verbose_name=_("IP address")) + + save_lock = Lock() + + class Meta: + abstract = True + + @property + def _unique_filter_args(self): + if not self._meta.constraints: + return {} + return dict((arg, getattr(self, arg)) for arg in self._meta.constraints[0].fields + if arg != 'version') + + def _unique_unconfirm_args(self): + return self._unique_filter_args + + def save(self, *args, **kwargs): + # Use a lock to protect against the possibility that two submissions do this + # at the same time and get the same version number or both be confirmed. + with self.save_lock: + + # Assign the version field to one more than the current maximum version. + if self.pk is None: + existing = self.__class__.objects.filter(**self._unique_filter_args) + if existing.exists(): + self.version = existing.aggregate(models.Max('version'))['version__max'] + 1 + else: + self.version = 1 + + # Check for uniqueness. + if self.confirmed: + unconfirmed = self.__class__.objects.filter(confirmed=True, + **self._unique_unconfirm_args()).exclude(pk=self.pk).update(confirmed=False) + if unconfirmed > 0: + logger.info("Unconfirmed %d %s so that %s could be confirmed", unconfirmed, self._meta.verbose_name_plural, self) + + super(Submission, self).save(*args, **kwargs) + + def clean(self): + super().clean() + if self.submitter_type == self.Submitter.TABROOM and self.submitter is None: + raise ValidationError(_("A tab room ballot must have a user associated.")) diff --git a/tabbycat/settings/core.py b/tabbycat/settings/core.py index 497f0c1ba46..953204e56e2 100644 --- a/tabbycat/settings/core.py +++ b/tabbycat/settings/core.py @@ -1,4 +1,5 @@ import os +from decimal import Decimal from django.contrib.messages import constants as messages from django.utils.translation import gettext_lazy as _ @@ -404,3 +405,22 @@ 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ] + +# ============================================================================== +# Payment processors for registration +# ============================================================================== + +STRIPE_PUBLIC_API_KEY = os.environ.get('STRIPE_PUBLIC_API_KEY') +STRIPE_PRIVATE_API_KEY = os.environ.get('STRIPE_PRIVATE_API_KEY') +STRIPE_WEBHOOK_SECRET = os.environ.get('STRIPE_WEBHOOK_SECRET') + +PAYPAL_CLIENT_ID = os.environ.get('PAYPAL_CLIENT_ID') +PAYPAL_CLIENT_SECRET = os.environ.get('PAYPAL_CLIENT_SECRET') +PAYPAL_BUILD_NOTATION_CODE = os.environ.get('PAYPAL_BN_CODE') +PAYPAL_WEBHOOK_ID = os.environ.get('PAYPAL_WEBHOOK_ID') + +PAYMENT_FEE_PERCENTAGE = Decimal('0') +PAYMENT_FEE_FIXED = { + 'cad': Decimal('0'), + 'usd': Decimal('0'), +} diff --git a/tabbycat/templates/nav/admin_nav.html b/tabbycat/templates/nav/admin_nav.html index 8f5f34f39b8..ed137177440 100644 --- a/tabbycat/templates/nav/admin_nav.html +++ b/tabbycat/templates/nav/admin_nav.html @@ -132,6 +132,10 @@ class="list-group-item" data-parent="#registrationh"> {% trans "Adjudicators" %} + + {% trans "Payments" %} +
diff --git a/tabbycat/tournaments/models.py b/tabbycat/tournaments/models.py index 7fbfadf424c..64ed9a6078b 100644 --- a/tabbycat/tournaments/models.py +++ b/tabbycat/tournaments/models.py @@ -19,7 +19,7 @@ PROHIBITED_TOURNAMENT_SLUGS = [ 'jet', 'database', 'admin', 'accounts', 'summernote', # System 'start', 'create', 'load-demo', # Setup Wizards - 'tournament', 'notifications', 'archive', 'api', # Cross-Tournament app's view roots + 'tournament', 'notifications', 'archive', 'api', 'registration', # Cross-Tournament app's view roots 'favicon.ico', 'robots.txt', # Files that must be at top level '__debug__', 'static', 'style', 'i18n', 'jsi18n'] # Misc diff --git a/tabbycat/urls.py b/tabbycat/urls.py index 3c762173fea..0bd43eb0307 100644 --- a/tabbycat/urls.py +++ b/tabbycat/urls.py @@ -73,6 +73,8 @@ path('archive/', include('importer.urls_archive')), + path('registration/', include('registration.urls_global')), + # Tournament URLs path('/', include('tournaments.urls')), diff --git a/tabbycat/users/permissions.py b/tabbycat/users/permissions.py index 9c95af17a79..5eea99cfc7b 100644 --- a/tabbycat/users/permissions.py +++ b/tabbycat/users/permissions.py @@ -157,6 +157,13 @@ class Permission(TextChoices): VIEW_EVENTS = 'view.events', _("view events") VIEW_REGISTRATION = 'view.registration', _("view registration responses") + EDIT_PAYMENT_PLATFORMS = 'edit.paymentplatform', _("edit payment platform links") + VIEW_PRODUCTS = 'view.products', _("view products") + EDIT_PRODUCTS = 'edit.products', _("edit products") + VIEW_INVOICES = 'view.invoices', _("view posted invoices") + CREATE_INVOICE = 'add.invoices', _("create new invoices") + VIEW_PAYMENTS = 'view.payments', _("view payment statuses") + ADD_PAYMENT = 'add.payment', _("record payments") permission_type = Union[Permission, bool]