From 91da3571c36da4a0fdf5df54ea3dd6f60ef46b7b Mon Sep 17 00:00:00 2001 From: Mawoka Date: Tue, 4 Jul 2023 15:45:20 +0200 Subject: [PATCH] :construction: Nearly finished excel-import --- Pipfile | 1 + Pipfile.lock | 192 ++++++++++++++++++---------------- classquiz/helpers/__init__.py | 53 +++++++++- classquiz/routers/quiz.py | 9 +- 4 files changed, 163 insertions(+), 92 deletions(-) diff --git a/Pipfile b/Pipfile index 8f1f6be..71c7d6e 100644 --- a/Pipfile +++ b/Pipfile @@ -43,6 +43,7 @@ xxhash = "*" arq = "*" thumbhash-python = "==1.0.0" python-magic = "*" +openpyxl = "*" [dev-packages] coverage = "*" diff --git a/Pipfile.lock b/Pipfile.lock index ada2f3d..6111ba5 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "f10247b4d32925c62f8f9c99a6b3d3bf0073f630d09cf15a7df884f86b78b0a2" + "sha256": "cd25d3ca63e997ce265acf2ab928e9799bd36af4768c03a8972b68205494f575" }, "pipfile-spec": 6, "requires": { @@ -250,11 +250,11 @@ }, "authlib": { "hashes": [ - "sha256:4ddf4fd6cfa75c9a460b361d4bd9dac71ffda0be879dbe4292a02e92349ad55a", - "sha256:4fa3e80883a5915ef9f5bc28630564bc4ed5b5af39812a3ff130ec76bd631e9d" + "sha256:421f7c6b468d907ca2d9afede256f068f87e34d23dd221c07d13d4c234726afb", + "sha256:c88984ea00149a90e3537c964327da930779afa4564e354edfd98410bea01911" ], "index": "pypi", - "version": "==1.2.0" + "version": "==1.2.1" }, "bidict": { "hashes": [ @@ -277,11 +277,11 @@ "pydantic" ], "hashes": [ - "sha256:4b01725c8ccf918752436a8aab595fa153c5123c147225434bf1f40041acb3c7", - "sha256:7f200e1d1067245f39ae2df6c547dc3de8619060012679702f6471187280e6eb" + "sha256:3b3d076e824ae979b271b4d497c90514c2b218811f76b0c368fb69da2556fe07", + "sha256:88e5d91be5b2dff9c0748ba515774c3421088922d9e77c39f8742eb41cb7db88" ], - "markers": "python_version >= '3.7' and python_version < '4.0'", - "version": "==3.0.0" + "markers": "python_version >= '3.8' and python_version < '4.0'", + "version": "==3.0.2" }, "cbor2": { "hashes": [ @@ -562,13 +562,21 @@ "index": "pypi", "version": "==2.0.0.post2" }, + "et-xmlfile": { + "hashes": [ + "sha256:8eb9e2bc2f8c97e37a2dc85a09ecdcdec9d8a396530a6d5a33b30b9a92da0c5c", + "sha256:a2ba85d1d6a74ef63837eed693bcb89c3f752169b0e3e7ae5b16ca5e1b3deada" + ], + "markers": "python_version >= '3.6'", + "version": "==1.1.0" + }, "fastapi": { "hashes": [ - "sha256:95d757511c596409930bd20673358d4a4d709004edb85c5d24d6ffc48fabcbf2", - "sha256:b53248ee45f64f19bb7600953696e3edf94b0f7de94df1e5433fc5c6136fa986" + "sha256:976df7bab51ac7beda9f68c4513b8c4490b5c1135c72aafd0a5ee4023ec5282e", + "sha256:ac78f717cd80d657bd183f94d33b9bda84aa376a46a9dab513586b8eef1dc6fc" ], "index": "pypi", - "version": "==0.97.0" + "version": "==0.99.1" }, "frozenlist": { "hashes": [ @@ -1072,16 +1080,24 @@ "markers": "python_version >= '3.7'", "version": "==6.0.4" }, + "openpyxl": { + "hashes": [ + "sha256:a6f5977418eff3b2d5500d54d9db50c8277a368436f4e4f8ddb1be3422870184", + "sha256:f91456ead12ab3c6c2e9491cf33ba6d08357d802192379bb482f1033ade496f5" + ], + "index": "pypi", + "version": "==3.1.2" + }, "ormar": { "extras": [ "postgresql" ], "hashes": [ - "sha256:d172b00398fc272ae1dde373b1ee9774f52e77efebeb57956f0b03aed3bc154f", - "sha256:e4400623140861da8e0a4d520bdaf5dfcbbf4b3e69c0dd4107f3c7faa16c7789" + "sha256:0cd60ee2106c8215486d9654e39cf8612837c16b7deaf2680c35eb55b06abf9d", + "sha256:8af45d1c28811d73c2c5015bd0a4353b4c4cb54a69bcd2eecac276922e2de19c" ], "index": "pypi", - "version": "==0.12.1" + "version": "==0.12.2" }, "passlib": { "hashes": [ @@ -1255,45 +1271,45 @@ }, "pydantic": { "hashes": [ - "sha256:05a81b006be15655b2a1bae5faa4280cf7c81d0e09fcb49b342ebf826abe5a72", - "sha256:0b53e1d41e97063d51a02821b80538053ee4608b9a181c1005441f1673c55423", - "sha256:2b3ce5f16deb45c472dde1a0ee05619298c864a20cded09c4edd820e1454129f", - "sha256:2e82a6d37a95e0b1b42b82ab340ada3963aea1317fd7f888bb6b9dfbf4fff57c", - "sha256:301d626a59edbe5dfb48fcae245896379a450d04baeed50ef40d8199f2733b06", - "sha256:39f4a73e5342b25c2959529f07f026ef58147249f9b7431e1ba8414a36761f53", - "sha256:4948f264678c703f3877d1c8877c4e3b2e12e549c57795107f08cf70c6ec7774", - "sha256:4b05697738e7d2040696b0a66d9f0a10bec0efa1883ca75ee9e55baf511909d6", - "sha256:51bdeb10d2db0f288e71d49c9cefa609bca271720ecd0c58009bd7504a0c464c", - "sha256:55b1625899acd33229c4352ce0ae54038529b412bd51c4915349b49ca575258f", - "sha256:572066051eeac73d23f95ba9a71349c42a3e05999d0ee1572b7860235b850cc6", - "sha256:6a05a9db1ef5be0fe63e988f9617ca2551013f55000289c671f71ec16f4985e3", - "sha256:6dc1cc241440ed7ca9ab59d9929075445da6b7c94ced281b3dd4cfe6c8cff817", - "sha256:6e7124d6855b2780611d9f5e1e145e86667eaa3bd9459192c8dc1a097f5e9903", - "sha256:75d52162fe6b2b55964fbb0af2ee58e99791a3138588c482572bb6087953113a", - "sha256:78cec42b95dbb500a1f7120bdf95c401f6abb616bbe8785ef09887306792e66e", - "sha256:7feb6a2d401f4d6863050f58325b8d99c1e56f4512d98b11ac64ad1751dc647d", - "sha256:8775d4ef5e7299a2f4699501077a0defdaac5b6c4321173bcb0f3c496fbadf85", - "sha256:887ca463c3bc47103c123bc06919c86720e80e1214aab79e9b779cda0ff92a00", - "sha256:9193d4f4ee8feca58bc56c8306bcb820f5c7905fd919e0750acdeeeef0615b28", - "sha256:983e720704431a6573d626b00662eb78a07148c9115129f9b4351091ec95ecc3", - "sha256:990406d226dea0e8f25f643b370224771878142155b879784ce89f633541a024", - "sha256:9cbdc268a62d9a98c56e2452d6c41c0263d64a2009aac69246486f01b4f594c4", - "sha256:a48f1953c4a1d9bd0b5167ac50da9a79f6072c63c4cef4cf2a3736994903583e", - "sha256:a9a6747cac06c2beb466064dda999a13176b23535e4c496c9d48e6406f92d42d", - "sha256:a9f2de23bec87ff306aef658384b02aa7c32389766af3c5dee9ce33e80222dfa", - "sha256:b5635de53e6686fe7a44b5cf25fcc419a0d5e5c1a1efe73d49d48fe7586db854", - "sha256:b6f9d649892a6f54a39ed56b8dfd5e08b5f3be5f893da430bed76975f3735d15", - "sha256:b9a3859f24eb4e097502a3be1fb4b2abb79b6103dd9e2e0edb70613a4459a648", - "sha256:cd8702c5142afda03dc2b1ee6bc358b62b3735b2cce53fc77b31ca9f728e4bc8", - "sha256:d7b5a3821225f5c43496c324b0d6875fde910a1c2933d726a743ce328fbb2a8c", - "sha256:d88c4c0e5c5dfd05092a4b271282ef0588e5f4aaf345778056fc5259ba098857", - "sha256:eb992a1ef739cc7b543576337bebfc62c0e6567434e522e97291b251a41dad7f", - "sha256:f2f7eb6273dd12472d7f218e1fef6f7c7c2f00ac2e1ecde4db8824c457300416", - "sha256:fdf88ab63c3ee282c76d652fc86518aacb737ff35796023fae56a65ced1a5978", - "sha256:fdf8d759ef326962b4678d89e275ffc55b7ce59d917d9f72233762061fd04a2d" + "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" ], "index": "pypi", - "version": "==1.10.4" + "version": "==1.10.8" }, "pygments": { "hashes": [ @@ -1428,11 +1444,11 @@ }, "redis": { "hashes": [ - "sha256:77929bc7f5dab9adf3acba2d3bb7d7658f1e0c2f1cafe7eb36434e751c471119", - "sha256:dc87a0bdef6c8bfe1ef1e1c40be7034390c2ae02d92dcd0c7ca1729443899880" + "sha256:585dc516b9eb042a619ef0a39c3d7d55fe81bdb4df09a52c9cdde0d07bf1aa7d", + "sha256:e2b03db868160ee4591de3cb90d40ebb50a90dd302138775937f6a42b7ed183c" ], "index": "pypi", - "version": "==4.5.5" + "version": "==4.6.0" }, "requests": { "hashes": [ @@ -1459,19 +1475,19 @@ }, "sentry-sdk": { "hashes": [ - "sha256:79afb7c896014038e358401ad1d36889f97a129dfa8031c49b3f238cd1aa3935", - "sha256:aa796423eb6a2f4a8cd7a5b02ba6558cb10aab4ccdc0537f63a47b038c520c38" + "sha256:8c0ba7a0ed4e39635d335fdac7c00840b14ce4319bb8629b934243d6306fee7d", + "sha256:eb2a5080912dc9b397925657669e3761d95d62c8aa4c0c6d6a73d5a31edaafe9" ], "index": "pypi", - "version": "==1.25.1" + "version": "==1.27.0" }, "setuptools": { "hashes": [ - "sha256:5df61bf30bb10c6f756eb19e7c9f3b473051f48db77fddbe06ff2ca307df9a6f", - "sha256:62642358adc77ffa87233bc4d2354c4b2682d214048f500964dbe760ccedf102" + "sha256:11e52c67415a381d10d6b462ced9cfb97066179f0e871399e006c4ab101fc85f", + "sha256:baf1fdb41c6da4cd2eae722e135500da913332ab3f2f5c7d33af9b492acb5235" ], "markers": "python_version >= '3.7'", - "version": "==67.8.0" + "version": "==68.0.0" }, "shellingham": { "hashes": [ @@ -1572,11 +1588,11 @@ }, "typing-extensions": { "hashes": [ - "sha256:88a4153d8505aabbb4e13aacb7c486c2b4a33ca3b3f807914a9b4c844c471c26", - "sha256:d91d5919357fe7f681a9f2b5b4cb2a5f1ef0a1e9f59c4d8ff0d3491e05c0ffd5" + "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36", + "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2" ], "markers": "python_version >= '3.7'", - "version": "==4.6.3" + "version": "==4.7.1" }, "urllib3": { "hashes": [ @@ -2162,11 +2178,11 @@ }, "filelock": { "hashes": [ - "sha256:42f1e4ff2b497311213d61ad7aac5fed9050608e5309573f101eefa94143134a", - "sha256:82b1f7da46f0ae42abf1bc78e548667f484ac59d2bcec38c713cee7e2eb51e83" + "sha256:002740518d8aa59a26b0c76e10fb8c6e15eae825d34b6fdf670333fd7b938d81", + "sha256:cbb791cdea2a72f23da6ac5b5269ab0a0d161e9ef0100e653b69049a7706d1ec" ], "markers": "python_version >= '3.7'", - "version": "==3.12.1" + "version": "==3.12.2" }, "flake8": { "hashes": [ @@ -2242,27 +2258,27 @@ }, "platformdirs": { "hashes": [ - "sha256:0ade98a4895e87dc51d47151f7d2ec290365a585151d97b4d8d6312ed6132fed", - "sha256:e48fabd87db8f3a7df7150a4a5ea22c546ee8bc39bc2473244730d4b56d2cc4e" + "sha256:b0cabcb11063d21a0b261d557acb0a9d2126350e63b70cdf7db6347baea456dc", + "sha256:ca9ed98ce73076ba72e092b23d3c93ea6c4e186b3f1c3dad6edd98ff6ffcca2e" ], "markers": "python_version >= '3.7'", - "version": "==3.5.3" + "version": "==3.8.0" }, "pluggy": { "hashes": [ - "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159", - "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3" + "sha256:c2fd55a7d7a3863cba1a013e4e2414658b1d07b6bc57b3919e0c63c9abb99849", + "sha256:d12f0c4b579b15f5e054301bb226ee85eeeba08ffec228092f8defbaa3a4c4b3" ], - "markers": "python_version >= '3.6'", - "version": "==1.0.0" + "markers": "python_version >= '3.7'", + "version": "==1.2.0" }, "pre-commit": { "hashes": [ - "sha256:66e37bec2d882de1f17f88075047ef8962581f83c234ac08da21a0c58953d1f0", - "sha256:8056bc52181efadf4aac792b1f4f255dfd2fb5a350ded7335d251a68561e8cb6" + "sha256:10badb65d6a38caff29703362271d7dca483d01da88f9d7e05d0b97171c136cb", + "sha256:a2256f489cd913d575c145132ae196fe335da32d91a8294b7afe6622335dd023" ], "index": "pypi", - "version": "==3.3.2" + "version": "==3.3.3" }, "pycodestyle": { "hashes": [ @@ -2282,11 +2298,11 @@ }, "pytest": { "hashes": [ - "sha256:cdcbd012c9312258922f8cd3f1b62a6580fdced17db6014896053d47cddf9295", - "sha256:ee990a3cc55ba808b80795a79944756f315c67c12b56abd3ac993a7b8c17030b" + "sha256:78bf16451a2eb8c7a2ea98e32dc119fd2aa758f1d5d66dbf0a59d69a3969df32", + "sha256:b4bf8c45bd59934ed84001ad51e11b4ee40d40a1229d2c79f9c592b0a3f6bd8a" ], "index": "pypi", - "version": "==7.3.2" + "version": "==7.4.0" }, "pytest-asyncio": { "hashes": [ @@ -2382,11 +2398,11 @@ }, "setuptools": { "hashes": [ - "sha256:5df61bf30bb10c6f756eb19e7c9f3b473051f48db77fddbe06ff2ca307df9a6f", - "sha256:62642358adc77ffa87233bc4d2354c4b2682d214048f500964dbe760ccedf102" + "sha256:11e52c67415a381d10d6b462ced9cfb97066179f0e871399e006c4ab101fc85f", + "sha256:baf1fdb41c6da4cd2eae722e135500da913332ab3f2f5c7d33af9b492acb5235" ], "markers": "python_version >= '3.7'", - "version": "==67.8.0" + "version": "==68.0.0" }, "urllib3": { "hashes": [ @@ -2398,18 +2414,18 @@ }, "virtualenv": { "hashes": [ - "sha256:6abec7670e5802a528357fdc75b26b9f57d5d92f29c5462ba0fbe45feacc685e", - "sha256:a85caa554ced0c0afbd0d638e7e2d7b5f92d23478d05d17a76daeac8f279f924" + "sha256:34da10f14fea9be20e0fd7f04aba9732f84e593dac291b757ce42e3368a39419", + "sha256:8ff19a38c1021c742148edc4f81cb43d7f8c6816d2ede2ab72af5b84c749ade1" ], "markers": "python_version >= '3.7'", - "version": "==20.23.0" + "version": "==20.23.1" }, "websocket-client": { "hashes": [ - "sha256:3566f8467cd350874c4913816355642a4942f6c1ed1e9406e3d42fae6d6c072a", - "sha256:b96f3bce3e54e3486ebe6504bc22bd4c140392bd2eb71764db29be8f2639aa65" + "sha256:c951af98631d24f8df89ab1019fc365f2227c0892f12fd150e935607c79dd0dd", + "sha256:f1f9f2ad5291f0225a49efad77abf9e700b6fef553900623060dad6e26503b9d" ], - "version": "==1.5.3" + "version": "==1.6.1" } } } diff --git a/classquiz/helpers/__init__.py b/classquiz/helpers/__init__.py index ff5013c..b0154da 100644 --- a/classquiz/helpers/__init__.py +++ b/classquiz/helpers/__init__.py @@ -5,11 +5,15 @@ import asyncio import uuid -from typing import Optional +from datetime import datetime +from typing import Optional, BinaryIO + +from fastapi import HTTPException +from openpyxl import load_workbook import ormar.exceptions -from classquiz.db.models import Quiz, User, InstanceData +from classquiz.db.models import Quiz, User, InstanceData, QuizQuestion, ABCDQuizAnswer import xlsxwriter from aiohttp import ClientSession from io import BytesIO @@ -107,6 +111,51 @@ async def generate_spreadsheet(quiz_results: dict, quiz: Quiz, player_fields: di return storage +async def handle_import_from_excel(data: BinaryIO, user: User): + try: + wb = load_workbook(filename=data) + except KeyError: + raise HTTPException(status_code=400, detail="File not in excel format") + print(wb) + ws = wb.active + questions: list[QuizQuestion] = [] + for i, row in enumerate(ws.iter_rows(min_row=14, min_col=2, max_row=100, max_col=8, values_only=True)): + if row[0] is None: + continue + question = row[0] + if len(question) > 200: + raise HTTPException(status_code=400, detail=f"Question {i + 1} is longer than 200") + try: + time = int(row[5]) + except (ValueError, TypeError): + raise HTTPException(status_code=400, detail=f"Time in q {i + 1} isn't a valid int") + if time > 120: + raise HTTPException(status_code=400, detail=f"Time in q {i + 1} is greater than 120") + answers = [row[1], row[2], row[3], row[4]] + for a, answer in enumerate(answers): + if answer is None: + answers.pop(a) + continue + if len(answer) > 150: + raise HTTPException(status_code=400, detail=f"Answer {a + 1} in Question {i + 1} is longer than 150") + if len(answers) < 2: + raise HTTPException(status_code=400, detail=f"Less than 2 answers in Question {i + 1}") + correct_answers: str = str(row[6]) + answers_list: list[ABCDQuizAnswer] = [] + for a, answer in enumerate(answers): + answers_list.append(ABCDQuizAnswer(answer=answer, right=str(a + 1) in correct_answers)) + questions.append(QuizQuestion(question=question, answers=answers_list, time=str(time))) + quiz = Quiz( + id=uuid.uuid4(), + created_at=datetime.now(), + updated_at=datetime.now(), + user_id=user, + questions=questions, + imported_from_kahoot=False, + ) + return quiz + + async def meilisearch_init(): classquiz_index_found = False try: diff --git a/classquiz/routers/quiz.py b/classquiz/routers/quiz.py index 96652f5..5cd62d0 100644 --- a/classquiz/routers/quiz.py +++ b/classquiz/routers/quiz.py @@ -12,8 +12,8 @@ from random import randint import ormar.exceptions -from classquiz.helpers import generate_spreadsheet -from fastapi import APIRouter, Depends, HTTPException +from classquiz.helpers import generate_spreadsheet, handle_import_from_excel +from fastapi import APIRouter, Depends, HTTPException, UploadFile, File from fastapi.responses import JSONResponse, StreamingResponse from pydantic import ValidationError, BaseModel @@ -246,3 +246,8 @@ async def export_quiz_answers(export_token: str, game_pin: str): "Content-Disposition": f"attachment;filename=ClassQuiz-{urllib.parse.quote(quiz.title)}-{datetime.now().strftime('%m-%d-%Y')}.xlsx" # noqa: E501 }, ) + + +@router.post("/excel-import") +async def import_from_excel(data: UploadFile = File(), user: User = Depends(get_current_user)): + await handle_import_from_excel(data.file, user)