diff --git a/classquiz/config (copy).py b/classquiz/config (copy).py
deleted file mode 100644
index b6f7635..0000000
--- a/classquiz/config (copy).py
+++ /dev/null
@@ -1,107 +0,0 @@
-# SPDX-FileCopyrightText: 2023 Marlon W (Mawoka)
-#
-# SPDX-License-Identifier: MPL-2.0
-
-import re
-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
-import meilisearch as MeiliSearch
-from typing import Optional
-from arq import create_pool
-from arq.connections import RedisSettings, ArqRedis
-
-from classquiz.storage import Storage
-
-
-class CustomOpenIDProvider(BaseModel):
- scopes: str = "openid email profile"
- server_metadata_url: str
- client_id: str
- client_secret: str
-
-
-class Settings(BaseSettings):
- """
- Settings class for the shop app.
- """
-
- root_address: str = "http://127.0.0.1:8000"
- redis: RedisDsn = "redis://localhost:6379/0?decode_responses=True"
- skip_email_verification: bool = False
- db_url: str | PostgresDsn = "postgresql://postgres:mysecretpassword@localhost:5432/classquiz"
- hcaptcha_key: str | None = None
- recaptcha_key: str | None = None
- mail_address: str
- mail_password: str
- mail_username: str
- mail_server: str
- mail_port: int
- secret_key: str
- access_token_expire_minutes: int = 30
- cache_expiry: int = 86400
- sentry_dsn: str | None
- meilisearch_url: str = "http://127.0.0.1:7700"
- meilisearch_index: str = "classquiz"
- google_client_id: Optional[str]
- google_client_secret: Optional[str]
- github_client_id: Optional[str]
- github_client_secret: Optional[str]
- custom_openid_provider: CustomOpenIDProvider | None = None
- telemetry_enabled: bool = True
- free_storage_limit: int = 1074000000
- pixabay_api_key: str | None = None
- mods: list[str] = []
- registration_disabled: bool = False
-
- # storage_backend
- storage_backend: str | None = "local"
-
- # if storage_backend == "local":
- storage_path: str | None
-
- # if storage_backend == "s3":
- s3_access_key: str | None
- s3_secret_key: str | None
- s3_bucket_name: str = "classquiz"
- s3_base_url: str | None
-
- class Config:
- env_file = ".env"
- env_file_encoding = "utf-8"
- env_nested_delimiter = "__"
-
-
-async def initialize_arq():
- # skipcq: PYL-W0603
- global arq
- arq = await create_pool(RedisSettings.from_dsn(settings.redis))
-
-
-@lru_cache()
-def settings() -> Settings:
- return Settings()
-
-
-pool = redis_lib.ConnectionPool().from_url(settings().redis)
-
-redis: redis_base_lib.client.Redis = redis_lib.Redis(connection_pool=pool)
-arq: ArqRedis = ArqRedis(pool_or_conn=pool)
-storage: Storage = Storage(
- backend=settings().storage_backend,
- storage_path=settings().storage_path,
- access_key=settings().s3_access_key,
- secret_key=settings().s3_secret_key,
- bucket_name=settings().s3_bucket_name,
- base_url=settings().s3_base_url,
-)
-
-meilisearch = MeiliSearch.Client(settings().meilisearch_url)
-
-ALLOWED_TAGS_FOR_QUIZ = ["b", "strong", "i", "em", "small", "mark", "del", "sub", "sup"]
-
-ALLOWED_MIME_TYPES = ["image/png", "video/mp4", "image/jpeg", "image/gif", "image/webp"]
-
-server_regex = rf"^{re.escape(settings().root_address)}/api/v1/storage/download/.{{36}}--.{{36}}$"
diff --git a/classquiz/db/models (copy).py b/classquiz/db/models (copy).py
deleted file mode 100644
index 2a9902f..0000000
--- a/classquiz/db/models (copy).py
+++ /dev/null
@@ -1,492 +0,0 @@
-# SPDX-FileCopyrightText: 2023 Marlon W (Mawoka)
-#
-# SPDX-License-Identifier: MPL-2.0
-
-
-import os
-import uuid
-from datetime import datetime
-from typing import Optional
-
-import ormar
-from ormar import ReferentialAction
-from pydantic import BaseModel, Json, validator
-from enum import Enum
-from . import metadata, database
-from .quiztivity import QuizTivityPage
-from sqlalchemy import func
-
-
-class UserAuthTypes(Enum):
- LOCAL = "LOCAL"
- GOOGLE = "GOOGLE"
- GITHUB = "GITHUB"
- CUSTOM = "CUSTOM"
-
-
-class User(ormar.Model):
- """
- The user model in the database
- """
-
- id: uuid.UUID = ormar.UUID(primary_key=True, default=uuid.uuid4())
- email: str = ormar.String(unique=True, max_length=100)
- username: str = ormar.String(unique=True, max_length=100)
- password: Optional[str] = ormar.String(max_length=100, nullable=True)
- verified: bool = ormar.Boolean(default=False)
- verify_key: str = ormar.String(unique=True, max_length=100, nullable=True)
- created_at: datetime = ormar.DateTime(default=datetime.now())
- auth_type: UserAuthTypes = ormar.Enum(enum_class=UserAuthTypes, default=UserAuthTypes.LOCAL)
- google_uid: Optional[str] = ormar.String(unique=True, max_length=255, nullable=True)
- avatar: bytes = ormar.LargeBinary(max_length=25000, represent_as_base64_str=True)
- github_user_id: int | None = ormar.Integer(nullable=True)
- require_password: bool = ormar.Boolean(default=True, nullable=False)
- backup_code: str = ormar.String(max_length=64, min_length=64, nullable=False, default=os.urandom(32).hex())
- 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
-
- class Config:
- use_enum_values = True
-
-
-class FidoCredentials(ormar.Model):
- pk: int = ormar.Integer(autoincrement=True, primary_key=True)
- id: bytes = ormar.LargeBinary(max_length=256)
- public_key: bytes = ormar.LargeBinary(max_length=256)
- sign_count: int = ormar.Integer()
- user: Optional[User] = ormar.ForeignKey(User, ondelete=ReferentialAction.CASCADE)
-
- class Meta:
- 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
-
-
-class UserSession(ormar.Model):
- """
- The user session model for user-sessions
- """
-
- id: uuid.UUID = ormar.UUID(primary_key=True, default=uuid.uuid4())
- user: Optional[User] = ormar.ForeignKey(User, ondelete=ReferentialAction.CASCADE)
- session_key: str = ormar.String(unique=True, max_length=64)
- created_at: datetime = ormar.DateTime(default=datetime.now())
- ip_address: str = ormar.String(max_length=100, nullable=True)
- 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
-
-
-class ABCDQuizAnswer(BaseModel):
- right: bool
- answer: str
- color: str | None
-
-
-class RangeQuizAnswer(BaseModel):
- min: int
- max: int
- min_correct: int
- max_correct: int
-
-
-class VotingQuizAnswer(BaseModel):
- answer: str
- image: str | None = None
- color: str | None
-
-
-class QuizQuestionType(str, Enum):
- ABCD = "ABCD"
- RANGE = "RANGE"
- VOTING = "VOTING"
- SLIDE = "SLIDE"
- TEXT = "TEXT"
- ORDER = "ORDER"
- CHECK = "CHECK"
-
-
-class TextQuizAnswer(BaseModel):
- answer: str
- case_sensitive: bool
-
-
-class QuizQuestion(BaseModel):
- question: str
- time: str # in Secs
- type: None | QuizQuestionType = QuizQuestionType.ABCD
- answers: list[ABCDQuizAnswer] | RangeQuizAnswer | list[TextQuizAnswer] | list[VotingQuizAnswer] | str
- image: str | None = None
-
- @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")
- if values["type"] == QuizQuestionType.RANGE and not isinstance(v, RangeQuizAnswer):
- raise ValueError("Answer must be from type RangeQuizAnswer if type is RANGE")
- if values["type"] == QuizQuestionType.VOTING and not isinstance(v[0], VotingQuizAnswer):
- raise ValueError("Answer must be from type VotingQuizAnswer if type is VOTING")
- if values["type"] == QuizQuestionType.TEXT and not isinstance(v[0], TextQuizAnswer):
- raise ValueError("Answer must be from type TextQuizAnswer if type is TEXT")
- if values["type"] == QuizQuestionType.ORDER and not isinstance(v[0], VotingQuizAnswer):
- raise ValueError("Answer must be from type VotingQuizAnswer if type is ORDER")
- if values["type"] == QuizQuestionType.SLIDE and not isinstance(v, str):
- raise ValueError("Answer must be from type SlideElement if type is SLIDE")
- if values["type"] == QuizQuestionType.CHECK and not isinstance(v[0], ABCDQuizAnswer):
- raise ValueError("Answers can't be none if type is CHECK")
- return v
-
-
-class QuizInput(BaseModel):
- public: bool = False
- title: str
- description: str
- cover_image: str | None
- background_color: str | None
- questions: list[QuizQuestion]
- background_image: str | None
-
-
-class Quiz(ormar.Model):
- id: uuid.UUID = ormar.UUID(primary_key=True, default=uuid.uuid4(), nullable=False, unique=True)
- public: bool = ormar.Boolean(default=False)
- title: str = ormar.Text()
- description: str = ormar.Text(nullable=True)
- created_at: datetime = ormar.DateTime(default=datetime.now())
- updated_at: datetime = ormar.DateTime(default=datetime.now())
- user_id: uuid.UUID = ormar.ForeignKey(User, ondelete=ReferentialAction.CASCADE)
- questions: Json[list[QuizQuestion]] = ormar.JSON(nullable=False)
- imported_from_kahoot: Optional[bool] = ormar.Boolean(default=False, nullable=True)
- cover_image: Optional[str] = ormar.Text(nullable=True, unique=False)
- background_color: str | None = ormar.Text(nullable=True, unique=False)
- background_image: str | None = ormar.Text(nullable=True, unique=False)
- kahoot_id: uuid.UUID | None = ormar.UUID(nullable=True, default=None)
- likes: int = ormar.Integer(nullable=False, default=0, server_default="0")
- dislikes: int = ormar.Integer(nullable=False, default=0, server_default="0")
- plays: int = ormar.Integer(nullable=False, default=0, server_default="0")
- 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
-
-
-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
-
-
-class Token(BaseModel):
- """
- For JWT
- """
-
- access_token: str
- token_type: str
-
-
-class TokenData(BaseModel):
- """
- For JWT
- """
-
- email: str | None = None
-
-
-class PlayGame(BaseModel):
- quiz_id: uuid.UUID | str
- description: str
- user_id: uuid.UUID
- title: str
- questions: list[QuizQuestion]
- game_id: uuid.UUID
- game_pin: str
- started: bool = False
- captcha_enabled: bool = False
- cover_image: str | None
- game_mode: str | None
- current_question: int = -1
- background_color: str | None
- background_image: str | None
- custom_field: str | None
- question_show: bool = False
-
-
-class GamePlayer(BaseModel):
- username: str
- sid: str | None
-
-
-class GameAnswer2(BaseModel):
- username: str
- right: bool
- answer: str
-
-
-class GameAnswer1(BaseModel):
- id: int
- answers: list[GameAnswer2]
-
-
-class GameSession(BaseModel):
- admin: str
- game_id: str
- # players: list[GamePlayer | None]
- answers: list[GameAnswer1 | None]
-
-
-class UpdatePassword(BaseModel):
- old_password: str
- new_password: str
-
-
-class AnswerData(BaseModel):
- username: str
- answer: str
- right: bool
- time_taken: float # In milliseconds
- score: int
-
-
-class AnswerDataList(BaseModel):
- # Just a method to make a top-level list
- __root__: list[AnswerData]
-
-
-class GameInLobby(BaseModel):
- game_pin: str
- quiz_title: str
- 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)
-# github_username: str | None = ormar.Text(nullable=True)
-# reddit_username: str | None = ormar.Text(nullable=True)
-# kahoot_user_id: str | None = ormar.Text(nullable=True)
-
-
-class GameResults(ormar.Model):
- id: uuid.UUID = ormar.UUID(primary_key=True)
- quiz: uuid.UUID | Quiz = ormar.ForeignKey(Quiz, ondelete=ReferentialAction.CASCADE)
- user: uuid.UUID | User = ormar.ForeignKey(User, ondelete=ReferentialAction.CASCADE)
- timestamp: datetime = ormar.DateTime(default=datetime.now(), nullable=False)
- player_count: int = ormar.Integer(nullable=False, default=0)
- note: str | None = ormar.Text(nullable=True)
- answers: Json[list[AnswerData]] = ormar.JSON(True)
- player_scores: Json[dict[str, str]] = ormar.JSON(nullable=True)
- custom_field_data: Json[dict[str, str]] | None = ormar.JSON(nullable=True)
- title: str = ormar.Text(nullable=False)
- description: str = ormar.Text(nullable=False)
- questions: Json[list[QuizQuestion]] = ormar.JSON(nullable=False)
-
- class Meta:
- tablename = "game_results"
- metadata = metadata
- database = database
-
-
-class QuizTivityInput(BaseModel):
- title: str
- pages: list[QuizTivityPage]
-
-
-class QuizTivity(ormar.Model):
- id: uuid.UUID = ormar.UUID(primary_key=True)
- title: str = ormar.Text(nullable=False)
- created_at: datetime = ormar.DateTime(nullable=False, server_default=func.now())
- user: User | None = ormar.ForeignKey(User, ondelete=ReferentialAction.CASCADE)
- pages: list[QuizTivityPage] = ormar.JSON(nullable=False)
-
- class Meta:
- tablename = "quiztivitys"
- metadata = metadata
- database = database
-
-
-class QuizTivityShare(ormar.Model):
- id: uuid.UUID = ormar.UUID(primary_key=True)
- name: str | None = ormar.Text(nullable=True)
- expire_at: datetime | None = ormar.DateTime(nullable=True)
- 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
-
-
-class OnlyId(BaseModel):
- id: uuid.UUID
-
-
-class PublicQuizTivityShare(BaseModel):
- id: uuid.UUID
- name: str | None
- expire_in: int | None
- quiztivity: OnlyId
- user: OnlyId
-
- @classmethod
- def from_db_model(cls, data: QuizTivityShare):
- expire_in = None
- if data.expire_at is not None:
- expire_in = int((data.expire_at - datetime.now()).seconds / 60)
- return cls(
- id=data.id,
- name=data.name,
- expire_in=expire_in,
- quiztivity=OnlyId(id=data.quiztivity.id),
- user=OnlyId(id=data.user.id),
- )
-
-
-class StorageItem(ormar.Model):
- id: uuid.UUID = ormar.UUID(primary_key=True)
- uploaded_at: datetime = ormar.DateTime(nullable=False, default=datetime.now())
- mime_type: str = ormar.Text(nullable=False)
- hash: bytes | None = ormar.LargeBinary(nullable=True, min_length=16, max_length=16)
- user: User | None = ormar.ForeignKey(User, ondelete=ReferentialAction.SET_NULL)
- size: int = ormar.BigInteger(nullable=False)
- storage_path: str | None = ormar.Text(nullable=True)
- deleted_at: datetime | None = ormar.DateTime(nullable=True, default=None)
- quiztivities: list[QuizTivity] | None = ormar.ManyToMany(QuizTivity)
- quizzes: list[Quiz] | None = ormar.ManyToMany(Quiz)
- alt_text: str | None = ormar.Text(default=None, nullable=True)
- filename: str | None = ormar.Text(default=None, nullable=True)
- thumbhash: str | None = ormar.Text(default=None, nullable=True)
- 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
-
-
-class PublicStorageItem(BaseModel):
- id: uuid.UUID
- uploaded_at: datetime
- 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
- imported: bool
-
- @classmethod
- def from_db_model(cls, data: StorageItem):
- hash_data = None
- if data.hash is not None:
- hash_data = data.hash.hex()
- return cls(
- id=data.id,
- uploaded_at=data.uploaded_at,
- mime_type=data.mime_type,
- hash=hash_data,
- size=data.size,
- deleted_at=data.deleted_at,
- alt_text=data.alt_text,
- filename=data.filename,
- thumbhash=data.thumbhash,
- server=data.server,
- imported=data.imported,
- )
-
-
-class PrivateStorageItem(PublicStorageItem):
- quizzes: list[OnlyId]
- quiztivities: list[OnlyId]
-
- @classmethod
- def from_db_model(cls, data: StorageItem):
- hash_data = None
- if data.hash is not None:
- hash_data = data.hash.hex()
- quiztivities = []
- quizzes = []
- for quiz in data.quizzes:
- quizzes.append(OnlyId(id=quiz.id))
- for quiztivity in data.quiztivities:
- quiztivities.append(OnlyId(id=quiztivity.id))
- return cls(
- id=data.id,
- uploaded_at=data.uploaded_at,
- mime_type=data.mime_type,
- hash=hash_data,
- size=data.size,
- deleted_at=data.deleted_at,
- alt_text=data.alt_text,
- filename=data.filename,
- quiztivities=quiztivities,
- quizzes=quizzes,
- thumbhash=data.thumbhash,
- server=data.server,
- imported=data.imported,
- )
-
-
-class UpdateStorageItem(BaseModel):
- filename: str | None
- alt_text: str | None
-
-
-class Controller(ormar.Model):
- id: uuid.UUID = ormar.UUID(primary_key=True)
- user: uuid.UUID | User = ormar.ForeignKey(User)
- secret_key: str = ormar.String(nullable=False, max_length=24, min_length=24)
- player_name: str = ormar.Text(nullable=False)
- last_seen: datetime | None = ormar.DateTime(nullable=True)
- first_seen: datetime | None = ormar.DateTime(nullable=True)
- name: str = ormar.Text(nullable=False)
- 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
-
-
-class Rating(ormar.Model):
- id: uuid.UUID = ormar.UUID(primary_key=True)
- user: uuid.UUID | User = ormar.ForeignKey(User)
- positive: bool = ormar.Boolean(nullable=False)
- 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
diff --git a/classquiz/routers/sitemap (copy).py b/classquiz/routers/sitemap (copy).py
deleted file mode 100644
index 441a8d3..0000000
--- a/classquiz/routers/sitemap (copy).py
+++ /dev/null
@@ -1,63 +0,0 @@
-# SPDX-FileCopyrightText: 2023 Marlon W (Mawoka)
-#
-# SPDX-License-Identifier: MPL-2.0
-
-
-import datetime
-import uuid
-
-from fastapi import APIRouter, Response
-from jinja2 import Template
-from pydantic import BaseModel
-from classquiz.config import redis, settings
-from classquiz.db import database
-
-settings = settings()
-router = APIRouter()
-
-
-sitemap_template = """
-
- {bg_text} -
- -- /{vi.command} - {#if vi.args} - {#each vi.args as arg} - {{arg}}{/each} - {/if} -
-{vi.description}
-{$t('index_page.slogan')}
-Shouldn't happen!
- {/if} -{$t('index_page.create_a_quiz_from_scratch')}
-{$t('index_page.find_or_explore')}
-Shouldn't happen!
- {/if} -{$t('index_page.choose_answer_wisely')}
-{$t('index_page.check_if_chosen_wisely')}
-{$t('index_page.get_ranking_and_winners')}
-- {classquiz_reasons[selected_classquiz_reason].content} -
-Key
-Authenticate using a security key
-Password
-Authenticate using a Password
-Totp
-Authenticate using a one-time password
-- {$t('login_page.login_or_create_account')} -
- - -