Spaces:
Build error
Build error
from datetime import datetime | |
from typing import Any, List, Dict | |
from hashlib import sha1 | |
import os | |
import shutil | |
from fastapi import APIRouter, Depends, HTTPException | |
from sqlalchemy.orm import Session | |
from core.config import settings | |
import json | |
from models import User | |
from utils import deps | |
from cruds import crud_quiz, crud_question | |
from schemas import ( | |
Quiz, | |
QuizCreate, | |
QuizUpdate, | |
QuizAnswer, | |
QuizAnswerCreate, | |
QuizAnswerUpdate, | |
QuizQuestion, | |
QuizQuestionCreate, | |
QuizQuestionUpdate, | |
QuizQuestionwoutAnswer, | |
) | |
from fastapi import FastAPI, File, UploadFile, HTTPException | |
from fastapi.responses import FileResponse | |
import aiofiles | |
from core.config import settings | |
router = APIRouter() | |
QUIZ_ROUTE: str = "quiz" | |
QUIZ_QUESTION_UPLOAD_DIR: str = "question_image" | |
QUIZ_OPTION_UPLOAD_DIR: str = "option_image" | |
hashedQuestionRoute = sha1( | |
QUIZ_QUESTION_UPLOAD_DIR.encode(encoding="UTF-8", errors="strict") | |
) | |
hashedOptionRoute = sha1( | |
QUIZ_OPTION_UPLOAD_DIR.encode(encoding="UTF-8", errors="strict") | |
) | |
async def get_quiz( | |
db: Session = Depends(deps.get_db), | |
skip: int = 0, | |
limit: int = -1, | |
current_user: User = Depends(deps.get_current_active_user), | |
) -> Any: | |
quiz = crud_quiz.get_multi(db, skip=skip, limit=limit) | |
if current_user.user_type == settings.UserType.STUDENT.value: | |
quiz_list = [] | |
for quizItem in quiz: | |
for group in quizItem.group: | |
if group.id == current_user.group.id: | |
quiz_list.append(quizItem) | |
return quiz_list | |
if current_user.user_type == settings.UserType.TEACHER.value: | |
quiz_list = [] | |
for quizItem in quiz: | |
for instructor in quizItem.instructor: | |
if current_user.id == instructor.id: | |
quiz_list.append(quizItem) | |
return quiz_list | |
if current_user.user_type <= settings.UserType.ADMIN.value: | |
return quiz | |
async def create_quiz( | |
db: Session = Depends(deps.get_db), | |
*, | |
obj_in: QuizCreate, | |
current_user: User = Depends(deps.get_current_active_teacher_or_above), | |
) -> Any: | |
quiz = crud_quiz.create(db, obj_in=obj_in) | |
return {"msg": "success", "id": quiz.id} | |
async def get_specific_quiz( | |
db: Session = Depends(deps.get_db), | |
*, | |
id: int, | |
current_user: User = Depends(deps.get_current_active_user), | |
) -> Any: | |
if current_user.user_type == settings.UserType.STUDENT.value: | |
quiz_list = await get_quiz(db=db, current_user=current_user) | |
for quiz in quiz_list: | |
if quiz.id == id: | |
return quiz | |
raise HTTPException( | |
status_code=403, detail="Error ID: 133" | |
) # not accessible by the Student user | |
if current_user.user_type == settings.UserType.TEACHER.value: | |
quiz_list = await get_quiz(db=db, current_user=current_user) | |
for quiz in quiz_list: | |
if quiz.id == id: | |
return quiz | |
raise HTTPException( | |
status_code=403, detail="Error ID: 134" | |
) # not accessible by the Teacher user | |
if current_user.user_type <= settings.UserType.ADMIN.value: | |
quiz = crud_quiz.get(db, id) | |
return quiz | |
async def update_quiz( | |
db: Session = Depends(deps.get_db), | |
*, | |
id: int, | |
obj_in: QuizUpdate, | |
current_user: User = Depends(deps.get_current_active_teacher_or_above), | |
) -> Any: | |
quiz = crud_quiz.get(db, id) | |
quiz = crud_quiz.update(db, db_obj=quiz, obj_in=obj_in) | |
return quiz | |
async def get_question( | |
db: Session = Depends(deps.get_db), | |
*, | |
quizid: int, | |
current_user: User = Depends(deps.get_current_active_user), | |
) -> Any: | |
quiz = await get_specific_quiz(db, id=quizid, current_user=current_user) | |
if not quiz: | |
raise HTTPException( | |
status_code=404, detail="Error ID = 135" | |
) # quiz not found in database | |
questions = crud_question.get_all_by_quiz_id(db, quiz_id=quiz.id) | |
return questions | |
async def get_specific_question( | |
db: Session = Depends(deps.get_db), | |
*, | |
quizid: int, | |
id: int, | |
current_user: User = Depends(deps.get_current_active_user), | |
) -> Any: | |
question = crud_question.get_by_quiz_id_question_id( | |
db=db, quiz_id=quizid, questionid=id | |
) | |
if not question: | |
raise HTTPException( | |
status_code=404, detail="Error ID: 136" | |
) # question specific to that id not found | |
return question | |
async def create_question( | |
db: Session = Depends(deps.get_db), | |
*, | |
quizid: int, | |
obj_in: QuizQuestionCreate, | |
current_user: User = Depends(deps.get_current_active_teacher_or_above), | |
) -> Any: | |
obj_in.quiz_id = quizid | |
# obj_in.question_image = "question" | |
# obj_in.options = [ | |
# {"image": eachDict["image"] if eachDict["image"] == "" else f"Options{index+1}"} | |
# for index, eachDict in enumerate(obj_in.options) | |
# ] | |
question = crud_question.create(db, obj_in=obj_in) | |
quiz = crud_quiz.get(db=db, id=quizid) | |
newMarks = quiz.total_marks + question.marks | |
newQuiz = QuizUpdate(total_marks=newMarks) | |
crud_quiz.update(db=db, db_obj=quiz, obj_in=newQuiz) | |
hashedQuizId = sha1(str(quizid).encode(encoding="UTF-8", errors="strict")) | |
hashedQuestionId = sha1(str(question.id).encode(encoding="UTF-8", errors="strict")) | |
# if the question is said to have a IMAGE then only create the folder to store the image | |
FILE_PATH = os.path.join( | |
settings.UPLOAD_DIR_ROOT, | |
QUIZ_ROUTE, | |
hashedQuizId.hexdigest(), | |
hashedQuestionId.hexdigest(), | |
) | |
FILE_PATH_QUESTION = os.path.join(FILE_PATH, hashedQuestionRoute.hexdigest()) | |
FILE_PATH_OPTION = os.path.join(FILE_PATH, hashedOptionRoute.hexdigest()) | |
if not os.path.exists(FILE_PATH_QUESTION): | |
os.makedirs(FILE_PATH_QUESTION) | |
if not os.path.exists(FILE_PATH_OPTION): | |
os.makedirs(FILE_PATH_OPTION) | |
return {"msg": "success", "id": question.id} | |
async def update_question( | |
db: Session = Depends(deps.get_db), | |
*, | |
quizid: int, | |
obj_in: QuizQuestionUpdate, | |
id: int, | |
current_user: User = Depends(deps.get_current_active_teacher_or_above), | |
) -> Any: | |
question = crud_question.get(db, id) | |
# on question_type update, create folder to store image if not already present | |
FILE_PATH_QUESTION = os.path.join( | |
settings.UPLOAD_DIR_ROOT, | |
QUIZ_QUESTION_UPLOAD_DIR, | |
f"{quizid}/{question.id}", | |
) | |
if not os.path.exists(FILE_PATH_QUESTION): | |
os.makedirs(FILE_PATH_QUESTION) | |
# on option_type update, create folder to store image if not already present | |
# if (obj_in.answer_type == AnswerType.IMAGE_OPTIONS.value) and ( | |
# question.answer_type != obj_in.answer_type | |
# ): | |
# FILE_PATH_OPTION = os.path.join( | |
# "static", QUIZ_OPTION_UPLOAD_DIR, f"{quizid}/{question.id}" | |
# ) | |
# FILE_PATH_OPTION = os.path.join(current_directory, FILE_PATH_OPTION) | |
# if not os.path.exists(FILE_PATH_OPTION): | |
# os.makedirs(FILE_PATH_OPTION) | |
if question.quiz_id == quizid == obj_in.quiz_id: | |
question = crud_question.update(db, db_obj=question, obj_in=obj_in) | |
return question | |
else: | |
raise HTTPException( | |
status_code=403, detail="Error ID = 137" | |
) # noqa Access Denied! | |
# XXX: ENDPOINTS for questions to write and read files and answers to those files | |
# FIXME: Uploaded files directory fix it | |
async def create_question_files( | |
db: Session = Depends(deps.get_db), | |
files: List[UploadFile] = File(...), | |
current_user=Depends(deps.get_current_active_teacher_or_above), | |
*, | |
quizid: int, | |
id: int, | |
): | |
question = await get_specific_question( | |
db, quizid=quizid, id=id, current_user=current_user | |
) | |
hashedQuizId = sha1(str(quizid).encode(encoding="UTF-8", errors="strict")) | |
hashedQuestionId = sha1(str(id).encode(encoding="UTF-8", errors="strict")) | |
FILE_QUESTION_PATH = os.path.join( | |
QUIZ_ROUTE, | |
hashedQuizId.hexdigest(), | |
hashedQuestionId.hexdigest(), | |
hashedQuestionRoute.hexdigest(), | |
) | |
FILE_PATH = os.path.join( | |
settings.UPLOAD_DIR_ROOT, | |
FILE_QUESTION_PATH, | |
) | |
questionImages = [] | |
fileIndex = 0 | |
for file in files: | |
fileName, fileExtension = os.path.splitext(file.filename) | |
hashedFileName = sha1( | |
(fileName + str(fileIndex)).encode(encoding="UTF-8", errors="strict") | |
) | |
fileIndex = fileIndex + 1 | |
filename = f"{FILE_PATH}/{hashedFileName.hexdigest()}{fileExtension}" | |
async with aiofiles.open(filename, mode="wb") as f: | |
content = await file.read() | |
await f.write(content) | |
questionImages.append( | |
f"{FILE_QUESTION_PATH}/{hashedFileName.hexdigest()}{fileExtension}" | |
) | |
obj_in = QuizQuestionUpdate(quiz_id=quizid, question_image=questionImages) | |
updated = crud_question.update(db=db, db_obj=question, obj_in=obj_in) | |
return updated | |
async def create_option_files( | |
options: List, | |
db: Session = Depends(deps.get_db), | |
files: List[UploadFile] = File(...), | |
current_user=Depends(deps.get_current_active_teacher_or_above), | |
*, | |
quizid: int, | |
id: int, | |
): | |
options = json.loads(options[0]) | |
question = await get_specific_question( | |
db, quizid=quizid, id=id, current_user=current_user | |
) | |
hashedQuizId = sha1(str(quizid).encode(encoding="UTF-8", errors="strict")) | |
hashedQuestionId = sha1(str(id).encode(encoding="UTF-8", errors="strict")) | |
FILE_OPTION_PATH = os.path.join( | |
QUIZ_ROUTE, | |
hashedQuizId.hexdigest(), | |
hashedQuestionId.hexdigest(), | |
hashedOptionRoute.hexdigest(), | |
) | |
FILE_PATH = os.path.join( | |
settings.UPLOAD_DIR_ROOT, | |
FILE_OPTION_PATH, | |
) | |
fileIndex = 0 | |
for file in files: | |
fileName, fileExtension = os.path.splitext(file.filename) | |
hashedFileName = sha1( | |
(fileName + str(fileIndex)).encode(encoding="UTF-8", errors="strict") | |
) | |
fileIndex = fileIndex + 1 | |
filename = f"{FILE_PATH}/{hashedFileName.hexdigest()}{fileExtension}" | |
async with aiofiles.open(filename, mode="wb") as f: | |
content = await file.read() | |
await f.write(content) | |
alreadyModified = [] | |
for index, eachDict in enumerate(options): | |
if eachDict["image"] == file.filename: | |
if index not in alreadyModified: | |
eachDict[ | |
"image" | |
] = f"{FILE_OPTION_PATH}/{hashedFileName.hexdigest()}{fileExtension}" | |
alreadyModified.append(index) | |
break | |
obj_in = QuizQuestionUpdate(quiz_id=quizid, options=json.dumps(options)) | |
updated = crud_question.update(db=db, db_obj=question, obj_in=obj_in) | |
return {"msg": "success"} | |
async def get_image( | |
db: Session = Depends(deps.get_db), | |
*, | |
quizid: int, | |
id: int, | |
filename: str, | |
type: int, | |
current_user: User = Depends(deps.get_current_active_user), | |
): | |
question = await get_specific_question( | |
db, quizid=quizid, id=id, current_user=current_user | |
) | |
if not question: | |
raise HTTPException(status_code=404, detail="Error ID: 138") | |
# question not found error | |
if type == 1: | |
if filename in question.question_image: | |
FILE_PATH = os.path.join( | |
settings.UPLOAD_DIR_ROOT, QUIZ_QUESTION_UPLOAD_DIR, f"{quizid}/{id}" | |
) | |
else: | |
raise HTTPException( | |
status_code=403, detail="Error ID: 139" | |
) # file not of that question | |
if type == 2: | |
if filename in question.option_image: | |
FILE_PATH = os.path.join( | |
settings.UPLOAD_DIR_ROOT, QUIZ_OPTION_UPLOAD_DIR, f"{quizid}/{id}" | |
) | |
else: | |
raise HTTPException( | |
status_code=403, detail="Error ID: 140" | |
) # file not of that question | |
FILE_PATH = os.path.join(FILE_PATH, filename) | |
if os.path.isfile(FILE_PATH): | |
file = FileResponse(f"{FILE_PATH}") | |
return file | |
else: | |
raise HTTPException( | |
status_code=404, detail="Error ID: 141" | |
) # no file exist in the path | |
async def delete_quiz( | |
db: Session = Depends(deps.get_db), | |
*, | |
quizid=int, | |
current_user: User = Depends(deps.get_current_active_teacher), | |
): | |
quiz = crud_quiz.get(db, id=quizid) | |
if not quiz: | |
raise HTTPException(status_code=404, detail="Error ID: 143") | |
for instructor in quiz.instructor: | |
if instructor.id == current_user.id: | |
print(instructor.id, current_user.id) | |
quiz = crud_quiz.remove(db, id=quizid) | |
hashedQuizId = sha1(str(quizid).encode(encoding="UTF-8", errors="strict")) | |
FILE_PATH = os.path.join( | |
settings.UPLOAD_DIR_ROOT, | |
QUIZ_ROUTE, | |
hashedQuizId.hexdigest(), | |
) | |
if os.path.exists(FILE_PATH): | |
shutil.rmtree(FILE_PATH) | |
return {"msg": "delete success"} | |
raise HTTPException( | |
status_code=403, | |
detail="Error ID: 142", | |
) # teacher not associated with the quiz | |