From f4c098b3396f38fbb7f99887635382fa9a21d1aa Mon Sep 17 00:00:00 2001 From: Ross Date: Mon, 8 Jan 2024 13:32:00 +0000 Subject: [PATCH] . --- TODO.md | 3 ++ anatomy/api.py | 40 +++++++++++++- generic/models.py | 15 ++++-- physics/filters.py | 1 + rapids/templates/rapids/mark.html | 89 +++++++++++++++++++++++++++---- templates/exam_user_status.html | 6 ++- 6 files changed, 139 insertions(+), 15 deletions(-) diff --git a/TODO.md b/TODO.md index 9c7e76ba..80de9cb2 100644 --- a/TODO.md +++ b/TODO.md @@ -13,7 +13,10 @@ - Search by image id / name / etc + # Future +- Exam review mode (show incorrect answers for the exam) + - Clean up user results / score pages - Remove external javascript dependencies diff --git a/anatomy/api.py b/anatomy/api.py index 2f5dd342..afee2fda 100644 --- a/anatomy/api.py +++ b/anatomy/api.py @@ -1,6 +1,9 @@ +from http import HTTPStatus from typing import List from django.shortcuts import get_object_or_404 from ninja import ModelSchema, Router + +from generic.models import CidUser, ExamUserStatus from .models import AnatomyQuestion as Question, Exam from django.core.exceptions import PermissionDenied @@ -9,9 +12,33 @@ from generic.decorators import check_user_in_group from generic.constants import Group from ninja.security import django_auth +from ninja.errors import HttpError +from django.conf import settings +from django.contrib.auth import get_user_model router = Router() +class CidUserSchema(ModelSchema): + class Config: + model = CidUser + + model_fields = ["cid", "name", "email"] + +class UserUserSchema(ModelSchema): + class Config: + model = get_user_model() + model_fields = ["id", "email"] + +class ExamUserStatusSchema(ModelSchema): + + cid_user : CidUserSchema | None + user_user : UserUserSchema | None + class Config: + model = ExamUserStatus + + #model_fields = ["question", "history", "feedback", "normal", "laterality"] + #model_fields = "__all__" + model_fields = ["id", "datetime", "status", "extra"] class QuestionSchema(ModelSchema): class Config: @@ -50,4 +77,15 @@ def user_exams(request): if user.groups.filter(name="anatomy_checker").exists(): return Exam.objects.filter(archive=False).order_by('name') - return Exam.objects.filter(author__id=user.id, archive=False).order_by('name') \ No newline at end of file + return Exam.objects.filter(author__id=user.id, archive=False).order_by('name') + + +@router.get('/exam/{exam_id}/user_status', response=List[ExamUserStatusSchema]) +def get_exam_user_status(request, exam_id: int, latest:bool=True): + exam = get_object_or_404(Exam, id=exam_id) + if not exam.check_user_can_edit(request.user): + raise HttpError(HTTPStatus.UNAUTHORIZED,"Exam author required") + + return exam.exam_user_status.all() + + \ No newline at end of file diff --git a/generic/models.py b/generic/models.py index 898e5f20..802d9fe9 100644 --- a/generic/models.py +++ b/generic/models.py @@ -563,6 +563,15 @@ class ExamCollectionGenericBase(models.Model): authors = [i for i in self.author.all()] return authors + def check_user_can_edit(self, user: User): + if user.is_superuser: + return True + + if user in self.get_author_objects(): + return True + + return False + def check_logged_in_user(self, request: HttpRequest): """Helper to check if the logged in user can access the exam""" return self.check_cid_user(request=request, user_id=request.user.id) @@ -905,7 +914,7 @@ class ExamBase(ExamCollectionGenericBase): class ExamUserStatus(models.Model): datetime = models.DateTimeField(auto_now_add=True) - cid_user = models.ForeignKey( + cid_user: "CidUser" = models.ForeignKey( "CidUser", blank=True, null=True, on_delete=models.SET_NULL ) user_user = models.ForeignKey( @@ -923,10 +932,10 @@ class ExamUserStatus(models.Model): if self.cid_user: user = self.cid_user.cid email = self.cid_user.email_field - return f"{self.datetime}: {user} () - {self.status} ({self.extra})" + return f"{self.datetime:%Y-%m-%d %H:%M:%S}: {user} ({email}) - {self.status} ({self.extra})" else: user = self.user_user.username - return f"{self.datetime}: {user} - {self.status} ({self.extra})" + return f"{self.datetime:%Y-%m-%d %H:%M:%S}: {user} - {self.status} ({self.extra})" class UserAnswerBase(models.Model): diff --git a/physics/filters.py b/physics/filters.py index 266eb45f..affa030f 100644 --- a/physics/filters.py +++ b/physics/filters.py @@ -48,6 +48,7 @@ class UserAnswerFilter(django_filters.FilterSet): model= UserAnswer fields = ( "cid", + "user", #"question", #"answer", #"answer_compare", diff --git a/rapids/templates/rapids/mark.html b/rapids/templates/rapids/mark.html index d6885dee..2f31b140 100644 --- a/rapids/templates/rapids/mark.html +++ b/rapids/templates/rapids/mark.html @@ -83,7 +83,7 @@ @@ -94,6 +94,7 @@ {% endif %} {% if review and not question.normal %}
+
{% endif %} {% if question_details.current > 1 %} @@ -122,13 +123,14 @@ $(document).ready(() => { {% if review and not question.normal %} $("button.check-review").click(() =>{ + $(".review-details").remove(); $(".marking-list .answer").each((n, el) => { console.log(n, el, el.dataset); if (el.dataset.mark != el.dataset.newmark) { - $(el).after(` saved [${el.dataset.mark}]`) + $(el).after(` saved [${el.dataset.mark}]`) } }) - $("button.check-review").after("Discreptant answers are show above. Clickinc next / save will overwrite saved scores with your current scoring.").hide() + $("#review-text").html("Discrepant answers are show above. Clicking next / save will overwrite saved scores with your current scoring.") $(".save-buttons").show(); }) @@ -147,11 +149,80 @@ {% endblock %} +{% block js %} + + + +{% endblock %} + {% block css %} - + {% endblock css %} \ No newline at end of file diff --git a/templates/exam_user_status.html b/templates/exam_user_status.html index dc0f7b3b..4b3d396c 100644 --- a/templates/exam_user_status.html +++ b/templates/exam_user_status.html @@ -2,7 +2,8 @@