This commit is contained in:
Ross
2024-01-08 13:32:00 +00:00
parent 5d210c2696
commit f4c098b339
6 changed files with 139 additions and 15 deletions
+3
View File
@@ -13,7 +13,10 @@
- Search by image id / name / etc - Search by image id / name / etc
# Future # Future
- Exam review mode (show incorrect answers for the exam)
- Clean up user results / score pages - Clean up user results / score pages
- Remove external javascript dependencies - Remove external javascript dependencies
+38
View File
@@ -1,6 +1,9 @@
from http import HTTPStatus
from typing import List from typing import List
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from ninja import ModelSchema, Router from ninja import ModelSchema, Router
from generic.models import CidUser, ExamUserStatus
from .models import AnatomyQuestion as Question, Exam from .models import AnatomyQuestion as Question, Exam
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
@@ -9,9 +12,33 @@ from generic.decorators import check_user_in_group
from generic.constants import Group from generic.constants import Group
from ninja.security import django_auth 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() 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 QuestionSchema(ModelSchema):
class Config: class Config:
@@ -51,3 +78,14 @@ def user_exams(request):
return Exam.objects.filter(archive=False).order_by('name') return Exam.objects.filter(archive=False).order_by('name')
return Exam.objects.filter(author__id=user.id, archive=False).order_by('name') 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()
+12 -3
View File
@@ -563,6 +563,15 @@ class ExamCollectionGenericBase(models.Model):
authors = [i for i in self.author.all()] authors = [i for i in self.author.all()]
return authors 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): def check_logged_in_user(self, request: HttpRequest):
"""Helper to check if the logged in user can access the exam""" """Helper to check if the logged in user can access the exam"""
return self.check_cid_user(request=request, user_id=request.user.id) return self.check_cid_user(request=request, user_id=request.user.id)
@@ -905,7 +914,7 @@ class ExamBase(ExamCollectionGenericBase):
class ExamUserStatus(models.Model): class ExamUserStatus(models.Model):
datetime = models.DateTimeField(auto_now_add=True) 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 "CidUser", blank=True, null=True, on_delete=models.SET_NULL
) )
user_user = models.ForeignKey( user_user = models.ForeignKey(
@@ -923,10 +932,10 @@ class ExamUserStatus(models.Model):
if self.cid_user: if self.cid_user:
user = self.cid_user.cid user = self.cid_user.cid
email = self.cid_user.email_field 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: else:
user = self.user_user.username 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): class UserAnswerBase(models.Model):
+1
View File
@@ -48,6 +48,7 @@ class UserAnswerFilter(django_filters.FilterSet):
model= UserAnswer model= UserAnswer
fields = ( fields = (
"cid", "cid",
"user",
#"question", #"question",
#"answer", #"answer",
#"answer_compare", #"answer_compare",
+76 -5
View File
@@ -83,7 +83,7 @@
<ul id="new-answer-list" class="answer-list rapid"> <ul id="new-answer-list" class="answer-list rapid">
{% for answer, mark in review_user_answers %} {% for answer, mark in review_user_answers %}
<li> <li>
<pre><span class="answer not-marked" data-mark="{{mark}}">{{ answer }}</span></pre> <pre><span class="answer not-marked" data-mark="{{mark}}" title="{{answer}}">{{ answer }}</span></pre>
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>
@@ -94,6 +94,7 @@
{% endif %} {% endif %}
{% if review and not question.normal %} {% if review and not question.normal %}
<button type="button" name="check-review" class="check-review">Check review answers</button><br/> <button type="button" name="check-review" class="check-review">Check review answers</button><br/>
<div id="review-text"></div>
{% endif %} {% endif %}
<span class="save-buttons"> <span class="save-buttons">
{% if question_details.current > 1 %} {% if question_details.current > 1 %}
@@ -122,13 +123,14 @@
$(document).ready(() => { $(document).ready(() => {
{% if review and not question.normal %} {% if review and not question.normal %}
$("button.check-review").click(() =>{ $("button.check-review").click(() =>{
$(".review-details").remove();
$(".marking-list .answer").each((n, el) => { $(".marking-list .answer").each((n, el) => {
console.log(n, el, el.dataset); console.log(n, el, el.dataset);
if (el.dataset.mark != el.dataset.newmark) { if (el.dataset.mark != el.dataset.newmark) {
$(el).after(`<span class="current-mark"> saved [${el.dataset.mark}]</span>`) $(el).after(`<span class="current-mark review-details"> saved [${el.dataset.mark}]</span>`)
} }
}) })
$("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(); $(".save-buttons").show();
}) })
@@ -147,11 +149,80 @@
{% endblock %} {% endblock %}
{% block js %}
<script type="text/javascript">
$(document).ready(function () {
console.log("loads")
$(".answer").each(function(n, el) {
console.log(n)
console.log(el)
uri = encodeURI(el.title)
button = $(`<a tabindex="0" class='popover-button' role="button" data-toggle="popover" data-trigger="focus" title='${el.title}'>+</a>`)
popover = $(`
<div id="popover-content-${n}" class="hide popoverdiv">
<ul class="popovermenu">
<li><a href="https://www.google.com/search?q=${uri}" target="_blank">Google answer</a></li>
<span class="copy-to-clipboard" data-text="${el.title}">Copy to clipboard</span>
</ul>
</div>`)
$(el).before(popover)
$(el).before(button)
// This is horrible
$(button).popover({
html: true,
trigger: "focus",
content: function () {
return $(`#popover-content-${n}`).html();
}
}).on('shown.bs.popover', function(e, n
) {
text = el.title
$(`.copy-to-clipboard`).on("click", function(e) {
navigator.clipboard.writeText(text);
});
});
})
}
)
</script>
{% endblock %}
{% block css %} {% block css %}
<style> <style>
#primary-answer { #primary-answer {
font-weight: strong; font-weight: strong;
color: white; color: white;
} }
</style> .popover-button {
padding-right: 5px;
cursor: help;
text-decoration: none;
color: purple;
}
.popover, .popover-header {
}
.popovermenu {
list-style: none;
padding: 0px;
margin: 0px;
}
.popovermenu li {
}
.popovermenu li a {
color: blue;
}
.answer-list{
list-style: none;
padding-left: 0px;
}
</style>
{% endblock css %} {% endblock css %}
+4 -2
View File
@@ -2,7 +2,8 @@
<div class="alert alert-warning" role="alert"> <div class="alert alert-warning" role="alert">
<ul> <ul>
{% for status in statuses %} {% for status in statuses %}
<li>{{status.datetime|date:"SHORT_DATETIME_FORMAT"}}: <li>
{% comment %} {{status.datetime|date:"SHORT_DATETIME_FORMAT"}}:
{% if status.user_user %} {% if status.user_user %}
{{status.user_user}} {{status.user_user}}
@@ -11,8 +12,9 @@
{{status.cid_user}} {{status.cid_user}}
{% endif %} {% endif %}
{{status.status}} {{status.status}}
{{status.extra}} {{status.extra}} {% endcomment %}
{{status}}
</li> </li>
{% endfor %} {% endfor %}