feat: add new endpoints for fetching active exams by type and cid; enhance exam handling in views

This commit is contained in:
Ross
2026-07-04 22:40:48 +01:00
parent 856fb0bf08
commit d8bacfefa9
7 changed files with 73 additions and 20 deletions
@@ -18,7 +18,7 @@
</select>
</div>
</div>
<ul class="list-group list-group-flush">
<ul class="list-group list-group-flush score-answer-list">
{% for ans, score, correct_answer in answers_and_marks %}
<li class="list-group-item d-flex justify-content-between align-items-start" data-score="{{ score }}">
<div class="d-flex align-items-start">
@@ -6,7 +6,7 @@
<div class="card">
<div class="card-body p-0">
<ul class="list-group list-group-flush">
<ul class="list-group list-group-flush score-answer-list">
{% for question, ans in answers_and_marks %}
<li class="list-group-item">
<div class="d-flex align-items-start">
+12
View File
@@ -137,6 +137,18 @@ urlpatterns = [
name="active_exams_cid_unbased",
),
path("exam/json/unbased", views.active_exams_unbased, name="active_exams_unbased"),
path("exam/json/<str:exam_type>/", views.active_exams_by_type, name="active_exams_by_type"),
path(
"exam/json/<str:exam_type>/<int:cid>/<str:passcode>",
views.active_exams_by_type,
name="active_exams_by_type_cid",
),
path(
"exam/json/<str:exam_type>/unbased/<int:cid>/<str:passcode>",
views.active_exams_by_type_unbased,
name="active_exams_by_type_cid_unbased",
),
path("exam/json/<str:exam_type>/unbased", views.active_exams_by_type_unbased, name="active_exams_by_type_unbased"),
path("exam/submit", views.exam_submit, name="global_exam_answers_submit"),
# path('', include('generic.urls')),
path(
+44
View File
@@ -879,7 +879,9 @@ def cid_user_overview(request, cid, passcode):
UserAnswer, Exam = EXAM_ANSWER_MAP[exam_type]
exam_answers = UserAnswer.objects.filter(cid=cid)
exam_ids = exam_answers.values_list("exam").distinct()
logger.debug(f"DEBUG cid_user_overview: {exam_type=}, {cid=}, answers_count={exam_answers.count()}, exam_ids={list(exam_ids)}")
exams_to_add = Exam.objects.filter(id__in=exam_ids, **kwargs)
logger.debug(f"DEBUG cid_user_overview: {exams_to_add=}")
if exams_to_add:
exams.append((exam_type, exams_to_add))
@@ -947,6 +949,48 @@ def active_exams_unbased(request, cid=None, passcode=None):
return active_exams(request, cid, passcode, based=False)
def active_exams_by_type(request, exam_type, cid=None, passcode=None, based=True):
active_exams_data = {}
if cid is not None:
active_exams_data["cid"] = cid
cid_user = CidUser.objects.filter(cid=cid).first()
if not cid_user or cid_user.passcode != passcode:
return JsonResponse({"status": "invalid"}, status=401)
exams = []
if exam_type == "anatomy":
exams = AnatomyExamViews.active_exams(
request, False, cid=cid, passcode=passcode
)
elif exam_type == "rapid":
exams = RapidsExamsViews.active_exams(
request, False, cid=cid, passcode=passcode
)
elif exam_type == "short":
exams = ShortsExamsViews.active_exams(
request, False, cid=cid, passcode=passcode
)
elif exam_type == "long":
exams = LongsExamsViews.active_exams(
request, False, cid=cid, passcode=passcode
)
active_exams_data["exams"] = exams
if request.user and request.user.is_authenticated:
active_exams_data["user"] = request.user.username
active_exams_data["user_id"] = request.user.pk
else:
active_exams_data["user"] = False
return JsonResponse(active_exams_data)
def active_exams_by_type_unbased(request, exam_type, cid=None, passcode=None):
return active_exams_by_type(request, exam_type, cid, passcode, based=False)
@csrf_exempt
def exam_submit(request):
if request.method == "POST":
+5 -3
View File
@@ -26,7 +26,7 @@
{% if exams %}
<div class="card mb-3">
<div class="card-header">{{ exam_type|title }}</div>
<ul class="list-group list-group-flush">
<ul class="list-group list-group-flush {{ exam_type|lower }}">
{% for exam in exams %}
<li class="list-group-item d-flex justify-content-between align-items-center" data-exam-id="{{exam.pk}}">
<div>
@@ -40,7 +40,9 @@
</div>
<div class="text-end">
{% if exam.active %}
<a class="btn btn-sm btn-primary me-2" href="{{exam.get_take_url}}?cid={{cid}}&passcode={{passcode}}&exam_mode=true" target="_blank" title="Click to take exam">Start</a>
<a href="{{exam.get_take_url}}?cid={{cid}}&passcode={{passcode}}&exam_mode=true" target="_blank" title="Click to take exam">
<button class="btn btn-sm btn-primary me-2 start-button">Start</button>
</a>
{% endif %}
{% if exam.publish_results %}
<a class="btn btn-sm btn-outline-secondary" href="{% url exam_type|add:':exam_scores_cid_user' pk=exam.pk cid=cid passcode=passcode %}" title="Click to view results">View Results</a>
@@ -62,7 +64,7 @@
{% if exams %}
<div class="card mb-3">
<div class="card-header">{{ exam_type|title }}</div>
<ul class="list-group list-group-flush">
<ul class="list-group list-group-flush {{ exam_type|lower }}">
{% for exam in exams %}
<li class="list-group-item d-flex justify-content-between align-items-center" data-exam-id="{{exam.pk}}">
<a href="{% url exam_type|add:':exam_scores_cid_user' pk=exam.pk cid=cid passcode=passcode %}">{{ exam }}</a>
+9 -7
View File
@@ -34,7 +34,7 @@
<div class="row">
<div class="col-lg-8">
<div class="card mb-3">
<div class="card mb-3" id="exam-assigned">
<div class="card-header">
<h5 class="mb-0">Assigned Exams</h5>
</div>
@@ -43,9 +43,9 @@
{% for exam_type, exams in available_exams %}
{% if exams %}
<h6 class="mt-3">{{ exam_type|title }}</h6>
<ul class="list-group">
<ul class="list-group {{ exam_type|lower }}">
{% for exam in exams %}
<li class="list-group-item d-flex justify-content-between align-items-center {% if exam.is_hidden %}opacity-50 bg-secondary bg-opacity-10{% endif %}">
<li class="list-group-item d-flex justify-content-between align-items-center {% if exam.is_hidden %}opacity-50 bg-secondary bg-opacity-10{% endif %}" data-exam-id="{{exam.pk}}">
<div>
{{ exam }}
{% if exam.active %}<span class="badge bg-success ms-2">Active</span>{% endif %}
@@ -55,7 +55,9 @@
<div class="d-flex align-items-center gap-2">
<div class="btn-group btn-group-sm" role="group">
{% if exam.active %}
<a class="btn btn-primary" href="{{ exam.get_take_url }}" target="_blank" title="Click to take exam">Start</a>
<a href="{{ exam.get_take_url }}" target="_blank" title="Click to take exam">
<button class="btn btn-primary start-button">Start</button>
</a>
{% endif %}
{% if exam.publish_results %}
<a class="btn btn-outline-secondary" href="{% url exam_type|lower|add:':exam_scores_user' pk=exam.pk %}" title="Click to view results">Results</a>
@@ -81,7 +83,7 @@
</div>
{% if all_exams %}
<div class="card mb-3">
<div class="card mb-3" id="exam-results">
<div class="card-header">
<h5 class="mb-0">Exam Results</h5>
</div>
@@ -90,9 +92,9 @@
{% for exam_type, exams in all_exams %}
{% if exams %}
<h6 class="mt-3">{{ exam_type|title }}</h6>
<ul class="list-group">
<ul class="list-group {{ exam_type|lower }}">
{% for exam in exams %}
<li class="list-group-item d-flex justify-content-between align-items-center {% if exam.is_hidden %}opacity-50 bg-secondary bg-opacity-10{% endif %}">
<li class="list-group-item d-flex justify-content-between align-items-center {% if exam.is_hidden %}opacity-50 bg-secondary bg-opacity-10{% endif %}" data-exam-id="{{exam.pk}}">
<div>
{% if admin %}
<a href="{% url exam_type|add:':exam_scores_user_admin' pk=exam.pk user_id=user.pk %}">{{ exam }}</a>
+1 -8
View File
@@ -1,8 +1 @@
1. the time / date filters on the logs viewing viewer should be labeled and better organised
2. on the Unused Media Cleanup page we need to shown loading dialogs when the preview unused files and delete unused files buttons are pressed. please also check the recovered space size calculations as it does not match with what actually happens.
3. on the series tools page we are able to generate reconstructions of selected series. currently the process works but the created dicoms do not align correctly with the original soruce (in terms of 3d volume space) indicating that there is a bug in the reconstruction code. in an ideal setting we would have an option to define /manually set the reconstruction alignment to generate the reconstructions from to account for scans where the patient is not corrcetly aligned with the x,y,z axis. this would be possible by making use of the crosshairs tools in the cornerstone3d library (https://www.cornerstonejs.org/live-examples/crosshairs) to extend the dv3d viewer in a way that allows setting the desired reconstruction planes, projection (min/max/average) mode and slab thickness. please look at fixing the 3d volume space of the current implemenatation and adding a new advanced page that implements the new dv3d implementation (leavving both in place).
i have added a dicom series into the folder temp/recon that you can use to test if required.
4. search widgets such as case-search-widget should show a loading indicator whilst awaiting htmx responses.
RTS: we should show a refresh button for each of the exam/packet sections (as well as a global refresh that refreshes all)