✨ Added Moderation ratings
This commit is contained in:
@@ -33,6 +33,7 @@ from classquiz.routers import (
|
||||
box_controller,
|
||||
quiztivity,
|
||||
pixabay,
|
||||
moderation,
|
||||
)
|
||||
from classquiz.socket_server import sio
|
||||
from classquiz.helpers import meilisearch_init, telemetry_ping
|
||||
@@ -77,6 +78,7 @@ async def auth_middleware_wrapper(request: Request, call_next):
|
||||
return await rememberme_middleware(request, call_next)
|
||||
|
||||
|
||||
app.include_router(moderation.router, tags=["moderation"], prefix="/api/v1/moderation", include_in_schema=True)
|
||||
app.include_router(pixabay.router, tags=["pixabay"], prefix="/api/v1/pixabay", include_in_schema=True)
|
||||
app.include_router(quiztivity.router, tags=["quiztivity"], prefix="/api/v1/quiztivity", include_in_schema=True)
|
||||
|
||||
|
||||
@@ -128,6 +128,23 @@ async def get_current_user(token: str = Depends(oauth2_scheme)):
|
||||
return user
|
||||
|
||||
|
||||
async def get_current_moderator(token: str = Depends(oauth2_scheme)):
|
||||
try:
|
||||
payload = jwt.decode(token, settings.secret_key, algorithms=[ALGORITHM])
|
||||
email: str = payload.get("sub")
|
||||
if email is None:
|
||||
raise credentials_exception
|
||||
token_data = TokenData(email=email)
|
||||
except JWTError:
|
||||
raise credentials_exception
|
||||
user = await get_user_from_mail(email=token_data.email)
|
||||
if user is None:
|
||||
raise credentials_exception
|
||||
if user.username not in settings.mods:
|
||||
raise credentials_exception
|
||||
return user
|
||||
|
||||
|
||||
async def get_admin_user(token: str = Depends(oauth2_scheme)) -> User:
|
||||
user = await get_current_user(token)
|
||||
admin_user = await User.objects.order_by(User.created_at.asc()).get()
|
||||
|
||||
@@ -53,6 +53,7 @@ class Settings(BaseSettings):
|
||||
telemetry_enabled: bool = True
|
||||
free_storage_limit: int = 1074000000
|
||||
pixabay_api_key: str | None = None
|
||||
mods: list[str] = []
|
||||
|
||||
# storage_backend
|
||||
storage_backend: str | None = "local"
|
||||
|
||||
@@ -184,6 +184,7 @@ class Quiz(ormar.Model):
|
||||
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"
|
||||
|
||||
@@ -170,6 +170,7 @@ async def handle_import_from_excel(data: BinaryIO, user: User) -> Quiz:
|
||||
else:
|
||||
existing_quiz.questions = [*existing_quiz.questions, *questions]
|
||||
existing_quiz.updated_at = datetime.now()
|
||||
existing_quiz.mod_rating = None
|
||||
await existing_quiz.update()
|
||||
quiz = existing_quiz
|
||||
return quiz
|
||||
|
||||
@@ -129,6 +129,7 @@ async def finish_edit(edit_id: str, quiz_input: QuizInput):
|
||||
quiz.cover_image = quiz_input.cover_image
|
||||
quiz.background_color = quiz_input.background_color
|
||||
quiz.background_image = quiz_input.background_image
|
||||
quiz.mod_rating = None
|
||||
for image in images_to_delete:
|
||||
if image is not None:
|
||||
try:
|
||||
|
||||
@@ -128,5 +128,6 @@ async def import_quiz(file: UploadFile = File(), user: User = Depends(get_curren
|
||||
quiz = Quiz.parse_obj(quiz_dict)
|
||||
quiz.user_id = user.id
|
||||
quiz.imported_from_kahoot = None
|
||||
quiz.mod_rating = None
|
||||
await quiz.save()
|
||||
return quiz
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
# SPDX-FileCopyrightText: 2023 Marlon W (Mawoka)
|
||||
#
|
||||
# SPDX-License-Identifier: MPL-2.0
|
||||
import uuid
|
||||
|
||||
from fastapi import APIRouter, Depends, Response, HTTPException
|
||||
from pydantic import BaseModel
|
||||
|
||||
from classquiz.auth import get_current_moderator
|
||||
from classquiz.db.models import User, Quiz
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.get("/status")
|
||||
async def get_mod_status(resp: Response, user: User = Depends(get_current_moderator)):
|
||||
resp.status_code = 200
|
||||
resp.set_cookie("moderator", "yes", path="/")
|
||||
resp.headers.update({"Content-Type": "application/json"})
|
||||
resp.body = '{"status": "ok"}'
|
||||
return resp
|
||||
|
||||
|
||||
@router.get("/quizzes")
|
||||
async def get_newest_quizzes(
|
||||
page: int = 1, all: bool = False, user: User = Depends(get_current_moderator)
|
||||
) -> list[Quiz]:
|
||||
if page < 1:
|
||||
raise HTTPException(status_code=400, detail="page 1 is the first")
|
||||
if all:
|
||||
quizzes = (
|
||||
await Quiz.objects.paginate(page=page)
|
||||
.order_by(Quiz.updated_at.desc())
|
||||
.filter(Quiz.public == True) # noqa: E712
|
||||
.all()
|
||||
)
|
||||
else:
|
||||
# noinspection PyComparisonWithNone
|
||||
quizzes = (
|
||||
await Quiz.objects.paginate(page=page)
|
||||
.order_by(Quiz.updated_at.desc())
|
||||
.filter(Quiz.public == True) # noqa: E712
|
||||
.filter(Quiz.mod_rating == None) # noqa: E711
|
||||
.all()
|
||||
)
|
||||
return quizzes
|
||||
|
||||
|
||||
class SetModRatingForQuizInput(BaseModel):
|
||||
rating: int | None
|
||||
|
||||
|
||||
@router.post("/rating/set/{quiz_id}")
|
||||
async def set_mod_rating_for_quiz(
|
||||
data: SetModRatingForQuizInput, quiz_id: uuid.UUID, user: User = Depends(get_current_moderator)
|
||||
) -> Quiz:
|
||||
quiz = await Quiz.objects.get_or_none(public=True, id=quiz_id)
|
||||
if quiz is None:
|
||||
raise HTTPException(status_code=404, detail="Quiz not found")
|
||||
quiz.mod_rating = data.rating
|
||||
return await quiz.update()
|
||||
Reference in New Issue
Block a user