diff --git a/classquiz/config.py b/classquiz/config.py index 49a8476..50f8cdd 100644 --- a/classquiz/config.py +++ b/classquiz/config.py @@ -71,6 +71,7 @@ class Settings(BaseSettings): async def initialize_arq(): + # skipcq: PYL-W0603 global arq arq = await create_pool(RedisSettings.from_dsn(settings.redis)) @@ -80,8 +81,6 @@ def settings() -> Settings: return Settings() -# asyncio.run(initialize_arq()) - pool = redis_lib.ConnectionPool().from_url(settings().redis) redis: redis_base_lib.client.Redis = redis_lib.Redis(connection_pool=pool) diff --git a/classquiz/db/models.py b/classquiz/db/models.py index 7d3ef0f..688f851 100644 --- a/classquiz/db/models.py +++ b/classquiz/db/models.py @@ -297,7 +297,7 @@ class GameInLobby(BaseModel): game_id: uuid.UUID -# +# skipcq: PY-W0069 # class UserProfileLinks(ormar.Model): # id: int = ormar.Integer(primary_key=True, autoincrement=True) # user: Optional[User] = ormar.ForeignKey(User) @@ -471,5 +471,5 @@ class PrivateStorageItem(PublicStorageItem): class UpdateStorageItem(BaseModel): - filename: str - alt_text: str + filename: str | None + alt_text: str | None diff --git a/classquiz/db/quiztivity.py b/classquiz/db/quiztivity.py index cf8c295..bc355de 100644 --- a/classquiz/db/quiztivity.py +++ b/classquiz/db/quiztivity.py @@ -41,10 +41,3 @@ class QuizTivityPage(BaseModel): title: str | None type: QuizTivityTypes data: Pdf | Memory | Markdown - - # @validator("type") - # def match_type_to_data_type(cls, v, values, **kwargs): - # print(values) - # if TYPE_CLASS_LIST[v] != type(values["data"]): - # raise ValueError("Specified Type doesn't match real data type") - # pass diff --git a/classquiz/oauth/custom.py b/classquiz/oauth/custom.py index 509515f..5f10c99 100644 --- a/classquiz/oauth/custom.py +++ b/classquiz/oauth/custom.py @@ -88,8 +88,9 @@ async def auth(request: Request, response: Response): google_uid=user_data.sub.hex, avatar=gzipped_user_avatar(), ) + # skipcq: PYL-W0703 except Exception as e: - if type(e) == asyncpg.exceptions.UniqueViolationError: + if type(e) is asyncpg.exceptions.UniqueViolationError: error = True counter = 1 while error: diff --git a/classquiz/oauth/github.py b/classquiz/oauth/github.py index 0bad0e7..80471b4 100644 --- a/classquiz/oauth/github.py +++ b/classquiz/oauth/github.py @@ -116,8 +116,9 @@ async def auth(request: Request, response: Response): auth_type=UserAuthTypes.GITHUB, avatar=gzipped_user_avatar(), ) + # skipcq: PYL-W0703 except Exception as e: - if type(e) == asyncpg.exceptions.UniqueViolationError: + if type(e) is asyncpg.exceptions.UniqueViolationError: error = True counter = 1 while error: diff --git a/classquiz/oauth/google.py b/classquiz/oauth/google.py index 3bf1d88..1c590d0 100644 --- a/classquiz/oauth/google.py +++ b/classquiz/oauth/google.py @@ -90,8 +90,9 @@ async def auth(request: Request, response: Response): google_uid=user_data.sub, avatar=gzipped_user_avatar(), ) + # skipcq: PYL-W0703 except Exception as e: - if type(e) == asyncpg.exceptions.UniqueViolationError: + if type(e) is asyncpg.exceptions.UniqueViolationError: error = True counter = 1 while error: diff --git a/classquiz/routers/avatar.py b/classquiz/routers/avatar.py index 26967fe..e80cf88 100644 --- a/classquiz/routers/avatar.py +++ b/classquiz/routers/avatar.py @@ -80,6 +80,7 @@ async def get_customized_avatar( clothe_color=clothe_color, clothe_graphic_type=clothe_graphic_type, ).render_svg() + # skipcq: PY-W0069 # print(f"skin_color: {len(AvatarItemsAsList.skin_color)},") # print(f"hair_color: {len(AvatarItemsAsList.hair_color)},") # print(f"facial_hair_type: {len(AvatarItemsAsList.facial_hair_type)},") diff --git a/classquiz/routers/box_controller/embedded.py b/classquiz/routers/box_controller/embedded.py index 288d6c3..19bcf42 100644 --- a/classquiz/routers/box_controller/embedded.py +++ b/classquiz/routers/box_controller/embedded.py @@ -109,7 +109,7 @@ button_to_index_map = {"b": 0, "g": 1, "y": 2, "r": 3} @router.websocket("/socket/{id}") async def websocket_endpoint(ws: WebSocket, game_id: str, id: str): try: - if id in wss_clients.keys(): + if id in wss_clients: await ws.close(code=status.WS_1001_GOING_AWAY) print("Client {} already exists.".format(id)) return diff --git a/classquiz/routers/box_controller/web.py b/classquiz/routers/box_controller/web.py index 72afce5..ee6abb2 100644 --- a/classquiz/routers/box_controller/web.py +++ b/classquiz/routers/box_controller/web.py @@ -26,7 +26,7 @@ def generate_code() -> str: "r", ] # Capital stands for long press, lowercase letter for short press resulting_code = "" - for i in range(specified_length): + for _ in range(specified_length): resulting_code += random.choice(buttons) return resulting_code diff --git a/classquiz/routers/editor.py b/classquiz/routers/editor.py index 7704eab..bcb0134 100644 --- a/classquiz/routers/editor.py +++ b/classquiz/routers/editor.py @@ -106,9 +106,6 @@ async def finish_edit(edit_id: str, quiz_input: QuizInput): if quiz_input.cover_image == "": quiz_input.cover_image = None - # if quiz_input.background_image is None and old_quiz_data.background_image is not None: - # mark_image_for_deletion(quiz_input.background_image) - if quiz_input.cover_image is not None and not check_image_string(quiz_input.cover_image)[0]: raise HTTPException(status_code=400, detail="image url is not valid") diff --git a/classquiz/routers/eximport.py b/classquiz/routers/eximport.py index 294548b..af5c264 100644 --- a/classquiz/routers/eximport.py +++ b/classquiz/routers/eximport.py @@ -46,9 +46,8 @@ async def export_quiz(quiz_id: uuid.UUID, user: User = Depends(get_current_user) quiz_dict["updated_at"] = quiz_dict["updated_at"].isoformat() quiz_json = json.dumps(quiz_dict) bin_data = gzip.compress(quiz_json.encode("utf-8"), compresslevel=9) - # bin_data = quiz_json.encode("utf-8") bin_data = bin_data + quiz_delimiter - for image_key in image_urls.keys(): + for image_key in image_urls: bin_data = bin_data + image_delimiter + str(image_key).encode("utf-8") + image_index_delimiter image_data = None async with ClientSession() as session, session.get( diff --git a/classquiz/routers/live.py b/classquiz/routers/live.py index 6e9d1b5..9aad7a2 100644 --- a/classquiz/routers/live.py +++ b/classquiz/routers/live.py @@ -134,8 +134,6 @@ async def get_live_game_data( @router.get("/user_count") async def get_game_user_count(game_pin: str, api_key: str, as_string: bool = False, as_array: bool = False): - # if redis_res is None: - # raise HTTPException(status_code=404, detail="Game not found") user_id = await check_api_key(api_key) redis_res = await redis.get(f"game_session:{game_pin}") if redis_res is None: @@ -149,12 +147,6 @@ async def get_game_user_count(game_pin: str, api_key: str, as_string: bool = Fal return {"players": {"count": player_count}} -# class _LivePlayersReturn(BaseModel): -# # players: list[GamePlayer | None] -# answers: list[GameAnswer1 | None] -# players: list[GamePlayer | None] - - @router.get( "/players", ) diff --git a/classquiz/routers/login.py b/classquiz/routers/login.py index 6efd0c1..7c0d1dd 100644 --- a/classquiz/routers/login.py +++ b/classquiz/routers/login.py @@ -149,7 +149,7 @@ class StepInput(BaseModel): @router.post("/step/{step_id}") -async def step_1(session_id: str, data: StepInput, request: Request, response: Response, step_id: int): +async def step_1_endpoint(session_id: str, data: StepInput, request: Request, response: Response, step_id: int): if step_id < 0 or step_id > 2: raise HTTPException(status_code=401) redis_res = await redis.get(f"login_session:{session_id}") diff --git a/classquiz/routers/quiz.py b/classquiz/routers/quiz.py index 3069cb9..6df16cd 100644 --- a/classquiz/routers/quiz.py +++ b/classquiz/routers/quiz.py @@ -83,7 +83,7 @@ def generate_code() -> str: "r", ] # Capital stands for long press, lowercase letter for short press resulting_code = "" - for i in range(specified_length): + for _ in range(specified_length): resulting_code += random.choice(buttons) return resulting_code diff --git a/classquiz/routers/results.py b/classquiz/routers/results.py index dbc8810..dd2e43a 100644 --- a/classquiz/routers/results.py +++ b/classquiz/routers/results.py @@ -49,6 +49,7 @@ async def set_note(id: UUID, data: _SetNoteInput, user: User = Depends(get_curre return await res.update() +# skipcq: PYL-W0105 """ @router.get("/export/{result_id}", response_class=StreamingResponse) async def export_result(result_id: UUID, user: User = Depends(get_current_user)): diff --git a/classquiz/routers/storage.py b/classquiz/routers/storage.py index 36b6a01..c99c686 100644 --- a/classquiz/routers/storage.py +++ b/classquiz/routers/storage.py @@ -197,6 +197,10 @@ async def update_image_data( file_data = await StorageItem.objects.get_or_none(id=file_id, user=user, deleted_at=None) if file_data is None: raise HTTPException(status_code=404, detail="File not found") + if data.alt_text == "": + data.alt_text = None + if data.filename == "": + data.filename = None file_data.filename = data.filename file_data.alt_text = data.alt_text await file_data.update() @@ -221,7 +225,6 @@ async def list_images( raise HTTPException(status_code=404, detail="No items found") return_items: list[PrivateStorageItem] = [] for item in storage_items: - # print(item.quizzes) return_items.append(PrivateStorageItem.from_db_model(item)) return return_items diff --git a/classquiz/routers/users/__init__.py b/classquiz/routers/users/__init__.py index 97f7d06..5417387 100644 --- a/classquiz/routers/users/__init__.py +++ b/classquiz/routers/users/__init__.py @@ -79,7 +79,6 @@ async def create_user(user: RouteUser, background_task: BackgroundTasks) -> User if len(user.username) == 32: return JSONResponse({"details": "Username mustn't be 32 characters long"}, 400) await user.save() - # print(settings.skip_email_verification) if settings.skip_email_verification: user.verify_key = None user.verified = True diff --git a/classquiz/socket_server/__init__.py b/classquiz/socket_server/__init__.py index b2ec89f..7b6c463 100644 --- a/classquiz/socket_server/__init__.py +++ b/classquiz/socket_server/__init__.py @@ -253,7 +253,7 @@ class ReturnQuestion(QuizQuestion): if values["type"] == QuizQuestionType.RANGE and type(v) != RangeQuizAnswerWithoutSolution: raise ValueError("Answer must be from type RangeQuizAnswer if type is RANGE") if values["type"] == QuizQuestionType.VOTING and type(v[0]) != VotingQuizAnswer: - # print("Answer must be from type VotingQuizAnswer if type is VOTING") + # skipcq: PTC-W0047 pass return v @@ -262,7 +262,6 @@ class ReturnQuestion(QuizQuestion): async def set_question_number(sid, data: str): # data is just a number (as a str) of the question session = await sio.get_session(sid) - # print("set_question_number", data, session) if session["admin"]: game_pin = session["game_pin"] game_data = PlayGame.parse_raw(await redis.get(f"game:{session['game_pin']}")) @@ -286,7 +285,6 @@ async def set_question_number(sid, data: str): temp_return["type"] = game_data.questions[int(float(data))].type if temp_return["type"] == QuizQuestionType.ORDER: random.shuffle(temp_return["answers"]) - # print("emitting") await sio.emit( "set_question_number", { @@ -375,8 +373,6 @@ async def submit_answer(sid: str, data: dict): answers = await redis.get(f"game_session:{session['game_pin']}:{data.question_index}") diff = (time_q_started - now).total_seconds() * 1000 # - timedelta(milliseconds=latency) - # print(abs(diff) - latency, latency, abs(diff)) - score = 0 if answer_right: score = calculate_score( @@ -394,6 +390,7 @@ async def submit_answer(sid: str, data: dict): answers, game_pin=session["game_pin"], data=answer_data, q_index=int(float(data.question_index)) ) player_count = await redis.scard(f"game_session:{session['game_pin']}:players") + await sio.emit("player_answer", {}) if len(answers.__root__) == player_count: # await sio.emit( # "question_results", diff --git a/classquiz/storage/local_storage.py b/classquiz/storage/local_storage.py index a352ee8..e473712 100644 --- a/classquiz/storage/local_storage.py +++ b/classquiz/storage/local_storage.py @@ -27,9 +27,10 @@ class LocalStorage: except FileNotFoundError: yield None - async def upload(self, file_name: str, data: BinaryIO, mime_type: str | None = None) -> None: + # skipcq: PYL-W0613 + async def upload(self, file_name: str, file: BinaryIO, mime_type: str | None = None) -> None: async with aiofiles.open(file=os.path.join(self.base_path, file_name), mode="wb") as f: - await aioshutil_copyfileobj(data, f) + await aioshutil_copyfileobj(file, f) async def delete(self, file_names: [str]) -> None: for i in file_names: diff --git a/classquiz/storage/s3_storage.py b/classquiz/storage/s3_storage.py index 6a1470d..50d4f3f 100644 --- a/classquiz/storage/s3_storage.py +++ b/classquiz/storage/s3_storage.py @@ -103,15 +103,13 @@ class S3Storage: + "Signature=" + signature ) - # if expiry is not None: - # authorization_header += f", Expires={expiry}" - # Send the request with the authorization header headers = {"x-amz-date": amz_date, "Authorization": authorization_header} request_url = self.base_url + path + "?" + canonical_querystring return headers, request_url + # skipcq: PYL-W0613 async def upload(self, file: BinaryIO, file_name: str, mime_type: str | None = "application/octet-stream") -> None: headers, url = self._generate_aws_signature_v4(method="PUT", path=f"/{file_name}") async with ClientSession() as session, session.put(url, headers=headers, data=file) as resp: diff --git a/classquiz/worker/storage.py b/classquiz/worker/storage.py index 2328703..01493f7 100644 --- a/classquiz/worker/storage.py +++ b/classquiz/worker/storage.py @@ -16,6 +16,7 @@ from classquiz.storage.errors import DeletionFailedError from thumbhash import image_to_thumbhash +# skipcq: PYL-W0613 async def clean_editor_images_up(ctx): print("Cleaning images up") edit_sessions = await redis.smembers("edit_sessions") @@ -52,10 +53,12 @@ async def calculate_hash(ctx, file_id_as_str: str): try: if 0 < file_data.size < 20_970_000: # greater than 0 but smaller than 20mbytes file_data.thumbhash = image_to_thumbhash(file) + # skipcq: PYL-W0703 except Exception: pass hash_obj = xxhash.xxh3_128() + # skipcq: PY-W0069 # assert hash_obj.block_size == 64 while chunk := file.read(6400): hash_obj.update(chunk) @@ -70,6 +73,7 @@ async def calculate_hash(ctx, file_id_as_str: str): await user.update() +# skipcq: PYL-W0613 async def quiz_update(ctx, old_quiz: Quiz, quiz_id: uuid.UUID): new_quiz: Quiz = await Quiz.objects.get(id=quiz_id) old_images = extract_image_ids_from_quiz(old_quiz) @@ -83,8 +87,6 @@ async def quiz_update(ctx, old_quiz: Quiz, quiz_id: uuid.UUID): removed_images = list(set(old_images) - set(new_images)) added_images = list(set(new_images) - set(old_images)) change_made = False - # print("added:", added_images) - # print("removed:", removed_images) for image in removed_images: if "--" in image: await storage.delete([image]) @@ -92,7 +94,6 @@ async def quiz_update(ctx, old_quiz: Quiz, quiz_id: uuid.UUID): item = await StorageItem.objects.get_or_none(id=uuid.UUID(image)) if item is None: continue - # print("removed item") try: await new_quiz.storageitems.remove(item) except ormar.exceptions.NoMatch: @@ -103,7 +104,6 @@ async def quiz_update(ctx, old_quiz: Quiz, quiz_id: uuid.UUID): item = await StorageItem.objects.get_or_none(id=uuid.UUID(image)) if item is None: continue - # print("added item") await new_quiz.storageitems.add(item) change_made = True if change_made: diff --git a/frontend/package.json b/frontend/package.json index 2b0e6ee..a68720a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -8,7 +8,7 @@ "preview": "vite preview", "check": "svelte-check --tsconfig ./tsconfig.json", "check:watch": "svelte-check --tsconfig ./tsconfig.json --watch", - "lint": "prettier --ignore-path .gitignore --check --plugin-search-dir=. . '!src/lib/i18n/locales/*.json' && eslint --ignore-path .gitignore .", + "lint": "prettier --ignore-path .gitignore --check --plugin-search-dir=. . '!src/lib/i18n/locales/*.json' '!pnpm-lock.yaml' && eslint --ignore-path .gitignore .", "format": "prettier --ignore-path .gitignore --write --plugin-search-dir=. . '!src/lib/i18n/locales/*.json'", "run:prod": "node index.js", "translations-scan": "i18next-scanner --config i18next-scanner.config.engine.cjs src/**/*.svelte" diff --git a/frontend/src/lib/admin.svelte b/frontend/src/lib/admin.svelte index 826e737..3936bf7 100644 --- a/frontend/src/lib/admin.svelte +++ b/frontend/src/lib/admin.svelte @@ -31,6 +31,7 @@ let shown_question_now: number; let final_results_clicked = false; let timer_interval; + let answer_count = 0; export let control_visible: boolean; export let player_scores; @@ -53,6 +54,7 @@ shown_question_now = data.question_index; timer_res = quiz_data.questions[data.question_index].time; selected_question = selected_question + 1; + answer_count = 0; timer(timer_res); }); const get_question_results = () => { @@ -95,6 +97,10 @@ } }); + socket.on('player_answer', (_) => { + answer_count += 1; + }); + const timer = (time: string) => { let seconds = Number(time); timer_interval = setInterval(() => { @@ -179,13 +185,13 @@ {/if} {:else} + on:click={() => { + set_question_number(selected_question + 1); + }} + class='admin-button' + >Next Question ({selected_question + 2} + ) + --> {/if} @@ -215,12 +221,18 @@ {@html quiz_data.questions[selected_question].question} -
+ {$t('admin_page.answers_submitted', { answer_count: answer_count })} +