From 03e85f85868e6417fbd8a0c9736dcc1093619b50 Mon Sep 17 00:00:00 2001 From: Mawoka Date: Tue, 20 May 2025 12:12:06 +0200 Subject: [PATCH] Untested Ormar and FastAPI update --- Pipfile | 8 +- Pipfile.lock | 856 +++++++++--------- classquiz/cache.py | 4 +- classquiz/config.py | 8 +- classquiz/db/models.py | 157 ++-- classquiz/db/quiztivity.py | 6 +- classquiz/helpers/__init__.py | 2 +- classquiz/helpers/pixabay.py | 4 +- classquiz/kahoot_importer/README.md | 4 +- classquiz/kahoot_importer/__init__.py | 64 +- classquiz/kahoot_importer/import_quiz.py | 2 +- classquiz/kahoot_importer/search.py | 2 +- classquiz/oauth/github.py | 70 +- .../box_controller/embedded/__init__.py | 2 +- .../routers/box_controller/embedded/socket.py | 16 +- classquiz/routers/box_controller/web.py | 8 +- classquiz/routers/editor.py | 10 +- classquiz/routers/eximport.py | 6 +- classquiz/routers/live.py | 36 +- classquiz/routers/login.py | 14 +- classquiz/routers/moderation.py | 2 +- classquiz/routers/quiz.py | 18 +- classquiz/routers/quiztivity/__init__.py | 6 +- classquiz/routers/quiztivity/shares.py | 8 +- classquiz/routers/remote.py | 2 +- classquiz/routers/sitemap.py | 2 +- classquiz/routers/users/__init__.py | 8 +- classquiz/routers/utils.py | 2 +- classquiz/socket_server/__init__.py | 80 +- classquiz/socket_server/export_helpers.py | 4 +- docker-compose.dev.yml | 2 +- 31 files changed, 719 insertions(+), 694 deletions(-) diff --git a/Pipfile b/Pipfile index 9d50500..98f3cb7 100644 --- a/Pipfile +++ b/Pipfile @@ -8,16 +8,13 @@ verify_ssl = true name = "pypi" [packages] -fastapi = "*" uvicorn = "*" python-socketio = "*" -ormar = { version = "*", extras = ["postgresql"] } passlib = "*" python-jose = "*" alembic = "*" email-validator = "*" python-multipart = "*" -pydantic = "1.10.8" redis = "*" aiohttp = "*" gunicorn = "*" @@ -46,6 +43,11 @@ starlette = "*" pyopenssl = "*" python-dotenv = "*" webauthn = "==1.*" +fastapi = "*" +pypng = "*" +ormar = "*" +pydantic = "*" +pydantic-settings = "*" [dev-packages] coverage = "*" diff --git a/Pipfile.lock b/Pipfile.lock index 1da04e2..27b6814 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "0de7945010825c716b8f550b49b5d9dfb3f37f36065a46ade5d4ce71e8836024" + "sha256": "f061696fa7f15024e20979e05dc96d51cb0a42ffdf8a7d2590ba40ac8104e619" }, "pipfile-spec": 6, "requires": { @@ -125,13 +125,21 @@ "markers": "python_version >= '3.8'", "version": "==1.13.1" }, - "anyio": { + "annotated-types": { "hashes": [ - "sha256:048e05d0f6caeed70d731f3db756d35dcc1f35747c8c403364a8332c630441b8", - "sha256:f75253795a87df48568485fd18cdd2a3fa5c4f7c5be8e5e36637733fce06fed6" + "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", + "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89" ], "markers": "python_version >= '3.8'", - "version": "==4.3.0" + "version": "==0.7.0" + }, + "anyio": { + "hashes": [ + "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", + "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c" + ], + "markers": "python_version >= '3.9'", + "version": "==4.9.0" }, "argon2-cffi": { "hashes": [ @@ -185,47 +193,6 @@ ], "version": "==1.5.1" }, - "asyncpg": { - "hashes": [ - "sha256:16ba8ec2e85d586b4a12bcd03e8d29e3d99e832764d6a1d0b8c27dbbe4a2569d", - "sha256:18f77e8e71e826ba2d0c3ba6764930776719ae2b225ca07e014590545928b576", - "sha256:1b6499de06fe035cf2fa932ec5617ed3f37d4ebbf663b655922e105a484a6af9", - "sha256:20b596d8d074f6f695c13ffb8646d0b6bb1ab570ba7b0cfd349b921ff03cfc1e", - "sha256:2232ebae9796d4600a7819fc383da78ab51b32a092795f4555575fc934c1c89d", - "sha256:4750f5cf49ed48a6e49c6e5aed390eee367694636c2dcfaf4a273ca832c5c43c", - "sha256:4bb366ae34af5b5cabc3ac6a5347dfb6013af38c68af8452f27968d49085ecc0", - "sha256:5710cb0937f696ce303f5eed6d272e3f057339bb4139378ccecafa9ee923a71c", - "sha256:609054a1f47292a905582a1cfcca51a6f3f30ab9d822448693e66fdddde27920", - "sha256:62932f29cf2433988fcd799770ec64b374a3691e7902ecf85da14d5e0854d1ea", - "sha256:69aa1b443a182b13a17ff926ed6627af2d98f62f2fe5890583270cc4073f63bf", - "sha256:71cca80a056ebe19ec74b7117b09e650990c3ca535ac1c35234a96f65604192f", - "sha256:720986d9a4705dd8a40fdf172036f5ae787225036a7eb46e704c45aa8f62c054", - "sha256:768e0e7c2898d40b16d4ef7a0b44e8150db3dd8995b4652aa1fe2902e92c7df8", - "sha256:7a6206210c869ebd3f4eb9e89bea132aefb56ff3d1b7dd7e26b102b17e27bbb1", - "sha256:7d8585707ecc6661d07367d444bbaa846b4e095d84451340da8df55a3757e152", - "sha256:8113e17cfe236dc2277ec844ba9b3d5312f61bd2fdae6d3ed1c1cdd75f6cf2d8", - "sha256:879c29a75969eb2722f94443752f4720d560d1e748474de54ae8dd230bc4956b", - "sha256:88b62164738239f62f4af92567b846a8ef7cf8abf53eddd83650603de4d52163", - "sha256:8934577e1ed13f7d2d9cea3cc016cc6f95c19faedea2c2b56a6f94f257cea672", - "sha256:9654085f2b22f66952124de13a8071b54453ff972c25c59b5ce1173a4283ffd9", - "sha256:975a320baf7020339a67315284a4d3bf7460e664e484672bd3e71dbd881bc692", - "sha256:9a3a4ff43702d39e3c97a8786314123d314e0f0e4dabc8367db5b665c93914de", - "sha256:a7a94c03386bb95456b12c66026b3a87d1b965f0f1e5733c36e7229f8f137747", - "sha256:ab0f21c4818d46a60ca789ebc92327d6d874d3b7ccff3963f7af0a21dc6cff52", - "sha256:bb71211414dd1eeb8d31ec529fe77cff04bf53efc783a5f6f0a32d84923f45cf", - "sha256:bf21ebf023ec67335258e0f3d3ad7b91bb9507985ba2b2206346de488267cad0", - "sha256:bfc3980b4ba6f97138b04f0d32e8af21d6c9fa1f8e6e140c07d15690a0a99279", - "sha256:c2232d4625c558f2aa001942cac1d7952aa9f0dbfc212f63bc754277769e1ef2", - "sha256:ccddb9419ab4e1c48742457d0c0362dbdaeb9b28e6875115abfe319b29ee225d", - "sha256:d20dea7b83651d93b1eb2f353511fe7fd554752844523f17ad30115d8b9c8cd6", - "sha256:e56ac8a8237ad4adec97c0cd4728596885f908053ab725e22900b5902e7f8e69", - "sha256:eb4b2fdf88af4fb1cc569781a8f933d2a73ee82cd720e0cb4edabbaecf2a905b", - "sha256:eca01eb112a39d31cc4abb93a5aef2a81514c23f70956729f42fb83b11b3483f", - "sha256:fca608d199ffed4903dce1bcd97ad0fe8260f405c1c225bdf0002709132171c2", - "sha256:fddcacf695581a8d856654bc4c8cfb73d5c9df26d5f55201722d3e6a699e9629" - ], - "version": "==0.27.0" - }, "attrs": { "hashes": [ "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30", @@ -274,46 +241,53 @@ }, "cbor2": { "hashes": [ - "sha256:03e5b68867b9d89ff2abd14ef7c6d42fbd991adc3e734a19a294935f22a4d05a", - "sha256:1b8b504b590367a51fe8c0d9b8cb458a614d782d37b24483097e2b1e93ed0fff", - "sha256:1fde9e704e96751e0729cc58b912d0e77c34387fb6bcceea0817069e8683df45", - "sha256:211a1e18e65ac71e04434ff5b58bde5c53f85b9c5bc92a3c0e2265089d3034f3", - "sha256:22996159b491d545ecfd489392d3c71e5d0afb9a202dfc0edc8b2cf413a58326", - "sha256:22c24fe9ef1696a84b8fd80ff66eb0e5234505d8b9a9711fc6db57bce10771f3", - "sha256:30e9ba8f4896726ca61869efacda50b6859aff92162ae5a0e192859664f36c81", - "sha256:377cfe9d5560c682486faef6d856226abf8b2801d95fa29d4e5d75b1615eb091", - "sha256:3a4a3420f80d6b942874d66eaad07658066370df994ddee4125b48b2cbc61ece", - "sha256:3c7f223f1fedc74d33f363d184cb2bab9e4bdf24998f73b5e3bef366d6c41628", - "sha256:42eaf0f768bd27afcb38135d5bfc361d3a157f1f5c7dddcd8d391f7fa43d9de8", - "sha256:44bf7457fca23209e14dab8181dff82466a83b72e55b444dbbfe90fa67659492", - "sha256:4f687e6731b1198811223576800258a712ddbfdcfa86c0aee2cc8269193e6b96", - "sha256:516b8390936bb172ff18d7b609a452eaa51991513628949b0a9bf25cbe5a7129", - "sha256:5b28d8ff0e726224a7429281700c28afe0e665f83f9ae79648cbae3f1a391cbf", - "sha256:6031a284d93fc953fc2a2918f261c4f5100905bd064ca3b46961643e7312a828", - "sha256:7221b83000ee01d674572eec1d1caa366eac109d1d32c14d7af9a4aaaf496563", - "sha256:7ea9e150029c3976c46ee9870b6dcdb0a5baae21008fe3290564886b11aa2b64", - "sha256:8839b73befa010358477736680657b9d08c1ed935fd973decb1909712a41afdc", - "sha256:8af7162fcf7aa2649f02563bdb18b2fa6478b751eee4df0257bffe19ea8f107a", - "sha256:8d1c0021d9a1f673066de7c8941f71a59abb11909cc355892dda01e79a2b3045", - "sha256:922e06710e5cf6f56b82b0b90d2f356aa229b99e570994534206985f675fd307", - "sha256:94981277b4bf448a2754c1f34a9d0055a9d1c5a8d102c933ffe95c80f1085bae", - "sha256:9aca73b63bdc6561e1a0d38618e78b9c204c942260d51e663c92c4ba6c961684", - "sha256:9e94043d99fe779f62a15a5e156768588a2a7047bb3a127fa312ac1135ff5ecb", - "sha256:9faa0712d414a88cc1244c78cd4b28fced44f1827dbd8c1649e3c40588aa670f", - "sha256:ac85eb731c524d148f608b9bdb2069fa79e374a10ed5d10a2405eba9a6561e60", - "sha256:b01a718e083e6de8b43296c3ccdb3aa8af6641f6bbb3ea1700427c6af73db28a", - "sha256:b7513c2dea8868991fad7ef8899890ebcf8b199b9b4461c3c11d7ad3aef4820d", - "sha256:c0b53a65673550fde483724ff683753f49462d392d45d7b6576364b39e76e54c", - "sha256:c10ede9462458998f1b9c488e25fe3763aa2491119b7af472b72bf538d789e24", - "sha256:cc29c068687aa2e7778f63b653f1346065b858427a2555df4dc2191f4a0de8ce", - "sha256:ea686dfb5e54d690e704ce04993bc8ca0052a7cd2d4b13dd333a41cca8a05a05", - "sha256:ea7ecd81c5c6e02c2635973f52a0dd1e19c0bf5ef51f813d8cd5e3e7ed072726", - "sha256:f30c8a9a9df79f26e72d8d5fa51ef08eb250d9869a711bcf9539f1865916c983", - "sha256:f70db0ebcf005c25408e8d5cc4b9558c899f13a3e2f8281fa3d3be4894e0e821", - "sha256:fdc564ef2e9228bcd96ec8c6cdaa431a48ab03b3fb8326ead4b3f986330e5b9e" + "sha256:3038523b8fc7de312bb9cdcbbbd599987e64307c4db357cd2030c472a6c7d468", + "sha256:34cf5ab0dc310c3d0196caa6ae062dc09f6c242e2544bea01691fe60c0230596", + "sha256:37096663a5a1c46a776aea44906cbe5fa3952f29f50f349179c00525d321c862", + "sha256:38886c41bebcd7dca57739439455bce759f1e4c551b511f618b8e9c1295b431b", + "sha256:3d1a18b3a58dcd9b40ab55c726160d4a6b74868f2a35b71f9e726268b46dc6a2", + "sha256:4586a4f65546243096e56a3f18f29d60752ee9204722377021b3119a03ed99ff", + "sha256:47261f54a024839ec649b950013c4de5b5f521afe592a2688eebbe22430df1dc", + "sha256:54c72a3207bb2d4480c2c39dad12d7971ce0853a99e3f9b8d559ce6eac84f66f", + "sha256:559dcf0d897260a9e95e7b43556a62253e84550b77147a1ad4d2c389a2a30192", + "sha256:5b856fda4c50c5bc73ed3664e64211fa4f015970ed7a15a4d6361bd48462feaf", + "sha256:5ce13a27ef8fddf643fc17a753fe34aa72b251d03c23da6a560c005dc171085b", + "sha256:5cff06464b8f4ca6eb9abcba67bda8f8334a058abc01005c8e616728c387ad32", + "sha256:61ceb77e6aa25c11c814d4fe8ec9e3bac0094a1f5bd8a2a8c95694596ea01e08", + "sha256:66dd25dd919cddb0b36f97f9ccfa51947882f064729e65e6bef17c28535dc459", + "sha256:6797b824b26a30794f2b169c0575301ca9b74ae99064e71d16e6ba0c9057de51", + "sha256:6e14a1bf6269d25e02ef1d4008e0ce8880aa271d7c6b4c329dba48645764f60e", + "sha256:73b9647eed1493097db6aad61e03d8f1252080ee041a1755de18000dd2c05f37", + "sha256:7488aec919f8408f9987a3a32760bd385d8628b23a35477917aa3923ff6ad45f", + "sha256:7f6d69f38f7d788b04c09ef2b06747536624b452b3c8b371ab78ad43b0296fab", + "sha256:824f202b556fc204e2e9a67d6d6d624e150fbd791278ccfee24e68caec578afd", + "sha256:863e0983989d56d5071270790e7ed8ddbda88c9e5288efdb759aba2efee670bc", + "sha256:87026fc838370d69f23ed8572939bd71cea2b3f6c8f8bb8283f573374b4d7f33", + "sha256:8f747b7a9aaa58881a0c5b4cd4a9b8fb27eca984ed261a769b61de1f6b5bd1e6", + "sha256:90bfa36944caccec963e6ab7e01e64e31cc6664535dc06e6295ee3937c999cbb", + "sha256:93676af02bd9a0b4a62c17c5b20f8e9c37b5019b1a24db70a2ee6cb770423568", + "sha256:94885903105eec66d7efb55f4ce9884fdc5a4d51f3bd75b6fedc68c5c251511b", + "sha256:97a7e409b864fecf68b2ace8978eb5df1738799a333ec3ea2b9597bfcdd6d7d2", + "sha256:a34ee99e86b17444ecbe96d54d909dd1a20e2da9f814ae91b8b71cf1ee2a95e4", + "sha256:a3ac50485cf67dfaab170a3e7b527630e93cb0a6af8cdaa403054215dff93adf", + "sha256:a83b76367d1c3e69facbcb8cdf65ed6948678e72f433137b41d27458aa2a40cb", + "sha256:a88f029522aec5425fc2f941b3df90da7688b6756bd3f0472ab886d21208acbd", + "sha256:a8947c102cac79d049eadbd5e2ffb8189952890df7cbc3ee262bbc2f95b011a9", + "sha256:ae2b49226224e92851c333b91d83292ec62eba53a19c68a79890ce35f1230d70", + "sha256:b682820677ee1dbba45f7da11898d2720f92e06be36acec290867d5ebf3d7e09", + "sha256:b9d15b638539b68aa5d5eacc56099b4543a38b2d2c896055dccf7e83d24b7955", + "sha256:e16c4a87fc999b4926f5c8f6c696b0d251b4745bc40f6c5aee51d69b30b15ca2", + "sha256:e25c2aebc9db99af7190e2261168cdde8ed3d639ca06868e4f477cf3a228a8e9", + "sha256:f0d0a9c5aabd48ecb17acf56004a7542a0b8d8212be52f3102b8218284bd881e", + "sha256:f2764804ffb6553283fc4afb10a280715905a4cea4d6dc7c90d3e89c4a93bc8d", + "sha256:f4c7dbcdc59ea7f5a745d3e30ee5e6b6ff5ce7ac244aa3de6786391b10027bb3", + "sha256:f91e6d74fa6917df31f8757fdd0e154203b0dd0609ec53eb957016a2b474896a", + "sha256:fa61a02995f3a996c03884cf1a0b5733f88cbfd7fa0e34944bf678d4227ee712", + "sha256:fde21ac1cf29336a31615a2c469a9cb03cf0add3ae480672d4d38cda467d07fc", + "sha256:fe11c2eb518c882cfbeed456e7a552e544893c17db66fe5d3230dbeaca6b615c" ], "markers": "python_version >= '3.8'", - "version": "==5.6.2" + "version": "==5.6.5" }, "certifi": { "hashes": [ @@ -325,61 +299,76 @@ }, "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" + "markers": "python_version >= '3.8'", + "version": "==1.17.1" }, "charset-normalizer": { "hashes": [ @@ -487,50 +476,55 @@ }, "cryptography": { "hashes": [ - "sha256:0270572b8bd2c833c3981724b8ee9747b3ec96f699a9665470018594301439ee", - "sha256:111a0d8553afcf8eb02a4fea6ca4f59d48ddb34497aa8706a6cf536f1a5ec576", - "sha256:16a48c23a62a2f4a285699dba2e4ff2d1cff3115b9df052cdd976a18856d8e3d", - "sha256:1b95b98b0d2af784078fa69f637135e3c317091b615cd0905f8b8a087e86fa30", - "sha256:1f71c10d1e88467126f0efd484bd44bca5e14c664ec2ede64c32f20875c0d413", - "sha256:2424ff4c4ac7f6b8177b53c17ed5d8fa74ae5955656867f5a8affaca36a27abb", - "sha256:2bce03af1ce5a5567ab89bd90d11e7bbdff56b8af3acbbec1faded8f44cb06da", - "sha256:329906dcc7b20ff3cad13c069a78124ed8247adcac44b10bea1130e36caae0b4", - "sha256:37dd623507659e08be98eec89323469e8c7b4c1407c85112634ae3dbdb926fdd", - "sha256:3eaafe47ec0d0ffcc9349e1708be2aaea4c6dd4978d76bf6eb0cb2c13636c6fc", - "sha256:5e6275c09d2badf57aea3afa80d975444f4be8d3bc58f7f80d2a484c6f9485c8", - "sha256:6fe07eec95dfd477eb9530aef5bead34fec819b3aaf6c5bd6d20565da607bfe1", - "sha256:7367d7b2eca6513681127ebad53b2582911d1736dc2ffc19f2c3ae49997496bc", - "sha256:7cde5f38e614f55e28d831754e8a3bacf9ace5d1566235e39d91b35502d6936e", - "sha256:9481ffe3cf013b71b2428b905c4f7a9a4f76ec03065b05ff499bb5682a8d9ad8", - "sha256:98d8dc6d012b82287f2c3d26ce1d2dd130ec200c8679b6213b3c73c08b2b7940", - "sha256:a011a644f6d7d03736214d38832e030d8268bcff4a41f728e6030325fea3e400", - "sha256:a2913c5375154b6ef2e91c10b5720ea6e21007412f6437504ffea2109b5a33d7", - "sha256:a30596bae9403a342c978fb47d9b0ee277699fa53bbafad14706af51fe543d16", - "sha256:b03c2ae5d2f0fc05f9a2c0c997e1bc18c8229f392234e8a0194f202169ccd278", - "sha256:b6cd2203306b63e41acdf39aa93b86fb566049aeb6dc489b70e34bcd07adca74", - "sha256:b7ffe927ee6531c78f81aa17e684e2ff617daeba7f189f911065b2ea2d526dec", - "sha256:b8cac287fafc4ad485b8a9b67d0ee80c66bf3574f655d3b97ef2e1082360faf1", - "sha256:ba334e6e4b1d92442b75ddacc615c5476d4ad55cc29b15d590cc6b86efa487e2", - "sha256:ba3e4a42397c25b7ff88cdec6e2a16c2be18720f317506ee25210f6d31925f9c", - "sha256:c41fb5e6a5fe9ebcd58ca3abfeb51dffb5d83d6775405305bfa8715b76521922", - "sha256:cd2030f6650c089aeb304cf093f3244d34745ce0cfcc39f20c6fbfe030102e2a", - "sha256:cd65d75953847815962c84a4654a84850b2bb4aed3f26fadcc1c13892e1e29f6", - "sha256:e4985a790f921508f36f81831817cbc03b102d643b5fcb81cd33df3fa291a1a1", - "sha256:e807b3188f9eb0eaa7bbb579b462c5ace579f1cedb28107ce8b48a9f7ad3679e", - "sha256:f12764b8fffc7a123f641d7d049d382b73f96a34117e0b637b80643169cec8ac", - "sha256:f8837fe1d6ac4a8052a9a8ddab256bc006242696f03368a4009be7ee3075cdb7" + "sha256:057723b79752a142efbc609e90b0dff27b0361ccbee3bd48312d70f5cdf53b78", + "sha256:05c2385b1f5c89a17df19900cfb1345115a77168f5ed44bdf6fd3de1ce5cc65b", + "sha256:08281de408e7eb71ba3cd5098709a356bfdf65eebd7ee7633c3610f0aa80d79b", + "sha256:10d68763892a7b19c22508ab57799c4423c7c8cd61d7eee4c5a6a55a46511949", + "sha256:1655d3a76e3dedb683c982a6c3a2cbfae2d08f47a48ec5a3d58db52b3d29ea6f", + "sha256:18f8084b7ca3ce1b8d38bdfe33c48116edf9a08b4d056ef4a96dceaa36d8d965", + "sha256:2cb03a944a1a412724d15a7c051d50e63a868031f26b6a312f2016965b661942", + "sha256:4142e20c29224cec63e9e32eb1e6014fb285fe39b7be66b3564ca978a3a8afe9", + "sha256:463096533acd5097f8751115bc600b0b64620c4aafcac10c6d0041e6e68f88fe", + "sha256:48caa55c528617fa6db1a9c3bf2e37ccb31b73e098ac2b71408d1f2db551dde4", + "sha256:49af56491473231159c98c2c26f1a8f3799a60e5cf0e872d00745b858ddac9d2", + "sha256:4cc31c66411e14dd70e2f384a9204a859dc25b05e1f303df0f5326691061b839", + "sha256:501de1296b2041dccf2115e3c7d4947430585601b251b140970ce255c5cfb985", + "sha256:59c0c8f043dd376bbd9d4f636223836aed50431af4c5a467ed9bf61520294627", + "sha256:614bca7c6ed0d8ad1dce683a6289afae1f880675b4090878a0136c3da16bc693", + "sha256:61a8b1bbddd9332917485b2453d1de49f142e6334ce1d97b7916d5a85d179c84", + "sha256:7429936146063bd1b2cfc54f0e04016b90ee9b1c908a7bed0800049cbace70eb", + "sha256:7c73968fbb7698a4c5d6160859db560d3aac160edde89c751edd5a8bc6560c88", + "sha256:80303ee6a02ef38c4253160446cbeb5c400c07e01d4ddbd4ff722a89b736d95a", + "sha256:965611880c3fa8e504b7458484c0697e00ae6e937279cd6734fdaa2bc954dc49", + "sha256:9a900036b42f7324df7c7ad9569eb92ba0b613cf699160dd9c2154b24fd02f8e", + "sha256:9cfd1399064b13043082c660ddd97a0358e41c8b0dc7b77c1243e013d305c344", + "sha256:a8ec324711596fbf21837d3a5db543937dd84597d364769b46e0102250023f77", + "sha256:a9727a21957d3327cf6b7eb5ffc9e4b663909a25fea158e3fcbc49d4cdd7881b", + "sha256:b19f4b28dd2ef2e6d600307fee656c00825a2980c4356a7080bd758d633c3a6f", + "sha256:b2de529027579e43b6dc1f805f467b102fb7d13c1e54c334f1403ee2b37d0059", + "sha256:c0c000c1a09f069632d8a9eb3b610ac029fcc682f1d69b758e625d6ee713f4ed", + "sha256:cdafb86eb673c3211accffbffdb3cdffa3aaafacd14819e0898d23696d18e4d3", + "sha256:d2a90ce2f0f5b695e4785ac07c19a58244092f3c85d57db6d8eb1a2b26d2aad6", + "sha256:d784d57b958ffd07e9e226d17272f9af0c41572557604ca7554214def32c26bf", + "sha256:d891942592789fa0ab71b502550bbadb12f540d7413d7d7c4cef4b02af0f5bc6", + "sha256:dc7693573f16535428183de8fd27f0ca1ca37a51baa0b41dc5ed7b3d68fe80e2", + "sha256:ddb8d01aa900b741d6b7cc585a97aff787175f160ab975e21f880e89d810781a", + "sha256:e328357b6bbf79928363dbf13f4635b7aac0306afb7e5ad24d21d0c5761c3253", + "sha256:e86c8d54cd19a13e9081898b3c24351683fd39d726ecf8e774aaa9d8d96f5f3a", + "sha256:e9e4bdcd70216b08801e267c0b563316b787f957a46e215249921f99288456f9", + "sha256:f169469d04a23282de9d0be349499cb6683b6ff1b68901210faacac9b0c24b7d" ], "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==42.0.5" + "markers": "python_version >= '3.7' and python_full_version not in '3.9.0, 3.9.1'", + "version": "==45.0.2" }, "databases": { "hashes": [ - "sha256:b09c370ad7c2f64c7f4316c096e265dc2e28304732639889272390decda2f893", - "sha256:ff4010136ac2bb9da2322a2ffda4ef9185ae1c365e5891e52924dd9499d33dc4" + "sha256:cf5da4b8a3e3cd038c459529725ebb64931cbbb7a091102664f20ef8f6cefd0d", + "sha256:ea2d419d3d2eb80595b7ceb8f282056f080af62efe2fb9bcd83562f93ec4b674" ], "markers": "python_version >= '3.7'", - "version": "==0.6.2" + "version": "==0.7.0" }, "dnspython": { "hashes": [ @@ -567,12 +561,12 @@ }, "fastapi": { "hashes": [ - "sha256:5df913203c482f820d31f48e635e022f8cbfe7350e4830ef05a3163925b1addc", - "sha256:6feac43ec359dfe4f45b2c18ec8c94edb8dc2dfc461d417d9e626590c071baad" + "sha256:1e2c2a2646905f9e83d32f04a3f86aff4a286669c6c950ca95b5fd68c2602681", + "sha256:e94613d6c05e27be7ffebdd6ea5f388112e5e430c8f7d6494a9d1d88d43e814d" ], "index": "pypi", "markers": "python_version >= '3.8'", - "version": "==0.110.1" + "version": "==0.115.12" }, "frozenlist": { "hashes": [ @@ -659,67 +653,64 @@ }, "greenlet": { "hashes": [ - "sha256:01bc7ea167cf943b4c802068e178bbf70ae2e8c080467070d01bfa02f337ee67", - "sha256:0448abc479fab28b00cb472d278828b3ccca164531daab4e970a0458786055d6", - "sha256:086152f8fbc5955df88382e8a75984e2bb1c892ad2e3c80a2508954e52295257", - "sha256:098d86f528c855ead3479afe84b49242e174ed262456c342d70fc7f972bc13c4", - "sha256:149e94a2dd82d19838fe4b2259f1b6b9957d5ba1b25640d2380bea9c5df37676", - "sha256:1551a8195c0d4a68fac7a4325efac0d541b48def35feb49d803674ac32582f61", - "sha256:15d79dd26056573940fcb8c7413d84118086f2ec1a8acdfa854631084393efcc", - "sha256:1996cb9306c8595335bb157d133daf5cf9f693ef413e7673cb07e3e5871379ca", - "sha256:1a7191e42732df52cb5f39d3527217e7ab73cae2cb3694d241e18f53d84ea9a7", - "sha256:1ea188d4f49089fc6fb283845ab18a2518d279c7cd9da1065d7a84e991748728", - "sha256:1f672519db1796ca0d8753f9e78ec02355e862d0998193038c7073045899f305", - "sha256:2516a9957eed41dd8f1ec0c604f1cdc86758b587d964668b5b196a9db5bfcde6", - "sha256:2797aa5aedac23af156bbb5a6aa2cd3427ada2972c828244eb7d1b9255846379", - "sha256:2dd6e660effd852586b6a8478a1d244b8dc90ab5b1321751d2ea15deb49ed414", - "sha256:3ddc0f794e6ad661e321caa8d2f0a55ce01213c74722587256fb6566049a8b04", - "sha256:3ed7fb269f15dc662787f4119ec300ad0702fa1b19d2135a37c2c4de6fadfd4a", - "sha256:419b386f84949bf0e7c73e6032e3457b82a787c1ab4a0e43732898a761cc9dbf", - "sha256:43374442353259554ce33599da8b692d5aa96f8976d567d4badf263371fbe491", - "sha256:52f59dd9c96ad2fc0d5724107444f76eb20aaccb675bf825df6435acb7703559", - "sha256:57e8974f23e47dac22b83436bdcf23080ade568ce77df33159e019d161ce1d1e", - "sha256:5b51e85cb5ceda94e79d019ed36b35386e8c37d22f07d6a751cb659b180d5274", - "sha256:649dde7de1a5eceb258f9cb00bdf50e978c9db1b996964cd80703614c86495eb", - "sha256:64d7675ad83578e3fc149b617a444fab8efdafc9385471f868eb5ff83e446b8b", - "sha256:68834da854554926fbedd38c76e60c4a2e3198c6fbed520b106a8986445caaf9", - "sha256:6b66c9c1e7ccabad3a7d037b2bcb740122a7b17a53734b7d72a344ce39882a1b", - "sha256:70fb482fdf2c707765ab5f0b6655e9cfcf3780d8d87355a063547b41177599be", - "sha256:7170375bcc99f1a2fbd9c306f5be8764eaf3ac6b5cb968862cad4c7057756506", - "sha256:73a411ef564e0e097dbe7e866bb2dda0f027e072b04da387282b02c308807405", - "sha256:77457465d89b8263bca14759d7c1684df840b6811b2499838cc5b040a8b5b113", - "sha256:7f362975f2d179f9e26928c5b517524e89dd48530a0202570d55ad6ca5d8a56f", - "sha256:81bb9c6d52e8321f09c3d165b2a78c680506d9af285bfccbad9fb7ad5a5da3e5", - "sha256:881b7db1ebff4ba09aaaeae6aa491daeb226c8150fc20e836ad00041bcb11230", - "sha256:894393ce10ceac937e56ec00bb71c4c2f8209ad516e96033e4b3b1de270e200d", - "sha256:99bf650dc5d69546e076f413a87481ee1d2d09aaaaaca058c9251b6d8c14783f", - "sha256:9da2bd29ed9e4f15955dd1595ad7bc9320308a3b766ef7f837e23ad4b4aac31a", - "sha256:afaff6cf5200befd5cec055b07d1c0a5a06c040fe5ad148abcd11ba6ab9b114e", - "sha256:b1b5667cced97081bf57b8fa1d6bfca67814b0afd38208d52538316e9422fc61", - "sha256:b37eef18ea55f2ffd8f00ff8fe7c8d3818abd3e25fb73fae2ca3b672e333a7a6", - "sha256:b542be2440edc2d48547b5923c408cbe0fc94afb9f18741faa6ae970dbcb9b6d", - "sha256:b7dcbe92cc99f08c8dd11f930de4d99ef756c3591a5377d1d9cd7dd5e896da71", - "sha256:b7f009caad047246ed379e1c4dbcb8b020f0a390667ea74d2387be2998f58a22", - "sha256:bba5387a6975598857d86de9eac14210a49d554a77eb8261cc68b7d082f78ce2", - "sha256:c5e1536de2aad7bf62e27baf79225d0d64360d4168cf2e6becb91baf1ed074f3", - "sha256:c5ee858cfe08f34712f548c3c363e807e7186f03ad7a5039ebadb29e8c6be067", - "sha256:c9db1c18f0eaad2f804728c67d6c610778456e3e1cc4ab4bbd5eeb8e6053c6fc", - "sha256:d353cadd6083fdb056bb46ed07e4340b0869c305c8ca54ef9da3421acbdf6881", - "sha256:d46677c85c5ba00a9cb6f7a00b2bfa6f812192d2c9f7d9c4f6a55b60216712f3", - "sha256:d4d1ac74f5c0c0524e4a24335350edad7e5f03b9532da7ea4d3c54d527784f2e", - "sha256:d73a9fe764d77f87f8ec26a0c85144d6a951a6c438dfe50487df5595c6373eac", - "sha256:da70d4d51c8b306bb7a031d5cff6cc25ad253affe89b70352af5f1cb68e74b53", - "sha256:daf3cb43b7cf2ba96d614252ce1684c1bccee6b2183a01328c98d36fcd7d5cb0", - "sha256:dca1e2f3ca00b84a396bc1bce13dd21f680f035314d2379c4160c98153b2059b", - "sha256:dd4f49ae60e10adbc94b45c0b5e6a179acc1736cf7a90160b404076ee283cf83", - "sha256:e1f145462f1fa6e4a4ae3c0f782e580ce44d57c8f2c7aae1b6fa88c0b2efdb41", - "sha256:e3391d1e16e2a5a1507d83e4a8b100f4ee626e8eca43cf2cadb543de69827c4c", - "sha256:fcd2469d6a2cf298f198f0487e0a5b1a47a42ca0fa4dfd1b6862c999f018ebbf", - "sha256:fd096eb7ffef17c456cfa587523c5f92321ae02427ff955bebe9e3c63bc9f0da", - "sha256:fe754d231288e1e64323cfad462fcee8f0288654c10bdf4f603a39ed923bef33" + "sha256:00cd814b8959b95a546e47e8d589610534cfb71f19802ea8a2ad99d95d702057", + "sha256:02a98600899ca1ca5d3a2590974c9e3ec259503b2d6ba6527605fcd74e08e207", + "sha256:02f5972ff02c9cf615357c17ab713737cccfd0eaf69b951084a9fd43f39833d3", + "sha256:055916fafad3e3388d27dd68517478933a97edc2fc54ae79d3bec827de2c64c4", + "sha256:0a16fb934fcabfdfacf21d79e6fed81809d8cd97bc1be9d9c89f0e4567143d7b", + "sha256:1592a615b598643dbfd566bac8467f06c8c8ab6e56f069e573832ed1d5d528cc", + "sha256:1919cbdc1c53ef739c94cf2985056bcc0838c1f217b57647cbf4578576c63825", + "sha256:1e4747712c4365ef6765708f948acc9c10350719ca0545e362c24ab973017370", + "sha256:1e76106b6fc55fa3d6fe1c527f95ee65e324a13b62e243f77b48317346559708", + "sha256:1f72667cc341c95184f1c68f957cb2d4fc31eef81646e8e59358a10ce6689457", + "sha256:2593283bf81ca37d27d110956b79e8723f9aa50c4bcdc29d3c0543d4743d2763", + "sha256:2dc5c43bb65ec3669452af0ab10729e8fdc17f87a1f2ad7ec65d4aaaefabf6bf", + "sha256:3091bc45e6b0c73f225374fefa1536cd91b1e987377b12ef5b19129b07d93ebe", + "sha256:354f67445f5bed6604e493a06a9a49ad65675d3d03477d38a4db4a427e9aad0e", + "sha256:3885f85b61798f4192d544aac7b25a04ece5fe2704670b4ab73c2d2c14ab740d", + "sha256:3ab7194ee290302ca15449f601036007873028712e92ca15fc76597a0aeb4c59", + "sha256:3aeca9848d08ce5eb653cf16e15bb25beeab36e53eb71cc32569f5f3afb2a3aa", + "sha256:44671c29da26539a5f142257eaba5110f71887c24d40df3ac87f1117df589e0e", + "sha256:45f9f4853fb4cc46783085261c9ec4706628f3b57de3e68bae03e8f8b3c0de51", + "sha256:4bd139e4943547ce3a56ef4b8b1b9479f9e40bb47e72cc906f0f66b9d0d5cab3", + "sha256:4fefc7aa68b34b9224490dfda2e70ccf2131368493add64b4ef2d372955c207e", + "sha256:6629311595e3fe7304039c67f00d145cd1d38cf723bb5b99cc987b23c1433d61", + "sha256:6fadd183186db360b61cb34e81117a096bff91c072929cd1b529eb20dd46e6c5", + "sha256:71566302219b17ca354eb274dfd29b8da3c268e41b646f330e324e3967546a74", + "sha256:7409796591d879425997a518138889d8d17e63ada7c99edc0d7a1c22007d4907", + "sha256:752f0e79785e11180ebd2e726c8a88109ded3e2301d40abced2543aa5d164275", + "sha256:7791dcb496ec53d60c7f1c78eaa156c21f402dda38542a00afc3e20cae0f480f", + "sha256:782743700ab75716650b5238a4759f840bb2dcf7bff56917e9ffdf9f1f23ec59", + "sha256:7c9896249fbef2c615853b890ee854f22c671560226c9221cfd27c995db97e5c", + "sha256:85f3e248507125bf4af607a26fd6cb8578776197bd4b66e35229cdf5acf1dfbf", + "sha256:89c69e9a10670eb7a66b8cef6354c24671ba241f46152dd3eed447f79c29fb5b", + "sha256:8cb8553ee954536500d88a1a2f58fcb867e45125e600e80f586ade399b3f8819", + "sha256:9ae572c996ae4b5e122331e12bbb971ea49c08cc7c232d1bd43150800a2d6c65", + "sha256:9c7b15fb9b88d9ee07e076f5a683027bc3befd5bb5d25954bb633c385d8b737e", + "sha256:9ea5231428af34226c05f927e16fc7f6fa5e39e3ad3cd24ffa48ba53a47f4240", + "sha256:a31ead8411a027c2c4759113cf2bd473690517494f3d6e4bf67064589afcd3c5", + "sha256:a8fa80665b1a29faf76800173ff5325095f3e66a78e62999929809907aca5659", + "sha256:ad053d34421a2debba45aa3cc39acf454acbcd025b3fc1a9f8a0dee237abd485", + "sha256:b24c7844c0a0afc3ccbeb0b807adeefb7eff2b5599229ecedddcfeb0ef333bec", + "sha256:b50a8c5c162469c3209e5ec92ee4f95c8231b11db6a04db09bbe338176723bb8", + "sha256:ba30e88607fb6990544d84caf3c706c4b48f629e18853fc6a646f82db9629418", + "sha256:bf3fc9145141250907730886b031681dfcc0de1c158f3cc51c092223c0f381ce", + "sha256:c23ea227847c9dbe0b3910f5c0dd95658b607137614eb821e6cbaecd60d81cc6", + "sha256:c3cc1a3ed00ecfea8932477f729a9f616ad7347a5e55d50929efa50a86cb7be7", + "sha256:c49e9f7c6f625507ed83a7485366b46cbe325717c60837f7244fc99ba16ba9d6", + "sha256:d0cb7d47199001de7658c213419358aa8937df767936506db0db7ce1a71f4a2f", + "sha256:d8009ae46259e31bc73dc183e402f548e980c96f33a6ef58cc2e7865db012e13", + "sha256:da956d534a6d1b9841f95ad0f18ace637668f680b1339ca4dcfb2c1837880a0b", + "sha256:dcb9cebbf3f62cb1e5afacae90761ccce0effb3adaa32339a0670fe7805d8068", + "sha256:decb0658ec19e5c1f519faa9a160c0fc85a41a7e6654b3ce1b44b939f8bf1325", + "sha256:df4d1509efd4977e6a844ac96d8be0b9e5aa5d5c77aa27ca9f4d3f92d3fcf330", + "sha256:eeb27bece45c0c2a5842ac4c5a1b5c2ceaefe5711078eed4e8043159fa05c834", + "sha256:efcdfb9df109e8a3b475c016f60438fcd4be68cd13a365d42b35914cdab4bb2b", + "sha256:fd9fb7c941280e2c837b603850efc93c999ae58aae2b40765ed682a6907ebbc5", + "sha256:fe46d4f8e94e637634d54477b0cfabcf93c53f29eedcbdeecaf2af32029b4421" ], - "markers": "python_version >= '3' and platform_machine == 'aarch64' or (platform_machine == 'ppc64le' or (platform_machine == 'x86_64' or (platform_machine == 'amd64' or (platform_machine == 'AMD64' or (platform_machine == 'win32' or platform_machine == 'WIN32')))))", - "version": "==3.0.3" + "markers": "python_version >= '3.9'", + "version": "==3.2.2" }, "gunicorn": { "hashes": [ @@ -871,11 +862,11 @@ }, "idna": { "hashes": [ - "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc", - "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0" + "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", + "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3" ], - "markers": "python_version >= '3.5'", - "version": "==3.7" + "markers": "python_version >= '3.6'", + "version": "==3.10" }, "itsdangerous": { "hashes": [ @@ -1108,15 +1099,13 @@ "version": "==3.1.2" }, "ormar": { - "extras": [ - "postgresql" - ], "hashes": [ - "sha256:0cd60ee2106c8215486d9654e39cf8612837c16b7deaf2680c35eb55b06abf9d", - "sha256:8af45d1c28811d73c2c5015bd0a4353b4c4cb54a69bcd2eecac276922e2de19c" + "sha256:263bf00f63cbe824882fbbb828764aa0539204ae6d259e9e1b51c2d2a6a1c83e", + "sha256:cc2f67a597f312eedc5815919d71fe34d9d0bf2bdb0eae5203033d4e8ec2587a" ], - "markers": "python_full_version >= '3.7.0' and python_full_version < '4.0.0'", - "version": "==0.12.2" + "index": "pypi", + "markers": "python_full_version >= '3.8.0' and python_full_version < '4.0.0'", + "version": "==0.20.2" }, "packaging": { "hashes": [ @@ -1209,83 +1198,6 @@ "markers": "python_version >= '3.8'", "version": "==10.3.0" }, - "psycopg2-binary": { - "hashes": [ - "sha256:03ef7df18daf2c4c07e2695e8cfd5ee7f748a1d54d802330985a78d2a5a6dca9", - "sha256:0a602ea5aff39bb9fac6308e9c9d82b9a35c2bf288e184a816002c9fae930b77", - "sha256:0c009475ee389757e6e34611d75f6e4f05f0cf5ebb76c6037508318e1a1e0d7e", - "sha256:0ef4854e82c09e84cc63084a9e4ccd6d9b154f1dbdd283efb92ecd0b5e2b8c84", - "sha256:1236ed0952fbd919c100bc839eaa4a39ebc397ed1c08a97fc45fee2a595aa1b3", - "sha256:143072318f793f53819048fdfe30c321890af0c3ec7cb1dfc9cc87aa88241de2", - "sha256:15208be1c50b99203fe88d15695f22a5bed95ab3f84354c494bcb1d08557df67", - "sha256:1873aade94b74715be2246321c8650cabf5a0d098a95bab81145ffffa4c13876", - "sha256:18d0ef97766055fec15b5de2c06dd8e7654705ce3e5e5eed3b6651a1d2a9a152", - "sha256:1ea665f8ce695bcc37a90ee52de7a7980be5161375d42a0b6c6abedbf0d81f0f", - "sha256:2293b001e319ab0d869d660a704942c9e2cce19745262a8aba2115ef41a0a42a", - "sha256:246b123cc54bb5361588acc54218c8c9fb73068bf227a4a531d8ed56fa3ca7d6", - "sha256:275ff571376626195ab95a746e6a04c7df8ea34638b99fc11160de91f2fef503", - "sha256:281309265596e388ef483250db3640e5f414168c5a67e9c665cafce9492eda2f", - "sha256:2d423c8d8a3c82d08fe8af900ad5b613ce3632a1249fd6a223941d0735fce493", - "sha256:2e5afae772c00980525f6d6ecf7cbca55676296b580c0e6abb407f15f3706996", - "sha256:30dcc86377618a4c8f3b72418df92e77be4254d8f89f14b8e8f57d6d43603c0f", - "sha256:31a34c508c003a4347d389a9e6fcc2307cc2150eb516462a7a17512130de109e", - "sha256:323ba25b92454adb36fa425dc5cf6f8f19f78948cbad2e7bc6cdf7b0d7982e59", - "sha256:34eccd14566f8fe14b2b95bb13b11572f7c7d5c36da61caf414d23b91fcc5d94", - "sha256:3a58c98a7e9c021f357348867f537017057c2ed7f77337fd914d0bedb35dace7", - "sha256:3f78fd71c4f43a13d342be74ebbc0666fe1f555b8837eb113cb7416856c79682", - "sha256:4154ad09dac630a0f13f37b583eae260c6aa885d67dfbccb5b02c33f31a6d420", - "sha256:420f9bbf47a02616e8554e825208cb947969451978dceb77f95ad09c37791dae", - "sha256:4686818798f9194d03c9129a4d9a702d9e113a89cb03bffe08c6cf799e053291", - "sha256:57fede879f08d23c85140a360c6a77709113efd1c993923c59fde17aa27599fe", - "sha256:60989127da422b74a04345096c10d416c2b41bd7bf2a380eb541059e4e999980", - "sha256:64cf30263844fa208851ebb13b0732ce674d8ec6a0c86a4e160495d299ba3c93", - "sha256:68fc1f1ba168724771e38bee37d940d2865cb0f562380a1fb1ffb428b75cb692", - "sha256:6e6f98446430fdf41bd36d4faa6cb409f5140c1c2cf58ce0bbdaf16af7d3f119", - "sha256:729177eaf0aefca0994ce4cffe96ad3c75e377c7b6f4efa59ebf003b6d398716", - "sha256:72dffbd8b4194858d0941062a9766f8297e8868e1dd07a7b36212aaa90f49472", - "sha256:75723c3c0fbbf34350b46a3199eb50638ab22a0228f93fb472ef4d9becc2382b", - "sha256:77853062a2c45be16fd6b8d6de2a99278ee1d985a7bd8b103e97e41c034006d2", - "sha256:78151aa3ec21dccd5cdef6c74c3e73386dcdfaf19bced944169697d7ac7482fc", - "sha256:7f01846810177d829c7692f1f5ada8096762d9172af1b1a28d4ab5b77c923c1c", - "sha256:804d99b24ad523a1fe18cc707bf741670332f7c7412e9d49cb5eab67e886b9b5", - "sha256:81ff62668af011f9a48787564ab7eded4e9fb17a4a6a74af5ffa6a457400d2ab", - "sha256:8359bf4791968c5a78c56103702000105501adb557f3cf772b2c207284273984", - "sha256:83791a65b51ad6ee6cf0845634859d69a038ea9b03d7b26e703f94c7e93dbcf9", - "sha256:8532fd6e6e2dc57bcb3bc90b079c60de896d2128c5d9d6f24a63875a95a088cf", - "sha256:876801744b0dee379e4e3c38b76fc89f88834bb15bf92ee07d94acd06ec890a0", - "sha256:8dbf6d1bc73f1d04ec1734bae3b4fb0ee3cb2a493d35ede9badbeb901fb40f6f", - "sha256:8f8544b092a29a6ddd72f3556a9fcf249ec412e10ad28be6a0c0d948924f2212", - "sha256:911dda9c487075abd54e644ccdf5e5c16773470a6a5d3826fda76699410066fb", - "sha256:977646e05232579d2e7b9c59e21dbe5261f403a88417f6a6512e70d3f8a046be", - "sha256:9dba73be7305b399924709b91682299794887cbbd88e38226ed9f6712eabee90", - "sha256:a148c5d507bb9b4f2030a2025c545fccb0e1ef317393eaba42e7eabd28eb6041", - "sha256:a6cdcc3ede532f4a4b96000b6362099591ab4a3e913d70bcbac2b56c872446f7", - "sha256:ac05fb791acf5e1a3e39402641827780fe44d27e72567a000412c648a85ba860", - "sha256:b0605eaed3eb239e87df0d5e3c6489daae3f7388d455d0c0b4df899519c6a38d", - "sha256:b58b4710c7f4161b5e9dcbe73bb7c62d65670a87df7bcce9e1faaad43e715245", - "sha256:b6356793b84728d9d50ead16ab43c187673831e9d4019013f1402c41b1db9b27", - "sha256:b76bedd166805480ab069612119ea636f5ab8f8771e640ae103e05a4aae3e417", - "sha256:bc7bb56d04601d443f24094e9e31ae6deec9ccb23581f75343feebaf30423359", - "sha256:c2470da5418b76232f02a2fcd2229537bb2d5a7096674ce61859c3229f2eb202", - "sha256:c332c8d69fb64979ebf76613c66b985414927a40f8defa16cf1bc028b7b0a7b0", - "sha256:c6af2a6d4b7ee9615cbb162b0738f6e1fd1f5c3eda7e5da17861eacf4c717ea7", - "sha256:c77e3d1862452565875eb31bdb45ac62502feabbd53429fdc39a1cc341d681ba", - "sha256:ca08decd2697fdea0aea364b370b1249d47336aec935f87b8bbfd7da5b2ee9c1", - "sha256:ca49a8119c6cbd77375ae303b0cfd8c11f011abbbd64601167ecca18a87e7cdd", - "sha256:cb16c65dcb648d0a43a2521f2f0a2300f40639f6f8c1ecbc662141e4e3e1ee07", - "sha256:d2997c458c690ec2bc6b0b7ecbafd02b029b7b4283078d3b32a852a7ce3ddd98", - "sha256:d3f82c171b4ccd83bbaf35aa05e44e690113bd4f3b7b6cc54d2219b132f3ae55", - "sha256:dc4926288b2a3e9fd7b50dc6a1909a13bbdadfc67d93f3374d984e56f885579d", - "sha256:ead20f7913a9c1e894aebe47cccf9dc834e1618b7aa96155d2091a626e59c972", - "sha256:ebdc36bea43063116f0486869652cb2ed7032dbc59fbcb4445c4862b5c1ecf7f", - "sha256:ed1184ab8f113e8d660ce49a56390ca181f2981066acc27cf637d5c1e10ce46e", - "sha256:ee825e70b1a209475622f7f7b776785bd68f34af6e7a46e2e42f27b659b5bc26", - "sha256:f7ae5d65ccfbebdfa761585228eb4d0df3a8b15cfb53bd953e713e09fbb12957", - "sha256:f7fc5a5acafb7d6ccca13bfa8c90f8c51f13d8fb87d95656d3950f0158d3ce53", - "sha256:f9b5571d33660d5009a8b3c25dc1db560206e2d2f89d3df1cb32d72c0d117d52" - ], - "version": "==2.9.9" - }, "py-avataaars-no-png": { "hashes": [ "sha256:9cd593fe93d6e223626903da7310ad53e857b3fd856ce7e7f2b1faa401740054", @@ -1351,46 +1263,116 @@ }, "pydantic": { "hashes": [ - "sha256:052d8654cb65174d6f9490cc9b9a200083a82cf5c3c5d3985db765757eb3b375", - "sha256:0c6fafa0965b539d7aab0a673a046466d23b86e4b0e8019d25fd53f4df62c277", - "sha256:1243d28e9b05003a89d72e7915fdb26ffd1d39bdd39b00b7dbe4afae4b557f9d", - "sha256:12f7b0bf8553e310e530e9f3a2f5734c68699f42218bf3568ef49cd9b0e44df4", - "sha256:1410275520dfa70effadf4c21811d755e7ef9bb1f1d077a21958153a92c8d9ca", - "sha256:16f8c3e33af1e9bb16c7a91fc7d5fa9fe27298e9f299cff6cb744d89d573d62c", - "sha256:17aef11cc1b997f9d574b91909fed40761e13fac438d72b81f902226a69dac01", - "sha256:191ba419b605f897ede9892f6c56fb182f40a15d309ef0142212200a10af4c18", - "sha256:1952526ba40b220b912cdc43c1c32bcf4a58e3f192fa313ee665916b26befb68", - "sha256:1ced8375969673929809d7f36ad322934c35de4af3b5e5b09ec967c21f9f7887", - "sha256:2e4148e635994d57d834be1182a44bdb07dd867fa3c2d1b37002000646cc5459", - "sha256:34d327c81e68a1ecb52fe9c8d50c8a9b3e90d3c8ad991bfc8f953fb477d42fb4", - "sha256:35db5301b82e8661fa9c505c800d0990bc14e9f36f98932bb1d248c0ac5cada5", - "sha256:3e59417ba8a17265e632af99cc5f35ec309de5980c440c255ab1ca3ae96a3e0e", - "sha256:42aa0c4b5c3025483240a25b09f3c09a189481ddda2ea3a831a9d25f444e03c1", - "sha256:666bdf6066bf6dbc107b30d034615d2627e2121506c555f73f90b54a463d1f33", - "sha256:66a703d1983c675a6e0fed8953b0971c44dba48a929a2000a493c3772eb61a5a", - "sha256:6a82d6cda82258efca32b40040228ecf43a548671cb174a1e81477195ed3ed56", - "sha256:6f2e754d5566f050954727c77f094e01793bcb5725b663bf628fa6743a5a9108", - "sha256:7456eb22ed9aaa24ff3e7b4757da20d9e5ce2a81018c1b3ebd81a0b88a18f3b2", - "sha256:7b1f6cb446470b7ddf86c2e57cd119a24959af2b01e552f60705910663af09a4", - "sha256:7d5b8641c24886d764a74ec541d2fc2c7fb19f6da2a4001e6d580ba4a38f7878", - "sha256:84d80219c3f8d4cad44575e18404099c76851bc924ce5ab1c4c8bb5e2a2227d0", - "sha256:88f195f582851e8db960b4a94c3e3ad25692c1c1539e2552f3df7a9e972ef60e", - "sha256:93e6bcfccbd831894a6a434b0aeb1947f9e70b7468f274154d03d71fabb1d7c6", - "sha256:93e766b4a8226e0708ef243e843105bf124e21331694367f95f4e3b4a92bbb3f", - "sha256:ab523c31e22943713d80d8d342d23b6f6ac4b792a1e54064a8d0cf78fd64e800", - "sha256:bb14388ec45a7a0dc429e87def6396f9e73c8c77818c927b6a60706603d5f2ea", - "sha256:c0ab53b609c11dfc0c060d94335993cc2b95b2150e25583bec37a49b2d6c6c3f", - "sha256:c33b60054b2136aef8cf190cd4c52a3daa20b2263917c49adad20eaf381e823b", - "sha256:ceb6a23bf1ba4b837d0cfe378329ad3f351b5897c8d4914ce95b85fba96da5a1", - "sha256:d532bf00f381bd6bc62cabc7d1372096b75a33bc197a312b03f5838b4fb84edd", - "sha256:df7800cb1984d8f6e249351139667a8c50a379009271ee6236138a22a0c0f319", - "sha256:e82d4566fcd527eae8b244fa952d99f2ca3172b7e97add0b43e2d97ee77f81ab", - "sha256:f90c1e29f447557e9e26afb1c4dbf8768a10cc676e3781b6a577841ade126b85", - "sha256:f9613fadad06b4f3bc5db2653ce2f22e0de84a7c6c293909b48f6ed37b83c61f" + "sha256:6f62c13d067b0755ad1c21a34bdd06c0c12625a22b0fc09c6b149816604f7c2a", + "sha256:73ee9fddd406dc318b885c7a2eab8a6472b68b8fb5ba8150949fc3db939f23c8" ], "index": "pypi", - "markers": "python_version >= '3.7'", - "version": "==1.10.8" + "markers": "python_version >= '3.8'", + "version": "==2.8.2" + }, + "pydantic-core": { + "hashes": [ + "sha256:035ede2e16da7281041f0e626459bcae33ed998cca6a0a007a5ebb73414ac72d", + "sha256:04024d270cf63f586ad41fff13fde4311c4fc13ea74676962c876d9577bcc78f", + "sha256:0827505a5c87e8aa285dc31e9ec7f4a17c81a813d45f70b1d9164e03a813a686", + "sha256:084659fac3c83fd674596612aeff6041a18402f1e1bc19ca39e417d554468482", + "sha256:10d4204d8ca33146e761c79f83cc861df20e7ae9f6487ca290a97702daf56006", + "sha256:11b71d67b4725e7e2a9f6e9c0ac1239bbc0c48cce3dc59f98635efc57d6dac83", + "sha256:150906b40ff188a3260cbee25380e7494ee85048584998c1e66df0c7a11c17a6", + "sha256:175873691124f3d0da55aeea1d90660a6ea7a3cfea137c38afa0a5ffabe37b88", + "sha256:177f55a886d74f1808763976ac4efd29b7ed15c69f4d838bbd74d9d09cf6fa86", + "sha256:19c0fa39fa154e7e0b7f82f88ef85faa2a4c23cc65aae2f5aea625e3c13c735a", + "sha256:1eedfeb6089ed3fad42e81a67755846ad4dcc14d73698c120a82e4ccf0f1f9f6", + "sha256:225b67a1f6d602de0ce7f6c1c3ae89a4aa25d3de9be857999e9124f15dab486a", + "sha256:242b8feb3c493ab78be289c034a1f659e8826e2233786e36f2893a950a719bb6", + "sha256:254ec27fdb5b1ee60684f91683be95e5133c994cc54e86a0b0963afa25c8f8a6", + "sha256:25e9185e2d06c16ee438ed39bf62935ec436474a6ac4f9358524220f1b236e43", + "sha256:26ab812fa0c845df815e506be30337e2df27e88399b985d0bb4e3ecfe72df31c", + "sha256:26ca695eeee5f9f1aeeb211ffc12f10bcb6f71e2989988fda61dabd65db878d4", + "sha256:26dc97754b57d2fd00ac2b24dfa341abffc380b823211994c4efac7f13b9e90e", + "sha256:270755f15174fb983890c49881e93f8f1b80f0b5e3a3cc1394a255706cabd203", + "sha256:2aafc5a503855ea5885559eae883978c9b6d8c8993d67766ee73d82e841300dd", + "sha256:2d036c7187b9422ae5b262badb87a20a49eb6c5238b2004e96d4da1231badef1", + "sha256:33499e85e739a4b60c9dac710c20a08dc73cb3240c9a0e22325e671b27b70d24", + "sha256:37eee5b638f0e0dcd18d21f59b679686bbd18917b87db0193ae36f9c23c355fc", + "sha256:38cf1c40a921d05c5edc61a785c0ddb4bed67827069f535d794ce6bcded919fc", + "sha256:3acae97ffd19bf091c72df4d726d552c473f3576409b2a7ca36b2f535ffff4a3", + "sha256:3c5ebac750d9d5f2706654c638c041635c385596caf68f81342011ddfa1e5598", + "sha256:3d482efec8b7dc6bfaedc0f166b2ce349df0011f5d2f1f25537ced4cfc34fd98", + "sha256:407653af5617f0757261ae249d3fba09504d7a71ab36ac057c938572d1bc9331", + "sha256:40a783fb7ee353c50bd3853e626f15677ea527ae556429453685ae32280c19c2", + "sha256:41e81317dd6a0127cabce83c0c9c3fbecceae981c8391e6f1dec88a77c8a569a", + "sha256:41f4c96227a67a013e7de5ff8f20fb496ce573893b7f4f2707d065907bffdbd6", + "sha256:469f29f9093c9d834432034d33f5fe45699e664f12a13bf38c04967ce233d688", + "sha256:4745f4ac52cc6686390c40eaa01d48b18997cb130833154801a442323cc78f91", + "sha256:4868f6bd7c9d98904b748a2653031fc9c2f85b6237009d475b1008bfaeb0a5aa", + "sha256:4aa223cd1e36b642092c326d694d8bf59b71ddddc94cdb752bbbb1c5c91d833b", + "sha256:4dd484681c15e6b9a977c785a345d3e378d72678fd5f1f3c0509608da24f2ac0", + "sha256:4f2790949cf385d985a31984907fecb3896999329103df4e4983a4a41e13e840", + "sha256:512ecfbefef6dac7bc5eaaf46177b2de58cdf7acac8793fe033b24ece0b9566c", + "sha256:516d9227919612425c8ef1c9b869bbbee249bc91912c8aaffb66116c0b447ebd", + "sha256:53e431da3fc53360db73eedf6f7124d1076e1b4ee4276b36fb25514544ceb4a3", + "sha256:595ba5be69b35777474fa07f80fc260ea71255656191adb22a8c53aba4479231", + "sha256:5b5ff4911aea936a47d9376fd3ab17e970cc543d1b68921886e7f64bd28308d1", + "sha256:5d41e6daee2813ecceea8eda38062d69e280b39df793f5a942fa515b8ed67953", + "sha256:5e999ba8dd90e93d57410c5e67ebb67ffcaadcea0ad973240fdfd3a135506250", + "sha256:5f239eb799a2081495ea659d8d4a43a8f42cd1fe9ff2e7e436295c38a10c286a", + "sha256:635fee4e041ab9c479e31edda27fcf966ea9614fff1317e280d99eb3e5ab6fe2", + "sha256:65db0f2eefcaad1a3950f498aabb4875c8890438bc80b19362cf633b87a8ab20", + "sha256:6b507132dcfc0dea440cce23ee2182c0ce7aba7054576efc65634f080dbe9434", + "sha256:6b9d9bb600328a1ce523ab4f454859e9d439150abb0906c5a1983c146580ebab", + "sha256:70c8daf4faca8da5a6d655f9af86faf6ec2e1768f4b8b9d0226c02f3d6209703", + "sha256:77bf3ac639c1ff567ae3b47f8d4cc3dc20f9966a2a6dd2311dcc055d3d04fb8a", + "sha256:784c1214cb6dd1e3b15dd8b91b9a53852aed16671cc3fbe4786f4f1db07089e2", + "sha256:7eb6a0587eded33aeefea9f916899d42b1799b7b14b8f8ff2753c0ac1741edac", + "sha256:7ed1b0132f24beeec5a78b67d9388656d03e6a7c837394f99257e2d55b461611", + "sha256:8ad4aeb3e9a97286573c03df758fc7627aecdd02f1da04516a86dc159bf70121", + "sha256:964faa8a861d2664f0c7ab0c181af0bea66098b1919439815ca8803ef136fc4e", + "sha256:9dc1b507c12eb0481d071f3c1808f0529ad41dc415d0ca11f7ebfc666e66a18b", + "sha256:9ebfef07dbe1d93efb94b4700f2d278494e9162565a54f124c404a5656d7ff09", + "sha256:a45f84b09ac9c3d35dfcf6a27fd0634d30d183205230a0ebe8373a0e8cfa0906", + "sha256:a4f55095ad087474999ee28d3398bae183a66be4823f753cd7d67dd0153427c9", + "sha256:a6d511cc297ff0883bc3708b465ff82d7560193169a8b93260f74ecb0a5e08a7", + "sha256:a8ad4c766d3f33ba8fd692f9aa297c9058970530a32c728a2c4bfd2616d3358b", + "sha256:aa2f457b4af386254372dfa78a2eda2563680d982422641a85f271c859df1987", + "sha256:b03f7941783b4c4a26051846dea594628b38f6940a2fdc0df00b221aed39314c", + "sha256:b0dae11d8f5ded51699c74d9548dcc5938e0804cc8298ec0aa0da95c21fff57b", + "sha256:b91ced227c41aa29c672814f50dbb05ec93536abf8f43cd14ec9521ea09afe4e", + "sha256:bc633a9fe1eb87e250b5c57d389cf28998e4292336926b0b6cdaee353f89a237", + "sha256:bebb4d6715c814597f85297c332297c6ce81e29436125ca59d1159b07f423eb1", + "sha256:c336a6d235522a62fef872c6295a42ecb0c4e1d0f1a3e500fe949415761b8a19", + "sha256:c6514f963b023aeee506678a1cf821fe31159b925c4b76fe2afa94cc70b3222b", + "sha256:c693e916709c2465b02ca0ad7b387c4f8423d1db7b4649c551f27a529181c5ad", + "sha256:c81131869240e3e568916ef4c307f8b99583efaa60a8112ef27a366eefba8ef0", + "sha256:d02a72df14dfdbaf228424573a07af10637bd490f0901cee872c4f434a735b94", + "sha256:d2a8fa9d6d6f891f3deec72f5cc668e6f66b188ab14bb1ab52422fe8e644f312", + "sha256:d2b27e6af28f07e2f195552b37d7d66b150adbaa39a6d327766ffd695799780f", + "sha256:d2fe69c5434391727efa54b47a1e7986bb0186e72a41b203df8f5b0a19a4f669", + "sha256:d3f3ed29cd9f978c604708511a1f9c2fdcb6c38b9aae36a51905b8811ee5cbf1", + "sha256:d573faf8eb7e6b1cbbcb4f5b247c60ca8be39fe2c674495df0eb4318303137fe", + "sha256:e0bbdd76ce9aa5d4209d65f2b27fc6e5ef1312ae6c5333c26db3f5ade53a1e99", + "sha256:e7c4ea22b6739b162c9ecaaa41d718dfad48a244909fe7ef4b54c0b530effc5a", + "sha256:e93e1a4b4b33daed65d781a57a522ff153dcf748dee70b40c7258c5861e1768a", + "sha256:e97fdf088d4b31ff4ba35db26d9cc472ac7ef4a2ff2badeabf8d727b3377fc52", + "sha256:e9fa4c9bf273ca41f940bceb86922a7667cd5bf90e95dbb157cbb8441008482c", + "sha256:eaad4ff2de1c3823fddf82f41121bdf453d922e9a238642b1dedb33c4e4f98ad", + "sha256:f1f62b2413c3a0e846c3b838b2ecd6c7a19ec6793b2a522745b0869e37ab5bc1", + "sha256:f6d6cff3538391e8486a431569b77921adfcdef14eb18fbf19b7c0a5294d4e6a", + "sha256:f9aa05d09ecf4c75157197f27cdc9cfaeb7c5f15021c6373932bf3e124af029f", + "sha256:fa2fddcb7107e0d1808086ca306dcade7df60a13a6c347a7acf1ec139aa6789a", + "sha256:faa6b09ee09433b87992fb5a2859efd1c264ddc37280d2dd5db502126d0e7f27" + ], + "markers": "python_version >= '3.8'", + "version": "==2.20.1" + }, + "pydantic-settings": { + "hashes": [ + "sha256:59b4f431b1defb26fe620c71a7d3968a710d719f5f4cdbbdb7926edeb770f6ef", + "sha256:c509bf79d27563add44e8446233359004ed85066cd096d8b510f715e6ef5d268" + ], + "index": "pypi", + "markers": "python_version >= '3.9'", + "version": "==2.9.1" }, "pygments": { "hashes": [ @@ -1402,12 +1384,12 @@ }, "pyopenssl": { "hashes": [ - "sha256:17ed5be5936449c5418d1cd269a1a9e9081bc54c17aed272b45856a3d3dc86ad", - "sha256:cabed4bfaa5df9f1a16c0ef64a0cb65318b5cd077a7eda7d6970131ca2f41a6f" + "sha256:2b11f239acc47ac2e5aca04fd7fa829800aeee22a2eb30d744572a157bd8a1ab", + "sha256:8d031884482e0c67ee92bf9a4d8cceb08d92aba7136432ffb0703c5280fc205b" ], "index": "pypi", "markers": "python_version >= '3.7'", - "version": "==24.1.0" + "version": "==25.1.0" }, "pyotp": { "hashes": [ @@ -1423,16 +1405,17 @@ "sha256:4a43e969b8f5aaafb2a415536c1a8ec7e341cd6a3f957fd5b5f32a4cfeed902c", "sha256:739c433ba96f078315de54c0db975aee537cbc3e1d0ae4ed9aab0ca1e427e2c1" ], + "index": "pypi", "version": "==0.20220715.0" }, "python-dotenv": { "hashes": [ - "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca", - "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a" + "sha256:41f90bc6f5f177fb41f53e87666db362025010eb28f60a01c9143bfa33a2b2d5", + "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d" ], "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==1.0.1" + "markers": "python_version >= '3.9'", + "version": "==1.1.0" }, "python-engineio": { "hashes": [ @@ -1479,12 +1462,12 @@ }, "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": { "extras": [ @@ -1530,6 +1513,14 @@ "index": "pypi", "version": "==1.44.1" }, + "setuptools": { + "hashes": [ + "sha256:0a6f876d62f4d978ca1a11ab4daf728d1357731f978543ff18ecdbf9fd071f73", + "sha256:b6eca2c3070cdc82f71b4cb4bb2946bc0760a210d11362278cf1ff394e6ea32c" + ], + "markers": "python_version >= '3.9'", + "version": "==75.9.1" + }, "shellingham": { "hashes": [ "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", @@ -1564,59 +1555,62 @@ }, "sqlalchemy": { "hashes": [ - "sha256:0002e829142b2af00b4eaa26c51728f3ea68235f232a2e72a9508a3116bd6ed0", - "sha256:0005bd73026cd239fc1e8ccdf54db58b6193be9a02b3f0c5983808f84862c767", - "sha256:0292f70d1797e3c54e862e6f30ae474014648bc9c723e14a2fda730adb0a9791", - "sha256:036d8472356e1d5f096c5e0e1a7e0f9182140ada3602f8fff6b7329e9e7cfbcd", - "sha256:05f0de3a1dc3810a776275763764bb0015a02ae0f698a794646ebc5fb06fad33", - "sha256:0990932f7cca97fece8017414f57fdd80db506a045869d7ddf2dda1d7cf69ecc", - "sha256:13e397a9371ecd25573a7b90bd037db604331cf403f5318038c46ee44908c44d", - "sha256:14576238a5f89bcf504c5f0a388d0ca78df61fb42cb2af0efe239dc965d4f5c9", - "sha256:199a73c31ac8ea59937cc0bf3dfc04392e81afe2ec8a74f26f489d268867846c", - "sha256:2082a2d2fca363a3ce21cfa3d068c5a1ce4bf720cf6497fb3a9fc643a8ee4ddd", - "sha256:22ff16cedab5b16a0db79f1bc99e46a6ddececb60c396562e50aab58ddb2871c", - "sha256:2307495d9e0ea00d0c726be97a5b96615035854972cc538f6e7eaed23a35886c", - "sha256:2ad2b727fc41c7f8757098903f85fafb4bf587ca6605f82d9bf5604bd9c7cded", - "sha256:2d6495f84c4fd11584f34e62f9feec81bf373787b3942270487074e35cbe5330", - "sha256:361f6b5e3f659e3c56ea3518cf85fbdae1b9e788ade0219a67eeaaea8a4e4d2a", - "sha256:3e2ef592ac3693c65210f8b53d0edcf9f4405925adcfc031ff495e8d18169682", - "sha256:4676d51c9f6f6226ae8f26dc83ec291c088fe7633269757d333978df78d931ab", - "sha256:4ba7e122510bbc07258dc42be6ed45997efdf38129bde3e3f12649be70683546", - "sha256:5102fb9ee2c258a2218281adcb3e1918b793c51d6c2b4666ce38c35101bb940e", - "sha256:5323252be2bd261e0aa3f33cb3a64c45d76829989fa3ce90652838397d84197d", - "sha256:58bb65b3274b0c8a02cea9f91d6f44d0da79abc993b33bdedbfec98c8440175a", - "sha256:59bdc291165b6119fc6cdbc287c36f7f2859e6051dd923bdf47b4c55fd2f8bd0", - "sha256:5facb7fd6fa8a7353bbe88b95695e555338fb038ad19ceb29c82d94f62775a05", - "sha256:639e1ae8d48b3c86ffe59c0daa9a02e2bfe17ca3d2b41611b30a0073937d4497", - "sha256:8eb8897367a21b578b26f5713833836f886817ee2ffba1177d446fa3f77e67c8", - "sha256:90484a2b00baedad361402c257895b13faa3f01780f18f4a104a2f5c413e4536", - "sha256:9c56e19780cd1344fcd362fd6265a15f48aa8d365996a37fab1495cae8fcd97d", - "sha256:b67fc780cfe2b306180e56daaa411dd3186bf979d50a6a7c2a5b5036575cbdbb", - "sha256:c0dcf127bb99458a9d211e6e1f0f3edb96c874dd12f2503d4d8e4f1fd103790b", - "sha256:c23d64a0b28fc78c96289ffbd0d9d1abd48d267269b27f2d34e430ea73ce4b26", - "sha256:ccfd238f766a5bb5ee5545a62dd03f316ac67966a6a658efb63eeff8158a4bbf", - "sha256:cd767cf5d7252b1c88fcfb58426a32d7bd14a7e4942497e15b68ff5d822b41ad", - "sha256:ce8feaa52c1640de9541eeaaa8b5fb632d9d66249c947bb0d89dd01f87c7c288", - "sha256:d2e054aed4645f9b755db85bc69fc4ed2c9020c19c8027976f66576b906a74f1", - "sha256:e16c2be5cb19e2c08da7bd3a87fed2a0d4e90065ee553a940c4fc1a0fb1ab72b", - "sha256:e4b12e3d88a8fffd0b4ca559f6d4957ed91bd4c0613a4e13846ab8729dc5c251", - "sha256:e570cfc40a29d6ad46c9aeaddbdcee687880940a3a327f2c668dd0e4ef0a441d", - "sha256:eb30cf008850c0a26b72bd1b9be6730830165ce049d239cfdccd906f2685f892", - "sha256:f37fa70d95658763254941ddd30ecb23fc4ec0c5a788a7c21034fc2305dab7cc", - "sha256:f5ebeeec5c14533221eb30bad716bc1fd32f509196318fb9caa7002c4a364e4c", - "sha256:f5fa526d027d804b1f85cdda1eb091f70bde6fb7d87892f6dd5a48925bc88898" + "sha256:02d2ecb9508f16ab9c5af466dfe5a88e26adf2e1a8d1c56eb616396ccae2c186", + "sha256:0b76bbb1cbae618d10679be8966f6d66c94f301cfc15cb49e2f2382563fb6efb", + "sha256:0de620f978ca273ce027769dc8db7e6ee72631796187adc8471b3c76091b809e", + "sha256:1183599e25fa38a1a322294b949da02b4f0da13dbc2688ef9dbe746df573f8a6", + "sha256:12bc0141b245918b80d9d17eca94663dbd3f5266ac77a0be60750f36102bbb0f", + "sha256:1390ca2d301a2708fd4425c6d75528d22f26b8f5cbc9faba1ddca136671432bc", + "sha256:13e91d6892b5fcb94a36ba061fb7a1f03d0185ed9d8a77c84ba389e5bb05e936", + "sha256:14b3f4783275339170984cadda66e3ec011cce87b405968dc8d51cf0f9997b0d", + "sha256:1576fba3616f79496e2f067262200dbf4aab1bb727cd7e4e006076686413c80c", + "sha256:1990d5a6a5dc358a0894c8ca02043fb9a5ad9538422001fb2826e91c50f1d539", + "sha256:1d83cd1cc03c22d922ec94d0d5f7b7c96b1332f5e122e81b1a61fb22da77879a", + "sha256:1e8c1b9ecaf9f2590337d5622189aeb2f0dbc54ba0232fa0856cf390957584a9", + "sha256:26e78444bc77d089e62874dc74df05a5c71f01ac598010a327881a48408d0064", + "sha256:2b37931eac4b837c45e2522066bda221ac6d80e78922fb77c75eb12e4dbcdee5", + "sha256:3112de9e11ff1957148c6de1df2bc5cc1440ee36783412e5eedc6f53638a577d", + "sha256:394b0135900b62dbf63e4809cdc8ac923182af2816d06ea61cd6763943c2cc05", + "sha256:3f01c2629a7d6b30d8afe0326b8c649b74825a0e1ebdcb01e8ffd1c920deb07d", + "sha256:41cffc63c7c83dfc30c4cab5b4308ba74440a9633c4509c51a0c52431fb0f8ab", + "sha256:4470fbed088c35dc20b78a39aaf4ae54fe81790c783b3264872a0224f437c31a", + "sha256:5ed3576675c187e3baa80b02c4c9d0edfab78eff4e89dd9da736b921333a2432", + "sha256:6b24364150738ce488333b3fb48bfa14c189a66de41cd632796fbcacb26b4585", + "sha256:6da60fb24577f989535b8fc8b2ddc4212204aaf02e53c4c7ac94ac364150ed08", + "sha256:76c2ba7b5a09863d0a8166fbc753af96d561818c572dbaf697c52095938e7be4", + "sha256:954816850777ac234a4e32b8c88ac1f7847088a6e90cfb8f0e127a1bf3feddff", + "sha256:9c24dd161c06992ed16c5e528a75878edbaeced5660c3db88c820f1f0d3fe1f4", + "sha256:a01bc25eb7a5688656c8770f931d5cb4a44c7de1b3cec69b84cc9745d1e4cc10", + "sha256:a19f816f4702d7b1951d7576026c7124b9bfb64a9543e571774cf517b7a50b29", + "sha256:a41611835010ed4ea4c7aed1da5b58aac78ee7e70932a91ed2705a7b38e40f52", + "sha256:a49730afb716f3f675755afec109895cab95bc9875db7ffe2e42c1b1c6279482", + "sha256:a86b0e4be775902a5496af4fb1b60d8a2a457d78f531458d294360b8637bb014", + "sha256:a8a72259a1652f192c68377be7011eac3c463e9892ef2948828c7d58e4829988", + "sha256:af00236fe21c4d4f4c227b6ccc19b44c594160cc3ff28d104cdce85855369277", + "sha256:b05e0626ec1c391432eabb47a8abd3bf199fb74bfde7cc44a26d2b1b352c2c6e", + "sha256:b5933c45d11cbd9694b1540aa9076816cc7406964c7b16a380fd84d3a5fe3241", + "sha256:b5e0d47d619c739bdc636bbe007da4519fc953393304a5943e0b5aec96c9877c", + "sha256:b67589f7955924865344e6eacfdcf70675e64f36800a576aa5e961f0008cde2a", + "sha256:c5a2530400a6e7e68fd1552a55515de6a4559122e495f73554a51cedafc11669", + "sha256:cafe0ba3a96d0845121433cffa2b9232844a2609fce694fcc02f3f31214ece28", + "sha256:cdb2886c0be2c6c54d0651d5a61c29ef347e8eec81fd83afebbf7b59b80b7393", + "sha256:d0cf7076c8578b3de4e43a046cc7a1af8466e1c3f5e64167189fe8958a4f9c02", + "sha256:f1e1b92ee4ee9ffc68624ace218b89ca5ca667607ccee4541a90cc44999b9aea", + "sha256:f941aaf15f47f316123e1933f9ea91a6efda73a161a6ab6046d1cde37be62c88", + "sha256:fb59a11689ff3c58e7652260127f9e34f7f45478a2f3ef831ab6db7bcd72108f", + "sha256:fc9ffd9a38e21fad3e8c5a88926d57f94a32546e937e0be46142b2702003eba7" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==1.4.41" + "version": "==1.4.54" }, "starlette": { "hashes": [ - "sha256:6fe59f29268538e5d0d182f2791a479a0c64638e6935d1c6989e63fb2699c6ee", - "sha256:9af890290133b79fc3db55474ade20f6220a364a0402e0b556e7cd5e1e093823" + "sha256:595633ce89f8ffa71a015caed34a5b2dc1c0cdb3f0f1fbd1e69339cf2abeec35", + "sha256:7f7361f34eed179294600af672f565727419830b54b7b084efe44bb82d2fccd5" ], "index": "pypi", - "markers": "python_version >= '3.8'", - "version": "==0.37.2" + "markers": "python_version >= '3.9'", + "version": "==0.46.2" }, "thumbhash-python": { "hashes": [ @@ -1640,11 +1634,19 @@ }, "typing-extensions": { "hashes": [ - "sha256:83f085bd5ca59c80295fc2a82ab5dac679cbe02b9f33f7d83af68e241bea51b0", - "sha256:c1f94d72897edaf4ce775bb7558d5b79d8126906a14ea5ed1635921406c0387a" + "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c", + "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef" ], "markers": "python_version >= '3.8'", - "version": "==4.11.0" + "version": "==4.13.2" + }, + "typing-inspection": { + "hashes": [ + "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", + "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122" + ], + "markers": "python_version >= '3.9'", + "version": "==0.4.0" }, "urllib3": { "hashes": [ @@ -1668,11 +1670,11 @@ }, "webauthn": { "hashes": [ - "sha256:53914b8529df3e70bec0e41438eea8d99442ad3e54e32b86ce1ba83513bcf30e", - "sha256:caca3cc62045923c542819169c22122bdb81efa0246c9d5208d073942d806bf3" + "sha256:13592ee71489b571cb6e4a5d8b3c34f7b040cd3539a9d94b6b7d23fa88df5dfb", + "sha256:24eda57903897369797f52a377f8c470e7057e79da5525779d0720a9fcc11926" ], "index": "pypi", - "version": "==1.8.1" + "version": "==1.11.1" }, "webencodings": { "hashes": [ diff --git a/classquiz/cache.py b/classquiz/cache.py index 2aee0c9..8d4004f 100644 --- a/classquiz/cache.py +++ b/classquiz/cache.py @@ -16,7 +16,7 @@ settings = settings() async def cache_account(criteria: str, content: str) -> Union[User, None]: async def insert_into_redis(usermodel: User, key: str): - await redis.set(key, usermodel.json(), ex=settings.cache_expiry) + await redis.set(key, usermodel.model_dump_json(), ex=settings.cache_expiry) if criteria == "email": try: @@ -54,7 +54,7 @@ async def get_from_redis(key: str) -> Union[None, User]: if user is None: return None else: - return User.parse_obj(loads(user)) + return User.model_validate(loads(user)) async def get_cache(criteria: str, content: str) -> User: diff --git a/classquiz/config.py b/classquiz/config.py index b6f7635..0c98ec7 100644 --- a/classquiz/config.py +++ b/classquiz/config.py @@ -7,7 +7,8 @@ from functools import lru_cache from redis import asyncio as redis_lib import redis as redis_base_lib -from pydantic import BaseSettings, RedisDsn, PostgresDsn, BaseModel +from pydantic import RedisDsn, PostgresDsn, BaseModel +from pydantic_settings import BaseSettings import meilisearch as MeiliSearch from typing import Optional from arq import create_pool @@ -17,7 +18,7 @@ from classquiz.storage import Storage class CustomOpenIDProvider(BaseModel): - scopes: str = "openid email profile" + scopes: str | None = "openid email profile" server_metadata_url: str client_id: str client_secret: str @@ -72,6 +73,7 @@ class Settings(BaseSettings): env_file = ".env" env_file_encoding = "utf-8" env_nested_delimiter = "__" + extra = "allow" async def initialize_arq(): @@ -85,7 +87,7 @@ def settings() -> Settings: return Settings() -pool = redis_lib.ConnectionPool().from_url(settings().redis) +pool = redis_lib.ConnectionPool().from_url(str(settings().redis)) redis: redis_base_lib.client.Redis = redis_lib.Redis(connection_pool=pool) arq: ArqRedis = ArqRedis(pool_or_conn=pool) diff --git a/classquiz/db/models.py b/classquiz/db/models.py index 2a9902f..ac9d287 100644 --- a/classquiz/db/models.py +++ b/classquiz/db/models.py @@ -10,7 +10,7 @@ from typing import Optional import ormar from ormar import ReferentialAction -from pydantic import BaseModel, Json, validator +from pydantic import BaseModel, Json, field_validator, ConfigDict, RootModel from enum import Enum from . import metadata, database from .quiztivity import QuizTivityPage @@ -45,13 +45,13 @@ class User(ormar.Model): totp_secret: str = ormar.String(max_length=32, min_length=32, nullable=True, default=None) storage_used: int = ormar.BigInteger(nullable=False, default=0, minimum=0) - class Meta: - tablename = "users" - metadata = metadata - database = database + ormar_config = ormar.OrmarConfig( + tablename="users", + metadata=metadata, + database=database, + ) - class Config: - use_enum_values = True + model_config = ConfigDict(use_enum_values=True) class FidoCredentials(ormar.Model): @@ -61,20 +61,22 @@ class FidoCredentials(ormar.Model): sign_count: int = ormar.Integer() user: Optional[User] = ormar.ForeignKey(User, ondelete=ReferentialAction.CASCADE) - class Meta: - tablename = "fido_credentials" - metadata = metadata - database = database + ormar_config = ormar.OrmarConfig( + tablename="fido_credentials", + metadata=metadata, + database=database, + ) class ApiKey(ormar.Model): key: str = ormar.String(max_length=48, min_length=48, primary_key=True) user: Optional[User] = ormar.ForeignKey(User, ondelete=ReferentialAction.CASCADE) - class Meta: - tablename = "api_keys" - metadata = metadata - database = database + ormar_config = ormar.OrmarConfig( + tablename="api_keys", + metadata=metadata, + database=database, + ) class UserSession(ormar.Model): @@ -90,10 +92,11 @@ class UserSession(ormar.Model): user_agent: str = ormar.String(max_length=255, nullable=True) last_seen: datetime = ormar.DateTime(default=datetime.now()) - class Meta: - tablename = "user_sessions" - metadata = metadata - database = database + ormar_config = ormar.OrmarConfig( + tablename="user_sessions", + metadata=metadata, + database=database, + ) class ABCDQuizAnswer(BaseModel): @@ -137,7 +140,7 @@ class QuizQuestion(BaseModel): answers: list[ABCDQuizAnswer] | RangeQuizAnswer | list[TextQuizAnswer] | list[VotingQuizAnswer] | str image: str | None = None - @validator("answers") + @field_validator("answers") def answers_not_none_if_abcd_type(cls, v, values): if values["type"] == QuizQuestionType.ABCD and not isinstance(v[0], ABCDQuizAnswer): raise ValueError("Answers can't be none if type is ABCD") @@ -157,13 +160,13 @@ class QuizQuestion(BaseModel): class QuizInput(BaseModel): - public: bool = False + public: bool | None = False title: str description: str - cover_image: str | None - background_color: str | None + cover_image: str | None = None + background_color: str | None = None questions: list[QuizQuestion] - background_image: str | None + background_image: str | None = None class Quiz(ormar.Model): @@ -186,19 +189,21 @@ class Quiz(ormar.Model): views: int = ormar.Integer(nullable=False, default=0, server_default="0") mod_rating: int | None = ormar.SmallInteger(nullable=True) - class Meta: - tablename = "quiz" - metadata = metadata - database = database + ormar_config = ormar.OrmarConfig( + tablename="quiz", + metadata=metadata, + database=database, + ) class InstanceData(ormar.Model): instance_id: uuid.UUID = ormar.UUID(primary_key=True, default=uuid.uuid4(), nullable=False, unique=True) - class Meta: - tablename = "instance_data" - metadata = metadata - database = database + ormar_config = ormar.OrmarConfig( + tablename="instance_data", + metadata=metadata, + database=database, + ) class Token(BaseModel): @@ -228,18 +233,18 @@ class PlayGame(BaseModel): game_pin: str started: bool = False captcha_enabled: bool = False - cover_image: str | None - game_mode: str | None + cover_image: str | None = None + game_mode: str | None = None current_question: int = -1 - background_color: str | None - background_image: str | None - custom_field: str | None + background_color: str | None = None + background_image: str | None = None + custom_field: str | None = None question_show: bool = False class GamePlayer(BaseModel): username: str - sid: str | None + sid: str | None = None class GameAnswer2(BaseModel): @@ -273,9 +278,7 @@ class AnswerData(BaseModel): score: int -class AnswerDataList(BaseModel): - # Just a method to make a top-level list - __root__: list[AnswerData] +AnswerDataList = RootModel[list[AnswerData]] class GameInLobby(BaseModel): @@ -307,10 +310,11 @@ class GameResults(ormar.Model): description: str = ormar.Text(nullable=False) questions: Json[list[QuizQuestion]] = ormar.JSON(nullable=False) - class Meta: - tablename = "game_results" - metadata = metadata - database = database + ormar_config = ormar.OrmarConfig( + tablename="game_results", + metadata=metadata, + database=database, + ) class QuizTivityInput(BaseModel): @@ -325,10 +329,11 @@ class QuizTivity(ormar.Model): user: User | None = ormar.ForeignKey(User, ondelete=ReferentialAction.CASCADE) pages: list[QuizTivityPage] = ormar.JSON(nullable=False) - class Meta: - tablename = "quiztivitys" - metadata = metadata - database = database + ormar_config = ormar.OrmarConfig( + tablename="quiztivitys", + metadata=metadata, + database=database, + ) class QuizTivityShare(ormar.Model): @@ -338,10 +343,11 @@ class QuizTivityShare(ormar.Model): quiztivity: QuizTivity | None = ormar.ForeignKey(QuizTivity, ondelete=ReferentialAction.CASCADE) user: User | None = ormar.ForeignKey(User, ondelete=ReferentialAction.CASCADE) - class Meta: - tablename = "quiztivityshares" - metadata = metadata - database = database + ormar_config = ormar.OrmarConfig( + tablename="quiztivityshares", + metadata=metadata, + database=database, + ) class OnlyId(BaseModel): @@ -350,8 +356,8 @@ class OnlyId(BaseModel): class PublicQuizTivityShare(BaseModel): id: uuid.UUID - name: str | None - expire_in: int | None + name: str | None = None + expire_in: int | None = None quiztivity: OnlyId user: OnlyId @@ -386,10 +392,11 @@ class StorageItem(ormar.Model): server: str | None = ormar.Text(default=None, nullable=True) imported: bool = ormar.Boolean(default=False, nullable=True) - class Meta: - tablename = "storage_items" - metadata = metadata - database = database + ormar_config = ormar.OrmarConfig( + tablename="storage_items", + metadata=metadata, + database=database, + ) class PublicStorageItem(BaseModel): @@ -398,11 +405,11 @@ class PublicStorageItem(BaseModel): mime_type: str hash: str | None size: int - deleted_at: datetime | None - alt_text: str | None - filename: str | None - thumbhash: str | None - server: str | None + deleted_at: datetime | None = None + alt_text: str | None = None + filename: str | None = None + thumbhash: str | None = None + server: str | None = None imported: bool @classmethod @@ -458,8 +465,8 @@ class PrivateStorageItem(PublicStorageItem): class UpdateStorageItem(BaseModel): - filename: str | None - alt_text: str | None + filename: str | None = None + alt_text: str | None = None class Controller(ormar.Model): @@ -473,10 +480,11 @@ class Controller(ormar.Model): os_version: str | None = ormar.Text(nullable=True) wanted_os_version: str = ormar.Text(nullable=True, default=None) - class Meta: - tablename = "controller" - metadata = metadata - database = database + ormar_config = ormar.OrmarConfig( + tablename="controller", + metadata=metadata, + database=database, + ) class Rating(ormar.Model): @@ -486,7 +494,8 @@ class Rating(ormar.Model): created_at: datetime = ormar.DateTime(nullable=False, server_default=func.now()) quiz: uuid.UUID | Quiz = ormar.ForeignKey(Quiz) - class Meta: - tablename = "rating" - metadata = metadata - database = database + ormar_config = ormar.OrmarConfig( + tablename="rating", + metadata=metadata, + database=database, + ) diff --git a/classquiz/db/quiztivity.py b/classquiz/db/quiztivity.py index 4eb283d..191c823 100644 --- a/classquiz/db/quiztivity.py +++ b/classquiz/db/quiztivity.py @@ -12,8 +12,8 @@ class Pdf(BaseModel): class _MemoryCard(BaseModel): - image: str | None - text: str | None + image: str | None = None + text: str | None = None id: str @@ -53,6 +53,6 @@ TYPE_CLASS_LIST = { class QuizTivityPage(BaseModel): - title: str | None + title: str | None = None type: QuizTivityTypes data: Pdf | Memory | Markdown | Abcd diff --git a/classquiz/helpers/__init__.py b/classquiz/helpers/__init__.py index 7a825d9..0d3bc6c 100644 --- a/classquiz/helpers/__init__.py +++ b/classquiz/helpers/__init__.py @@ -166,7 +166,7 @@ async def handle_import_from_excel(data: BinaryIO, user: User) -> Quiz: answers=answers_list, time=str(time), image=existing_question["image"] if existing_question is not None else None, - ).dict() + ).model_dump() ) if existing_quiz is None: quiz = Quiz( diff --git a/classquiz/helpers/pixabay.py b/classquiz/helpers/pixabay.py index cbc193e..b724c9c 100644 --- a/classquiz/helpers/pixabay.py +++ b/classquiz/helpers/pixabay.py @@ -127,9 +127,9 @@ class NotFoundError(Exception): async def get_images(api_key: str, params: GetImagesParams) -> GetImagesResponse: async with ( ClientSession() as session, - session.get("https://pixabay.com/api/", params={"key": api_key, **params.dict()}) as resp, + session.get("https://pixabay.com/api/", params={"key": api_key, **params.model_dump()}) as resp, ): if resp.status == 200: - return GetImagesResponse.parse_obj(await resp.json()) + return GetImagesResponse.model_validate(await resp.json()) else: raise NotFoundError diff --git a/classquiz/kahoot_importer/README.md b/classquiz/kahoot_importer/README.md index 3290789..917c947 100644 --- a/classquiz/kahoot_importer/README.md +++ b/classquiz/kahoot_importer/README.md @@ -20,7 +20,7 @@ from asyncio import run async def main(): kahoot_quiz: _Response = await get("GAME_ID") - print(kahoot_quiz.json(exclude={"kahoot"})) + print(kahoot_quiz.model_dump_json(exclude={"kahoot"})) run(main()) ``` @@ -35,7 +35,7 @@ from asyncio import run async def main(): kahoot_quizzes: _Response = await search("QUERY") - print(kahoot_quizzes.json()) + print(kahoot_quizzes.model_dump_json()) run(main()) ``` diff --git a/classquiz/kahoot_importer/__init__.py b/classquiz/kahoot_importer/__init__.py index 63056c7..a46b7e1 100644 --- a/classquiz/kahoot_importer/__init__.py +++ b/classquiz/kahoot_importer/__init__.py @@ -10,19 +10,19 @@ from pydantic import BaseModel class _CoverMetadata(BaseModel): - id: UUID | None - resources: str | None + id: UUID | None = None + resources: str | None = None class _CreatorAvatar(BaseModel): - url: str | None - id: UUID | None - type: str | None - bitmojiAvatarId: str | None - altText: str | None - contentType: str | None - width: int | None - height: int | None + url: str | None = None + id: UUID | None = None + type: str | None = None + bitmojiAvatarId: str | None = None + altText: str | None = None + contentType: str | None = None + width: int | None = None + height: int | None = None class _LastEdit(BaseModel): @@ -32,7 +32,7 @@ class _LastEdit(BaseModel): class _ImageMetadata(BaseModel): - id: UUID | None + id: UUID | None = None content_type: Optional[str] width: Optional[int] height: Optional[int] @@ -40,11 +40,11 @@ class _ImageMetadata(BaseModel): class _SampleQuestion(BaseModel): - image: str | None - imageMetadata: _ImageMetadata | None + image: str | None = None + imageMetadata: _ImageMetadata | None = None title: str type: str - time: int | None + time: int | None = None class _Access(BaseModel): @@ -57,19 +57,19 @@ class _Card(BaseModel): title: str description: str slug: str - cover: str | None - coverMetadata: _CoverMetadata | dict[None, None] | None + cover: str | None = None + coverMetadata: _CoverMetadata | dict[None, None] | None = None draftExists: bool inventoryItemIds: List[Any] number_of_questions: int creator: UUID creator_username: str - creator_avatar: _CreatorAvatar | dict[None, None] | None + creator_avatar: _CreatorAvatar | dict[None, None] | None = None badges: List[str] visibility: int locked: bool writeProtection: bool - lastEdit: _LastEdit | None + lastEdit: _LastEdit | None = None featured: bool young_featured: bool sponsored: bool @@ -111,8 +111,8 @@ class _LanguageInfo(BaseModel): class _Metadata(BaseModel): access: _Access - duplicationProtection: bool | None - lastEdit: _LastEdit | None + duplicationProtection: bool | None = None + lastEdit: _LastEdit | None = None class _Parent(BaseModel): @@ -123,7 +123,7 @@ class _Parent(BaseModel): class _Choice(BaseModel): answer: str correct: bool - languageInfo: _LanguageInfo | None + languageInfo: _LanguageInfo | None = None class _Video(BaseModel): @@ -141,12 +141,12 @@ class _Question(BaseModel): points: bool pointsMultiplier: int choices: List[_Choice] - image: str | None - imageMetadata: _ImageMetadata | None + image: str | None = None + imageMetadata: _ImageMetadata | None = None resources: Optional[str] video: _Video questionFormat: int - languageInfo: _LanguageInfo | None + languageInfo: _LanguageInfo | None = None media: List[Any] @@ -157,23 +157,23 @@ class _Kahoot(BaseModel): creator_username: str compatibilityLevel: int creator_primary_usage: str - folderId: UUID | None + folderId: UUID | None = None visibility: int - difficulty: int | None + difficulty: int | None = None audience: str audience: str title: str description: str quizType: str - tags: str | None | List[str] - cover: str | None - coverMetadata: _CoverMetadata | dict[None, None] | None + tags: str | None | List[str] = None + cover: str | None = None + coverMetadata: _CoverMetadata | dict[None, None] | None = None questions: List[_Question] metadata: _Metadata - parent: _Parent | None - resources: str | None + parent: _Parent | None = None + resources: str | None = None slug: str - languageInfo: _LanguageInfo | None + languageInfo: _LanguageInfo | None = None inventoryItemIds: List[Any] type: str created: int diff --git a/classquiz/kahoot_importer/import_quiz.py b/classquiz/kahoot_importer/import_quiz.py index 65f99b8..87cf75d 100644 --- a/classquiz/kahoot_importer/import_quiz.py +++ b/classquiz/kahoot_importer/import_quiz.py @@ -87,7 +87,7 @@ async def import_quiz(quiz_id: str, user: User) -> Quiz | int: answers=answers, time=str(q.time / 1000), image=image, - ).dict() + ).model_dump() ) cover = None if quiz.kahoot.cover != "" and quiz.kahoot.cover is not None: diff --git a/classquiz/kahoot_importer/search.py b/classquiz/kahoot_importer/search.py index 201dac7..fc4565a 100644 --- a/classquiz/kahoot_importer/search.py +++ b/classquiz/kahoot_importer/search.py @@ -15,7 +15,7 @@ from classquiz.kahoot_importer import _Entity class _Response(BaseModel): entities: List[_Entity] totalHits: int - cursor: int | None + cursor: int | None = None pageTimestamp: int diff --git a/classquiz/oauth/github.py b/classquiz/oauth/github.py index 1d173e8..c95a618 100644 --- a/classquiz/oauth/github.py +++ b/classquiz/oauth/github.py @@ -35,43 +35,43 @@ class Plan(BaseModel): class GitHubOauthResponse(BaseModel): login: str id: int - email: Optional[str] - node_id: Optional[str] - avatar_url: Optional[str] - gravatar_id: Optional[str] - url: Optional[str] - html_url: Optional[str] - followers_url: Optional[str] - following_url: Optional[str] - gists_url: Optional[str] - starred_url: Optional[str] - subscriptions_url: Optional[str] - organizations_url: Optional[str] - repos_url: Optional[str] - events_url: Optional[str] - received_events_url: Optional[str] - type: Optional[str] - site_admin: Optional[str] - name: Optional[str] - company: Optional[str] - blog: Optional[str] - location: Optional[str] - hireable: Optional[bool] - bio: Optional[str] - twitter_username: Optional[str] - public_repos: Optional[int] - public_gists: Optional[int] - followers: Optional[int] - following: Optional[int] + email: Optional[str] = None + node_id: Optional[str] = None + avatar_url: Optional[str] = None + gravatar_id: Optional[str] = None + url: Optional[str] = None + html_url: Optional[str] = None + followers_url: Optional[str] = None + following_url: Optional[str] = None + gists_url: Optional[str] = None + starred_url: Optional[str] = None + subscriptions_url: Optional[str] = None + organizations_url: Optional[str] = None + repos_url: Optional[str] = None + events_url: Optional[str] = None + received_events_url: Optional[str] = None + type: Optional[str] = None + site_admin: Optional[str] = None + name: Optional[str] = None + company: Optional[str] = None + blog: Optional[str] = None + location: Optional[str] = None + hireable: Optional[bool] = None + bio: Optional[str] = None + twitter_username: Optional[str] = None + public_repos: Optional[int] = None + public_gists: Optional[int] = None + followers: Optional[int] = None + following: Optional[int] = None created_at: datetime updated_at: datetime - private_gists: Optional[int] - total_private_repos: Optional[int] - owned_private_repos: Optional[int] - disk_usage: Optional[int] - collaborators: Optional[int] - two_factor_authentication: Optional[bool] - plan: Optional[Plan] + private_gists: Optional[int] = None + total_private_repos: Optional[int] = None + owned_private_repos: Optional[int] = None + disk_usage: Optional[int] = None + collaborators: Optional[int] = None + two_factor_authentication: Optional[bool] = None + plan: Optional[Plan] = None @router.get("/login") diff --git a/classquiz/routers/box_controller/embedded/__init__.py b/classquiz/routers/box_controller/embedded/__init__.py index 92e88e2..1683f3c 100644 --- a/classquiz/routers/box_controller/embedded/__init__.py +++ b/classquiz/routers/box_controller/embedded/__init__.py @@ -35,7 +35,7 @@ async def join_game(data: JoinGameInput) -> JoinGameResponse: if game_pin is None: raise HTTPException(status_code=404, detail="Game not found") game = await redis.get(f"game:{game_pin}") - game = PlayGame.parse_raw(game) + game = PlayGame.model_validate_json(game) # Check if game is already running if game.started: raise HTTPException(status_code=400, detail="Game started already") diff --git a/classquiz/routers/box_controller/embedded/socket.py b/classquiz/routers/box_controller/embedded/socket.py index 390fffe..11b9731 100644 --- a/classquiz/routers/box_controller/embedded/socket.py +++ b/classquiz/routers/box_controller/embedded/socket.py @@ -25,7 +25,7 @@ async def submit_answer_fn(data_answer: int, game_pin: str, player_id: str, now: username = await redis.get(f"game:cqc:player:{player_id}") if redis_res_game is None or username is None: raise HTTPException(status_code=404, detail="id not existent") - game = PlayGame.parse_raw(redis_res_game) + game = PlayGame.model_validate_json(redis_res_game) if not game.question_show: return @@ -96,7 +96,7 @@ async def websocket_endpoint(ws: WebSocket, game_id: str): player_id, game_pin = game_id.split(":") if player_id is None or game_pin is None: - await ws.send_text(WebSocketRequest(type=WebSocketTypes.Error, data="BadId").json()) + await ws.send_text(WebSocketRequest(type=WebSocketTypes.Error, data="BadId").model_dump_json()) await ws.close(code=status.WS_1003_UNSUPPORTED_DATA) username = await redis.get(f"game:cqc:player:{player_id}") await sio.emit( @@ -104,17 +104,19 @@ async def websocket_endpoint(ws: WebSocket, game_id: str): {"username": username, "sid": None}, room=f"admin:{game_pin}", ) - await redis.sadd(f"game_session:{game_pin}:players", GamePlayer(username=username, sid=None).json()) + await redis.sadd(f"game_session:{game_pin}:players", GamePlayer(username=username, sid=None).model_dump_json()) while True: raw_data = await ws.receive_text() try: - data = WebSocketRequest.parse_raw(raw_data) + data = WebSocketRequest.model_validate_json(raw_data) except ValidationError as e: print("ValError") print(e) print(raw_data) - await ws.send_text(WebSocketRequest(type=WebSocketTypes.Error, data="ValidationError").json()) + await ws.send_text( + WebSocketRequest(type=WebSocketTypes.Error, data="ValidationError").model_dump_json() + ) continue if data.type == WebSocketTypes.ButtonPress: @@ -122,7 +124,9 @@ async def websocket_endpoint(ws: WebSocket, game_id: str): try: answer_index = button_to_index_map[data.data.lower()] except (KeyError, AttributeError): - await ws.send_text(WebSocketRequest(type=WebSocketTypes.Error, data="InvalidButton").json()) + await ws.send_text( + WebSocketRequest(type=WebSocketTypes.Error, data="InvalidButton").model_dump_json() + ) continue await submit_answer_fn(answer_index, game_pin, player_id, now) diff --git a/classquiz/routers/box_controller/web.py b/classquiz/routers/box_controller/web.py index 14c60ad..2a51f44 100644 --- a/classquiz/routers/box_controller/web.py +++ b/classquiz/routers/box_controller/web.py @@ -18,7 +18,7 @@ router = APIRouter() class SetControllerUpInput(BaseModel): - player_name: str | None + player_name: str | None = None name: str @@ -57,7 +57,7 @@ async def get_controller(id: uuid.UUID, user: User = Depends(get_current_user)) controller = await Controller.objects.get_or_none(id=id, user=user.id) if controller is None: raise HTTPException(status_code=404, detail="Controller not found") - return GetControllerResponse(**controller.dict()) + return GetControllerResponse(**controller.model_dump()) class ModifyControllerInput(BaseModel): @@ -76,7 +76,7 @@ async def modify_controller( controller.player_name = data.player_name controller.name = data.name await controller.update() - return GetControllerResponse(**controller.dict()) + return GetControllerResponse(**controller.model_dump()) @router.get("/list") @@ -86,7 +86,7 @@ async def get_all_controllers(user: User = Depends(get_current_user)) -> list[Ge return [] return_list = [] for controller in controllers: - return_list.append(GetControllerResponse(**controller.dict())) + return_list.append(GetControllerResponse(**controller.model_dump())) return return_list diff --git a/classquiz/routers/editor.py b/classquiz/routers/editor.py index 2e5c496..cf167d7 100644 --- a/classquiz/routers/editor.py +++ b/classquiz/routers/editor.py @@ -60,7 +60,9 @@ async def init_editor(edit: bool, quiz_id: Optional[UUID] = None, user: User = D edit_id = os.urandom(4).hex() await redis.sadd("edit_sessions", edit_id) await redis.set( - f"edit_session:{edit_id}", EditSessionData(quiz_id=quiz_id, edit=edit, user_id=user.id).json(), ex=3600 + f"edit_session:{edit_id}", + EditSessionData(quiz_id=quiz_id, edit=edit, user_id=user.id).model_dump_json(), + ex=3600, ) return InitEditorResponse(token=edit_id) @@ -70,7 +72,7 @@ async def finish_edit(edit_id: str, quiz_input: QuizInput): session_data = await redis.get(f"edit_session:{edit_id}") if session_data is None: raise HTTPException(status_code=401, detail="Edit ID not found!") - session_data = EditSessionData.parse_raw(session_data) + session_data = EditSessionData.model_validate_json(session_data) quiz_input.title = bleach.clean(quiz_input.title, tags=ALLOWED_TAGS_FOR_QUIZ, strip=True) quiz_input.description = bleach.clean(quiz_input.description, tags=ALLOWED_TAGS_FOR_QUIZ, strip=True) if quiz_input.background_color is not None: @@ -122,7 +124,7 @@ async def finish_edit(edit_id: str, quiz_input: QuizInput): quiz.public = quiz_input.public quiz.description = quiz_input.description quiz.updated_at = datetime.now() - quiz.questions = quiz_input.dict()["questions"] + quiz.questions = quiz_input.model_dump()["questions"] quiz.cover_image = quiz_input.cover_image quiz.background_color = quiz_input.background_color quiz.background_image = quiz_input.background_image @@ -140,7 +142,7 @@ async def finish_edit(edit_id: str, quiz_input: QuizInput): return quiz else: quiz = Quiz( - **quiz_input.dict(), + **quiz_input.model_dump(), user_id=session_data.user_id, id=session_data.quiz_id, created_at=datetime.now(), diff --git a/classquiz/routers/eximport.py b/classquiz/routers/eximport.py index 6c289d3..10f1888 100644 --- a/classquiz/routers/eximport.py +++ b/classquiz/routers/eximport.py @@ -52,7 +52,7 @@ async def export_quiz(quiz_id: uuid.UUID, user: User = Depends(get_current_user) if quiz.cover_image is not None: image_urls[-1] = quiz.cover_image quiz.cover_image = -1 - quiz_dict = quiz.dict() + quiz_dict = quiz.model_dump() del quiz_dict["user_id"], quiz_dict["id"] quiz_dict["created_at"] = quiz_dict["created_at"].isoformat() quiz_dict["updated_at"] = quiz_dict["updated_at"].isoformat() @@ -129,7 +129,7 @@ async def import_quiz(file: UploadFile = File(), user: User = Depends(get_curren question["image"] = image_urls[question["image"]] if quiz_dict["cover_image"] is not None: quiz_dict["cover_image"] = image_urls[-1] - quiz = Quiz.parse_obj(quiz_dict) + quiz = Quiz.model_validate(quiz_dict) quiz.user_id = user.id quiz.imported_from_kahoot = None quiz.mod_rating = None @@ -165,7 +165,7 @@ async def export_quiz_as_excel(quiz_id: uuid.UUID, user: User = Depends(get_curr ], ) for i, question in enumerate(quiz.questions): - question = QuizQuestion.parse_obj(question) + question = QuizQuestion.model_validate(question) data: list[Any] = [None] * 9 data[0] = i + 1 data[1] = question.question diff --git a/classquiz/routers/live.py b/classquiz/routers/live.py index 14a3efe..9e6bb8c 100644 --- a/classquiz/routers/live.py +++ b/classquiz/routers/live.py @@ -7,7 +7,7 @@ import json import uuid from fastapi import APIRouter, HTTPException -from pydantic import BaseModel, validator +from pydantic import BaseModel, field_validator from classquiz.config import settings, redis from classquiz.db.models import ( PlayGame, @@ -41,7 +41,7 @@ class _ABCDQuizAnswer(ABCDQuizAnswer): class _QuizQuestion(QuizQuestion): answers: list[ABCDQuizAnswer] | RangeQuizAnswer | list[VotingQuizAnswer] - @validator("answers") + @field_validator("answers") def answers_not_none_if_abcd_type(cls, v, values): # if values["type"] == QuizQuestionType.ABCD and type(v[0]) != _ABCDQuizAnswer: # print(type(v[0]), values) @@ -83,7 +83,7 @@ async def get_live_game_data( redis_res = await redis.get(f"game:{game_pin}") if redis_res is None or user_id is None: raise HTTPException(status_code=404, detail="Game not found or API key not found") - game = _PlayGame.parse_raw(redis_res) + game = _PlayGame.model_validate_json(redis_res) if game.user_id != user_id: raise HTTPException(status_code=404, detail="Game not found or API key not found") for i, question in enumerate(game.questions): @@ -105,18 +105,18 @@ async def get_live_game_data( data_redis_res = await redis.get(f"game_session:{game_pin}") if data_redis_res is None: raise HTTPException(status_code=404, detail="Game not found or API key not found") - data = GameSession.parse_raw(data_redis_res) + data = GameSession.model_validate_json(data_redis_res) for i in range(0, len(game.questions)): res = await redis.get(f"game_session:{game_pin}:{i}") if res is None: break else: res = json.loads(res) - ga_1 = GameAnswer1(id=i, answers=[GameAnswer2.parse_obj(i) for i in res]) + ga_1 = GameAnswer1(id=i, answers=[GameAnswer2.model_validate(i) for i in res]) data.answers.append(ga_1) player_count = await redis.scard(f"game_session:{game_pin}:players") total_questions = len(game.questions) - game = _GetLivePlayGame(**{**game.dict(), "total_questions": total_questions}) + game = _GetLivePlayGame(**{**game.model_dump(), "total_questions": total_questions}) if in_human_count: game.current_question += 1 @@ -160,10 +160,10 @@ async def get_game_session(game_pin: str, api_key: str | None = None, game_id: u redis_res = await redis.get(f"game_session:{game_pin}") if redis_res is None: raise HTTPException(status_code=404, detail="Game not found or API key not found") - data = GameSession.parse_raw(redis_res) + data = GameSession.model_validate_json(redis_res) if user_id is None and data.game_id != str(game_id): raise HTTPException(status_code=401, detail="Game not found or API key not found") - game = PlayGame.parse_raw(await redis.get(f"game:{game_pin}")) + game = PlayGame.model_validate_json(await redis.get(f"game:{game_pin}")) if game.user_id != user_id and data.game_id != str(game_id): raise HTTPException(status_code=404, detail="Game not found or API key not found") for i in range(0, len(game.questions)): @@ -172,12 +172,12 @@ async def get_game_session(game_pin: str, api_key: str | None = None, game_id: u break else: res = json.loads(res) - ga_1 = GameAnswer1(id=i, answers=[GameAnswer2.parse_obj(i) for i in res]) + ga_1 = GameAnswer1(id=i, answers=[GameAnswer2.model_validate(i) for i in res]) data.answers.append(ga_1) players = await redis.smembers(f"game_session:{game_pin}:players") player_list = [] for p in players: - player_list.append(GamePlayer.parse_raw(p)) + player_list.append(GamePlayer.model_validate_json(p)) return player_list @@ -190,16 +190,18 @@ async def set_next_question(game_pin: str, question_number: int, api_key: str): redis_res = await redis.get(f"game:{game_pin}") if redis_res is None or user_id is None: raise HTTPException(status_code=404, detail="Game not found or API key not found") - game_data = PlayGame.parse_raw(redis_res) + game_data = PlayGame.model_validate_json(redis_res) if game_data.user_id != user_id: raise HTTPException(status_code=404, detail="Game not found or API key not found") game_data.current_question = question_number - await redis.set(f"game:{game_pin}", game_data.json(), ex=18000) + await redis.set(f"game:{game_pin}", game_data.model_dump_json(), ex=18000) await sio.emit( "set_question_number", { "question_index": question_number, - "question": ReturnQuestion(**game_data.dict(include={"questions"})["questions"][question_number]).dict(), + "question": ReturnQuestion( + **game_data.model_dump(include={"questions"})["questions"][question_number] + ).model_dump(), }, room=game_pin, ) @@ -231,7 +233,7 @@ async def too_stupid_to_come_up_with_a_name(game_pin: str, api_key: str, in_huma redis_res = await redis.get(f"game:{game_pin}") if redis_res is None or user_id is None: raise HTTPException(status_code=404, detail="Game not found or API key not found") - game = PlayGame.parse_raw(redis_res) + game = PlayGame.model_validate_json(redis_res) for i, question in enumerate(game.questions): if question.type == QuizQuestionType.ABCD: for o, answer in enumerate(question.answers): @@ -244,7 +246,7 @@ async def too_stupid_to_come_up_with_a_name(game_pin: str, api_key: str, in_huma if game.current_question >= 0: return [ { - **game.questions[game.current_question].dict(), + **game.questions[game.current_question].model_dump(), "current_question": game.current_question + 1 if in_human_count else game.current_question, "total_questions": len(game.questions), } @@ -263,13 +265,13 @@ async def voting_results(game_pin: str, api_key: str, as_array: bool = False): redis_res = await redis.get(f"game:{game_pin}") if redis_res is None or user_id is None: raise HTTPException(status_code=404, detail="Game not found or API key not found") - game = PlayGame.parse_raw(redis_res) + game = PlayGame.model_validate_json(redis_res) if game.questions[game.current_question].type != QuizQuestionType.VOTING: return answer_data = await redis.get(f"game_session:{game_pin}:{game.current_question}") if answer_data is None: return - answer_list = AnswerDataList.parse_raw(answer_data) + answer_list = AnswerDataList.model_validate_json(answer_data) answer_dict = {} for answer in game.questions[game.current_question].answers: answer_dict[answer.answer] = 0 diff --git a/classquiz/routers/login.py b/classquiz/routers/login.py index a88d33e..7e2c34e 100644 --- a/classquiz/routers/login.py +++ b/classquiz/routers/login.py @@ -50,7 +50,7 @@ class LoginSession(BaseModel): user_id: str step_1: set[StartLoginResponseTypes] step_2: set[StartLoginResponseTypes] - webauthn_challenge: str | None + webauthn_challenge: str | None = None step1_success: bool = False @@ -58,12 +58,12 @@ class StartLoginResponse(BaseModel): step_1: set[StartLoginResponseTypes] step_2: set[StartLoginResponseTypes] session_id: str - webauthn_data: None | str + webauthn_data: None | str = None def verify_webauthn(data, fidocredentialss: list[FidoCredentials], login_session: LoginSession): try: - credential = AuthenticationCredential.parse_obj(data) + credential = AuthenticationCredential.model_validate(data) except ValidationError: print("ValidationError") raise HTTPException(401) @@ -141,7 +141,7 @@ async def start_login(data: StartLoginInput): webauthn_challenge=webauthn_challenge, ) session_id = os.urandom(16).hex() - await redis.set(f"login_session:{session_id}", login_session.json(), ex=600) + await redis.set(f"login_session:{session_id}", login_session.model_dump_json(), ex=600) return StartLoginResponse(step_1=step_1, step_2=step_2, session_id=session_id, webauthn_data=webauthn_data) @@ -157,7 +157,7 @@ async def step_1_endpoint(session_id: str, data: StepInput, request: Request, re redis_res = await redis.get(f"login_session:{session_id}") if redis_res is None: raise HTTPException(401, detail="wrong credentials") - login_session = LoginSession.parse_raw(redis_res) + login_session = LoginSession.model_validate_json(redis_res) if step_id == 1: if data.auth_type not in {*login_session.step_1, StartLoginResponseTypes.BACKUP}: @@ -177,7 +177,7 @@ async def step_1_endpoint(session_id: str, data: StepInput, request: Request, re return await log_user_in(user, request, response) else: login_session.step1_success = True - await redis.set(f"login_session:{session_id}", login_session.json(), ex=600) + await redis.set(f"login_session:{session_id}", login_session.model_dump_json(), ex=600) return Response(status_code=202) else: print("Wrong Password") @@ -189,7 +189,7 @@ async def step_1_endpoint(session_id: str, data: StepInput, request: Request, re return await log_user_in(user, request, response) else: login_session.step1_success = True - await redis.set(f"login_session:{session_id}", login_session.json(), ex=600) + await redis.set(f"login_session:{session_id}", login_session.model_dump_json(), ex=600) return Response(status_code=202) else: raise HTTPException(401, detail="webauthn failed") diff --git a/classquiz/routers/moderation.py b/classquiz/routers/moderation.py index 0ef4d16..6b1ff73 100644 --- a/classquiz/routers/moderation.py +++ b/classquiz/routers/moderation.py @@ -47,7 +47,7 @@ async def get_newest_quizzes( class SetModRatingForQuizInput(BaseModel): - rating: int | None + rating: int | None = None @router.post("/rating/set/{quiz_id}") diff --git a/classquiz/routers/quiz.py b/classquiz/routers/quiz.py index 9d742a7..4fc7bee 100644 --- a/classquiz/routers/quiz.py +++ b/classquiz/routers/quiz.py @@ -71,7 +71,7 @@ async def get_public_quiz(quiz_id: uuid.UUID): else: quiz.views += 1 await quiz.update() - return PublicQuizResponse(**quiz.dict()) + return PublicQuizResponse(**quiz.model_dump()) @router.post("/start/{quiz_id}") @@ -130,21 +130,21 @@ async def start_quiz( if cqcs_enabled: code = generate_code(6) await redis.set(f"game:cqc:code:{code}", game_pin, ex=3600) - await redis.set(f"game:{str(game.game_pin)}", game.json(), ex=18000) + await redis.set(f"game:{str(game.game_pin)}", game.model_dump_json(), ex=18000) await redis.set(f"game_pin:{user.id}:{quiz_id}", game_pin, ex=18000) await redis.set( f"game_in_lobby:{user.id.hex}", - GameInLobby(game_id=game.game_id, game_pin=str(game_pin), quiz_title=quiz.title).json(), + GameInLobby(game_id=game.game_id, game_pin=str(game_pin), quiz_title=quiz.title).model_dump_json(), ex=900, ) - return {**quiz.dict(exclude={"id"}), **game.dict(exclude={"questions"}), "cqc_code": code} + return {**quiz.model_dump(exclude={"id"}), **game.model_dump(exclude={"questions"}), "cqc_code": code} class CheckIfCaptchaEnabledResponse(BaseModel): enabled: bool - game_mode: str | None - custom_field: str | None + game_mode: str | None = None + custom_field: str | None = None @router.get("/play/check_captcha/{game_pin}", response_model=CheckIfCaptchaEnabledResponse) @@ -152,7 +152,7 @@ async def check_if_captcha_enabled(game_pin: str): game = await redis.get(f"game:{game_pin}") if game is None: return JSONResponse(status_code=404, content={"detail": "game not found"}) - game = PlayGame.parse_raw(game) + game = PlayGame.model_validate_json(game) if game.captcha_enabled: return CheckIfCaptchaEnabledResponse(enabled=True, game_mode=game.game_mode, custom_field=game.custom_field) else: @@ -228,7 +228,7 @@ async def export_quiz_answers(export_token: str, game_pin: str): raise HTTPException(status_code=404, detail="export token not found") data = json.loads(data) data2 = await redis.get(f"game:{game_pin}") - game_data = PlayGame.parse_raw(data2) + game_data = PlayGame.model_validate_json(data2) quiz = await Quiz.objects.get_or_none(id=game_data.quiz_id) if quiz is None: raise HTTPException(status_code=404, detail="quiz not found") @@ -255,4 +255,4 @@ async def export_quiz_answers(export_token: str, game_pin: str): @router.post("/excel-import") async def import_from_excel(file: UploadFile = File(), user: User = Depends(get_current_user)) -> Quiz: quiz = await handle_import_from_excel(file.file, user) - return Quiz.parse_obj(quiz.dict(exclude={"user_id": ...})) + return Quiz.model_validate(quiz.model_dump(exclude={"user_id": ...})) diff --git a/classquiz/routers/quiztivity/__init__.py b/classquiz/routers/quiztivity/__init__.py index cd5a5b2..da570f7 100644 --- a/classquiz/routers/quiztivity/__init__.py +++ b/classquiz/routers/quiztivity/__init__.py @@ -19,7 +19,9 @@ router.include_router(shares_router, prefix="/shares") @router.post("/create", response_model_exclude={"user": ...}) async def create_quiztivity(data: QuizTivityInput, user: User = Depends(get_current_user)) -> QuizTivity: - quiztivity = QuizTivity.parse_obj({**data.dict(), "user": user, "id": uuid4(), "created_at": datetime.now()}) + quiztivity = QuizTivity.model_validate( + {**data.model_dump(), "user": user, "id": uuid4(), "created_at": datetime.now()} + ) return await quiztivity.save() @@ -36,7 +38,7 @@ async def put_quiztivity(data: QuizTivityInput, uuid: UUID, user: User = Depends quiztivity = await QuizTivity.objects.get_or_none(id=uuid, user=user) if quiztivity is None: raise HTTPException(status_code=404, detail="QuizTivity not found") - quiztivity.pages = data.dict()["pages"] + quiztivity.pages = data.model_dump()["pages"] quiztivity.title = data.title return await quiztivity.update() diff --git a/classquiz/routers/quiztivity/shares.py b/classquiz/routers/quiztivity/shares.py index ffa740b..47e1213 100644 --- a/classquiz/routers/quiztivity/shares.py +++ b/classquiz/routers/quiztivity/shares.py @@ -25,9 +25,9 @@ async def get_shares(user: User = Depends(get_current_user)) -> list[PublicQuizT class CreateShareInput(BaseModel): - name: str | None + name: str | None = None quiztivity: UUID - expire_in: int | None + expire_in: int | None = None @router.post("/") @@ -55,8 +55,8 @@ async def delete_share(uuid: UUID, user: User = Depends(get_current_user)): class UpdateShareInput(BaseModel): - name: str | None - expire_in: int | None + name: str | None = None + expire_in: int | None = None @router.put("/{uuid}") diff --git a/classquiz/routers/remote.py b/classquiz/routers/remote.py index 5f37698..8ad3192 100644 --- a/classquiz/routers/remote.py +++ b/classquiz/routers/remote.py @@ -17,5 +17,5 @@ async def get_game_in_lobby(user: User = Depends(get_current_user)): game_in_lobby_raw = await redis.get(f"game_in_lobby:{user.id.hex}") if game_in_lobby_raw is None: raise HTTPException(status_code=404, detail="No game waiting") - game_in_lobby = GameInLobby.parse_raw(game_in_lobby_raw) + game_in_lobby = GameInLobby.model_validate_json(game_in_lobby_raw) return game_in_lobby diff --git a/classquiz/routers/sitemap.py b/classquiz/routers/sitemap.py index 441a8d3..af79b39 100644 --- a/classquiz/routers/sitemap.py +++ b/classquiz/routers/sitemap.py @@ -45,7 +45,7 @@ class SitemapQuiz(BaseModel): updated_at: datetime.datetime class Config: - orm_mode = True + from_attributes = True @router.get("/get") diff --git a/classquiz/routers/users/__init__.py b/classquiz/routers/users/__init__.py index 10be527..c086c92 100644 --- a/classquiz/routers/users/__init__.py +++ b/classquiz/routers/users/__init__.py @@ -66,7 +66,7 @@ router.include_router(oauth.router, tags=["users", "oauth"], prefix="/oauth") async def create_user(user: RouteUser, background_task: BackgroundTasks) -> User | JSONResponse: if settings.registration_disabled: raise HTTPException(status_code=423) - user = User(**user.dict(), id=uuid.uuid4(), avatar=gzipped_user_avatar(), created_at=datetime.now()) + user = User(**user.model_dump(), id=uuid.uuid4(), avatar=gzipped_user_avatar(), created_at=datetime.now()) try: validate_email(user.email) except EmailNotValidError as e: @@ -232,7 +232,7 @@ async def reset_password_with_token(reset_password: ResetPassword, response: Res @router.get("/sessions/list", response_model=list[UserSession], response_model_exclude={"user", "session_key", "quizs"}) async def list_sessions(user: User = Depends(get_current_user)): sessions = await UserSession.objects.filter(user=user).all() - return [session.dict() for session in sessions] + return [session.model_dump() for session in sessions] @router.delete("/sessions/{session_id}") @@ -297,7 +297,7 @@ async def get_other_avatar(respo: Response, user_id: uuid.UUID): class InternalAuthData(BaseModel): rememberme: str - jwt: str | None + jwt: str | None = None @router.post("/auth/internal") @@ -341,7 +341,7 @@ async def get_email_from_jwt(data: GetEmailFromJWT): async def generate_api_key(user: User = Depends(get_current_user)): key = ApiKey(key=os.urandom(24).hex(), user=user) await key.save() - return key.dict(include={"key"}) + return key.model_dump(include={"key"}) @router.get("/api_keys", response_model=list[ApiKey], response_model_include={"key"}) diff --git a/classquiz/routers/utils.py b/classquiz/routers/utils.py index 717eac1..36371df 100644 --- a/classquiz/routers/utils.py +++ b/classquiz/routers/utils.py @@ -59,7 +59,7 @@ class IpResponse(BaseModel): @router.get("/ip-lookup/{ip}", response_model=IpResponse) async def get_ip_data(ip: str, _: User = Depends(get_current_user)): async with ClientSession() as session, session.get(f"http://ip-api.com/json/{ip}") as response: - data = await response.json() + data = await response.model_dump_json() try: return IpResponse(**data) except ValidationError: diff --git a/classquiz/socket_server/__init__.py b/classquiz/socket_server/__init__.py index e39c846..935a65a 100644 --- a/classquiz/socket_server/__init__.py +++ b/classquiz/socket_server/__init__.py @@ -24,7 +24,7 @@ from classquiz.db.models import ( AnswerDataList, AnswerData, ) -from pydantic import BaseModel, ValidationError, validator +from pydantic import BaseModel, ValidationError, field_validator from datetime import datetime from classquiz.socket_server.export_helpers import save_quiz_to_storage @@ -63,11 +63,11 @@ async def set_answer(answers, game_pin: str, q_index: int, data: AnswerData) -> if answers is None: answers = AnswerDataList(__root__=[data]) else: - answers = AnswerDataList.parse_raw(answers) + answers = AnswerDataList.model_validate_json(answers) answers.__root__.append(data) await redis.set( f"game_session:{game_pin}:{q_index}", - answers.json(), + answers.model_dump_json(), ex=7200, ) return answers @@ -76,8 +76,8 @@ async def set_answer(answers, game_pin: str, q_index: int, data: AnswerData) -> class _JoinGameData(BaseModel): username: str game_pin: str - captcha: str | None - custom_field: str | None + captcha: str | None = None + custom_field: str | None = None class _RejoinGameData(BaseModel): @@ -105,10 +105,12 @@ async def rejoin_game(sid: str, data: dict): await sio.emit("time_sync", encrypted_datetime, room=sid) await redis.set(redis_sid_key, sid) await redis.srem( - f"game_session:{data.game_pin}:players", GamePlayer(username=data.username, sid=data.old_sid).json() + f"game_session:{data.game_pin}:players", GamePlayer(username=data.username, sid=data.old_sid).model_dump_json() ) - await redis.sadd(f"game_session:{data.game_pin}:players", GamePlayer(username=data.username, sid=sid).json()) - game_data = PlayGame.parse_raw(redis_res) + await redis.sadd( + f"game_session:{data.game_pin}:players", GamePlayer(username=data.username, sid=sid).model_dump_json() + ) + game_data = PlayGame.model_validate_json(redis_res) session = { "game_pin": data.game_pin, "username": data.username, @@ -120,7 +122,7 @@ async def rejoin_game(sid: str, data: dict): await sio.emit( "rejoined_game", { - **json.loads(game_data.json(exclude={"quiz_id", "questions", "user_id"})), + **json.loads(game_data.model_dump_json(exclude={"quiz_id", "questions", "user_id"})), "question_count": len(game_data.questions), }, room=sid, @@ -139,7 +141,7 @@ async def join_game(sid: str, data: dict): await sio.emit("error", room=sid) print(e) return - game_data = PlayGame.parse_raw(redis_res) + game_data = PlayGame.model_validate_json(redis_res) if game_data.started: await sio.emit("game_already_started", room=sid) return @@ -153,7 +155,7 @@ async def join_game(sid: str, data: dict): "https://hcaptcha.com/siteverify", data={"response": data.captcha, "secret": settings.hcaptcha_key}, ) as resp: - resp_data = await resp.json() + resp_data = await resp.model_dump_json() if not resp_data["success"]: print("CAPTCHA FAILED") return @@ -166,7 +168,7 @@ async def join_game(sid: str, data: dict): data={"secret": settings.recaptcha_key, "response": data.captcha}, ) as resp: try: - resp_data = await resp.json() + resp_data = await resp.model_dump_json() if not resp_data["success"]: print("CAPTCHA FAILED") return @@ -190,25 +192,22 @@ async def join_game(sid: str, data: dict): await sio.emit( "joined_game", { - **json.loads(game_data.json(exclude={"quiz_id", "questions", "user_id"})), + **json.loads(game_data.model_dump_json(exclude={"quiz_id", "questions", "user_id"})), "question_count": len(game_data.questions), }, room=sid, ) redis_res = await redis.get(f"game_session:{data.game_pin}") - redis_res = GameSession.parse_raw(redis_res) + redis_res = GameSession.model_validate_json(redis_res) await redis.set(f"game_session:{data.game_pin}:players:{data.username}", sid, ex=7200) - await redis.sadd(f"game_session:{data.game_pin}:players", GamePlayer(username=data.username, sid=sid).json()) + await redis.sadd( + f"game_session:{data.game_pin}:players", GamePlayer(username=data.username, sid=sid).model_dump_json() + ) if data.custom_field == "": data.custom_field = None if data.custom_field is not None: await redis.hset(f"game:{data.game_pin}:players:custom_fields", data.username, data.custom_field) - # await redis.set( - # f"game_session:{data.game_pin}", - # GameSession(admin=redis_res.admin, game_id=redis_res.game_id, answers=[]).json(), - # ex=18000, - # ) await sio.emit( "player_joined", {"username": data.username, "sid": sid}, @@ -226,9 +225,9 @@ async def start_game(sid: str, _data: dict): session = await sio.get_session(sid) if not session["admin"]: return - game_data = PlayGame.parse_raw(await redis.get(f"game:{session['game_pin']}")) + game_data = PlayGame.model_validate_json(await redis.get(f"game:{session['game_pin']}")) game_data.started = True - await redis.set(f"game:{session['game_pin']}", game_data.json(), ex=7200) + await redis.set(f"game:{session['game_pin']}", game_data.model_dump_json(), ex=7200) await redis.delete(f"game_in_lobby:{game_data.user_id.hex}") await sio.emit("start_game", room=session["game_pin"]) @@ -251,7 +250,7 @@ async def register_as_admin(sid: str, data: dict): if (await redis.get(f"game_session:{game_pin}")) is None: await redis.set( f"game_session:{game_pin}", - GameSession(admin=sid, game_id=game_id, answers=[]).json(), + GameSession(admin=sid, game_id=game_id, answers=[]).model_dump_json(), ex=7200, ) @@ -280,10 +279,10 @@ async def get_question_results(sid: str, data: dict): if redis_res is None: redis_res = [] else: - redis_res = AnswerDataList.parse_raw(redis_res).dict()["__root__"] - game_data = PlayGame.parse_raw(await redis.get(f"game:{session['game_pin']}")) + redis_res = AnswerDataList.model_validate_json(redis_res).model_dump()["__root__"] + game_data = PlayGame.model_validate_json(await redis.get(f"game:{session['game_pin']}")) game_data.question_show = False - await redis.set(f"game:{session['game_pin']}", game_data.json()) + await redis.set(f"game:{session['game_pin']}", game_data.model_dump_json()) game_pin = session["game_pin"] await sio.emit("question_results", redis_res, room=game_pin) @@ -291,7 +290,7 @@ async def get_question_results(sid: str, data: dict): class ABCDQuizAnswerWithoutSolution(BaseModel): answer: str - color: str | None + color: str | None = None class RangeQuizAnswerWithoutSolution(BaseModel): @@ -303,7 +302,7 @@ class ReturnQuestion(QuizQuestion): answers: list[ABCDQuizAnswerWithoutSolution] | RangeQuizAnswerWithoutSolution | list[VotingQuizAnswer] type: QuizQuestionType = QuizQuestionType.ABCD - @validator("answers") + @field_validator("answers") def answers_not_none_if_abcd_type(cls, v, values): if values["type"] == QuizQuestionType.ABCD and type(v[0]) is not ABCDQuizAnswerWithoutSolution: raise ValueError("Answers can't be none if type is ABCD") @@ -322,12 +321,12 @@ async def set_question_number(sid, data: str): if not session["admin"]: return game_pin = session["game_pin"] - game_data = PlayGame.parse_raw(await redis.get(f"game:{session['game_pin']}")) + game_data = PlayGame.model_validate_json(await redis.get(f"game:{session['game_pin']}")) game_data.current_question = int(float(data)) game_data.question_show = True - await redis.set(f"game:{session['game_pin']}", game_data.json(), ex=7200) + await redis.set(f"game:{session['game_pin']}", game_data.model_dump_json(), ex=7200) await redis.set(f"game:{session['game_pin']}:current_time", datetime.now().isoformat(), ex=7200) - temp_return = game_data.dict(include={"questions"})["questions"][int(float(data))] + temp_return = game_data.model_dump(include={"questions"})["questions"][int(float(data))] if game_data.questions[int(float(data))].type == QuizQuestionType.SLIDE: await sio.emit( "set_question_number", @@ -347,7 +346,7 @@ async def set_question_number(sid, data: str): "set_question_number", { "question_index": int(float(data)), - "question": ReturnQuestion(**temp_return).dict(), + "question": ReturnQuestion(**temp_return).model_dump(), }, room=game_pin, ) @@ -360,7 +359,7 @@ class _SubmitAnswerDataOrderType(BaseModel): class _SubmitAnswerData(BaseModel): question_index: int answer: str - complex_answer: list[_SubmitAnswerDataOrderType] | None + complex_answer: list[_SubmitAnswerDataOrderType] | None = None @sio.event @@ -373,7 +372,7 @@ async def submit_answer(sid: str, data: dict): print(e) return session = await sio.get_session(sid) - game_data = PlayGame.parse_raw(await redis.get(f"game:{session['game_pin']}")) + game_data = PlayGame.model_validate_json(await redis.get(f"game:{session['game_pin']}")) answer_right = False if game_data.questions[int(float(data.question_index))].type == QuizQuestionType.ABCD: for answer in game_data.questions[int(float(data.question_index))].answers: @@ -398,10 +397,10 @@ async def submit_answer(sid: str, data: dict): for a in question.answers: correct_answers.append({"answer": a.answer}) answer_order = [] - for a in data.dict()["complex_answer"]: + for a in data.model_dump()["complex_answer"]: answer_order.append(a["answer"]) data.answer = ", ".join(answer_order) - if correct_answers == data.dict()["complex_answer"]: + if correct_answers == data.model_dump()["complex_answer"]: answer_right = True elif game_data.questions[int(float(data.question_index))].type == QuizQuestionType.TEXT: @@ -454,9 +453,9 @@ async def submit_answer(sid: str, data: dict): # await redis.get(f"game_session:{session['game_pin']}:{data.question_index}"), # room=session["game_pin"], # ) - game_data = PlayGame.parse_raw(await redis.get(f"game:{session['game_pin']}")) + game_data = PlayGame.model_validate_json(await redis.get(f"game:{session['game_pin']}")) game_data.question_show = False - await redis.set(f"game:{session['game_pin']}", game_data.json()) + await redis.set(f"game:{session['game_pin']}", game_data.model_dump_json()) await sio.emit("everyone_answered", {}) @@ -491,7 +490,7 @@ async def show_solutions(sid: str, _data: dict): game_data = PlayGame(**json.loads(await redis.get(f"game:{session['game_pin']}"))) if not session["admin"]: return - await sio.emit("solutions", game_data.questions[game_data.current_question].dict(), room=session["game_pin"]) + await sio.emit("solutions", game_data.questions[game_data.current_question].model_dump(), room=session["game_pin"]) @sio.event @@ -523,7 +522,8 @@ async def kick_player(sid: str, data: dict): player_sid = await redis.get(f"game_session:{session['game_pin']}:players:{data.username}") await redis.srem( - f"game_session:{session['game_pin']}:players", GamePlayer(username=data.username, sid=player_sid).json() + f"game_session:{session['game_pin']}:players", + GamePlayer(username=data.username, sid=player_sid).model_dump_json(), ) await sio.leave_room(player_sid, session["game_pin"]) await sio.emit("kick", room=player_sid) diff --git a/classquiz/socket_server/export_helpers.py b/classquiz/socket_server/export_helpers.py index 32b4dbc..30b6b4e 100644 --- a/classquiz/socket_server/export_helpers.py +++ b/classquiz/socket_server/export_helpers.py @@ -13,7 +13,7 @@ from classquiz.db.models import PlayGame, GameResults async def save_quiz_to_storage(game_pin: str): - game = PlayGame.parse_raw(await redis.get(f"game:{game_pin}")) + game = PlayGame.model_validate_json(await redis.get(f"game:{game_pin}")) player_count = await redis.scard(f"game_session:{game_pin}:players") answers = [] for i in range(len(game.questions)): @@ -26,7 +26,7 @@ async def save_quiz_to_storage(game_pin: str): custom_field_data = await redis.hgetall(f"game:{game_pin}:players:custom_fields") q_return = [] for q in game.questions: - q_return.append(q.dict()) + q_return.append(q.model_dump()) data = GameResults( id=game.game_id, quiz=game.quiz_id, diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index dd675c9..0431d21 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -8,7 +8,7 @@ services: # context: pg_uuidv7 # args: # PG_MAJOR_VERSION: 16 - image: postgres + image: postgres:16 environment: POSTGRES_PASSWORD: mysecretpassword POSTGRES_DB: classquiz