ashwinR's picture
Upload 245 files
b7a7f32
raw
history blame
13.9 kB
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")
)
@router.get("/", response_model=List[Quiz])
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
@router.post("/")
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}
@router.get("/{id}", response_model=Quiz)
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
@router.put("/{id}", response_model=QuizUpdate)
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
@router.get("/{quizid}/question", response_model=List[QuizQuestionwoutAnswer])
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
@router.get("/{quizid}/question/{id}", response_model=QuizQuestionwoutAnswer)
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
@router.post("/{quizid}/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}
@router.put("/{quizid}/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
@router.post("/{quizid}/question/{id}/question_image/")
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
@router.post("/{quizid}/question/{id}/option_image/")
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"}
@router.get("/{quizid}/question/{id}/{type}/{filename}")
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
@router.delete("/{quizid}/")
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