some refactoring and atlas improvements

This commit is contained in:
Ross
2023-07-24 11:56:12 +01:00
parent e6469e70f0
commit dd300afd63
26 changed files with 521 additions and 170 deletions
@@ -0,0 +1,36 @@
# Generated by Django 4.1.4 on 2023-07-24 09:06
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('generic', '0006_alter_ciduserexam_completed'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('atlas', '0004_casecollection_self_review'),
]
operations = [
migrations.AddField(
model_name='casecollection',
name='user_user_groups',
field=models.ManyToManyField(blank=True, help_text='These groups define which candidates are able to be added to the exams/collection.', related_name='casecollection_user_user_groups', to='generic.userusergroup'),
),
migrations.AddField(
model_name='casecollection',
name='valid_user_users',
field=models.ManyToManyField(blank=True, related_name='user_casecollection_exams', to=settings.AUTH_USER_MODEL),
),
migrations.AlterField(
model_name='casecollection',
name='author',
field=models.ManyToManyField(blank=True, help_text='Author of the collection', related_name='casecollection_authored_cases', to=settings.AUTH_USER_MODEL),
),
migrations.AlterField(
model_name='casecollection',
name='cid_user_groups',
field=models.ManyToManyField(blank=True, help_text='These groups define which candidates are able to be added to the exams/collection.', related_name='casecollection_cid_user_groups', to='generic.cidusergroup'),
),
]
@@ -0,0 +1,43 @@
# Generated by Django 4.1.4 on 2023-07-24 09:24
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('atlas', '0005_casecollection_user_user_groups_and_more'),
]
operations = [
migrations.AddField(
model_name='casecollection',
name='candidates_only',
field=models.BooleanField(default=True, help_text='If the exam/collection is only available to candidates who have been added'),
),
migrations.AlterField(
model_name='casecollection',
name='active',
field=models.BooleanField(default=False, help_text='If an exam/collection should be available to take'),
),
migrations.AlterField(
model_name='casecollection',
name='archive',
field=models.BooleanField(default=False, help_text='Archived exams/collections will remain on the test system but will not be displayed by default'),
),
migrations.AlterField(
model_name='casecollection',
name='name',
field=models.CharField(help_text='Name of the exam/collection', max_length=200),
),
migrations.AlterField(
model_name='casecollection',
name='open_access',
field=models.BooleanField(default=False, help_text='If the exam/collection is freely accessible (to view and edit on the test system)'),
),
migrations.AlterField(
model_name='casecollection',
name='publish_results',
field=models.BooleanField(default=False, help_text='If an exam/collections results should be available'),
),
]
+15 -42
View File
@@ -2,7 +2,7 @@ import json
import os
import pathlib
from django.http import Http404
from django.http import Http404, HttpRequest
from rad.settings import REMOTE_URL
from django.db.models.fields.files import ImageField
from django.db.models.fields.related import ForeignKey
@@ -46,6 +46,7 @@ from generic.models import (
SeriesBase,
SeriesImageBase,
Modality,
UserUserGroup,
)
# from generic.models import Examination, Site, Condition, Sign
@@ -413,18 +414,8 @@ class Series(SeriesBase):
class CaseCollection(ExamCollectionGenericBase):
app_name = "atlas"
name = models.CharField(max_length=255, unique=True)
cases = models.ManyToManyField(Case, through="CaseDetail")
publish_results = models.BooleanField(
help_text="If a collection should published", default=False
)
active = models.BooleanField(
help_text="If a collection should be available", default=True
)
show_title_pre = models.BooleanField(
default=False, help_text="Show the title of the cases (pre exam)"
)
@@ -461,20 +452,31 @@ class CaseCollection(ExamCollectionGenericBase):
settings.AUTH_USER_MODEL,
blank=True,
help_text="Author of the collection",
# related_name="atlas_authored_cases",
related_name="casecollection_authored_cases",
)
valid_cid_users = models.ManyToManyField(
CidUser, blank=True, related_name="casecollection_exams"
)
valid_user_users = models.ManyToManyField(
settings.AUTH_USER_MODEL, blank=True, related_name="user_casecollection_exams"
)
cid_user_groups = models.ManyToManyField(
CidUserGroup,
blank=True,
help_text="These groups define which candidates are able to be added to the exams/collection.",
related_name="casecollection_cid_user_groups",
)
user_user_groups = models.ManyToManyField(
UserUserGroup,
blank=True,
help_text="These groups define which candidates are able to be added to the exams/collection.",
related_name="casecollection_user_user_groups",
)
archive = models.BooleanField(default=False)
exam_mode = models.BooleanField(default=False)
# This should override the publish setting
@@ -483,11 +485,6 @@ class CaseCollection(ExamCollectionGenericBase):
help_text="If true allows users self complete and review cases in a self directed way.",
)
open_access = models.BooleanField(
help_text="If the exam is freely accessible (to view and edit on the test system)",
default=False,
)
class COLLECTION_TYPE_CHOICES(models.TextChoices):
REVIEW = (
"REV",
@@ -525,30 +522,6 @@ class CaseCollection(ExamCollectionGenericBase):
return False
def check_user_can_take(self, cid, passcode, request):
"""
Helper to check if a user is allowed to access a collection
Args:
cid (_type_): _description_
passcode (_type_): _description_
request (_type_): _description_
Raises:
Http404: If user does not have access
"""
if not self.active:
raise Http404("Exam not found")
#if self.collection_is_review():
# return True
#if not self.self_review:
if not self.check_cid_user(cid, passcode, request):
raise Http404("Error accessing exam")
class CaseDetail(models.Model):
case = models.ForeignKey(Case, on_delete=models.CASCADE)
collection = models.ForeignKey(CaseCollection, on_delete=models.CASCADE)
+2 -2
View File
@@ -273,7 +273,7 @@ class SubspecialtyTable(tables.Table):
class CaseCollectionTable(tables.Table):
edit = tables.LinkColumn(
"atlas:collection_update", text="Edit", args=[A("pk")], orderable=False
"atlas:exam_update", text="Edit", args=[A("pk")], orderable=False
)
view = tables.LinkColumn(
"atlas:collection_detail", text="View", args=[A("pk")], orderable=False
@@ -282,7 +282,7 @@ class CaseCollectionTable(tables.Table):
# "atlas:case_clone", text="Clone", args=[A("pk")], orderable=False
#)
delete = tables.LinkColumn(
"atlas:collection_delete", text="Delete", args=[A("pk")], orderable=False
"atlas:exam_deleted", text="Delete", args=[A("pk")], orderable=False
)
#differential = tables.ManyToManyColumn(verbose_name="Differential")
+3 -10
View File
@@ -4,16 +4,6 @@
Atlas
{% endblock %}
{% block css %}
{% endblock %}
{% block js %}
{% endblock %}
{% block content %}
{% endblock %}
{% block navigation %}
Atlas:
{% if request.user.is_authenticated %}
@@ -28,3 +18,6 @@
Questions by:
<span id="authors-link"><a href="{% url 'atlas:author_list' %}">author</a></span> {% endcomment %}
{% endblock %}
{% block content %}
{% endblock %}
+1 -1
View File
@@ -53,7 +53,7 @@
Use this form to create a atlas case. Existing associated image sets can be added using this form.
{% if form.collection %}
<div class="alert alert-info" role="alert">Creating a case in the collection: <a href="{% url 'atlas:collection_detail' pk=form.collection.pk %}">{{form.collection.name}}</a></div>
<div class="alert alert-info" role="alert">Creating a case in the collection: <a href="{% url 'atlas:collection_detail' pk=form.exam.pk %}">{{form.collection.name}}</a></div>
{% endif %}
<form action="" method="post" enctype="multipart/form-data" id="atlas-form">
{% csrf_token %}
@@ -9,7 +9,7 @@
{% if collection.publish_results or cid_user_exam.completed %}
review
<span class="stamp-white">REVIEW</span>
{% endif %}
@@ -6,8 +6,8 @@
<a href="{% url 'atlas:exam_cids' collection.pk %}">Candidates</a> /
<a href="{% url 'atlas:atlas_create_exam' pk=collection.pk %}">Add New Case</a>
<div class="floating-header">
<a href="{% url 'atlas:collection_update' collection.id %}" title="Edit the Collection">Edit</a>
\ <a href="{% url 'atlas:collection_delete' collection.id %}" title="Delete the Collection">Delete</a>
<a href="{% url 'atlas:exam_update' collection.id %}" title="Edit the Collection">Edit</a>
\ <a href="{% url 'atlas:exam_deleted' collection.id %}" title="Delete the Collection">Delete</a>
{% comment %} \ <a href="{% url 'atlas:collection_clone' collection.id %}" title="Clone the Collection">Clone</a> {% endcomment %}
{% if request.user.is_superuser %}
\ <a href="{% url 'admin:atlas_casecollection_change' collection.id %}" title="Edit the Collection using the admin interface">Admin Edit</a>
@@ -1,7 +1,7 @@
{% extends 'atlas/base.html' %}
{% block content %}
<a href="{% url 'atlas:collection_create' %}">Create collection</a>
<a href="{% url 'atlas:exam_create' %}">Create collection</a>
<ul>
{% for collection in collections %}
<li><a href='{{collection.get_absolute_url}}'>{{collection.name}}</a></li>
@@ -8,16 +8,15 @@
<h2>Collection: {{collection.name}}
{% if collection.publish_results or cid_user_exam.completed %}
review
<span class="stamp-white">REVIEW</span>
{% endif %}
</h2>
<div><p>Questions</p></div>
{{answer_count}} out of {{collection_length}} cases answered. Click to go to case.
<div class="sba-finish-list">
<ul>
{% for question, answer in question_answer_tuples %}
<li>
<li> <span {% if not answer %}class="unanswered"{% endif %}>
<a href="
{% if cid is not None %}
{% url 'atlas:collection_case_view_take' pk=collection.id case_number=forloop.counter0 cid=cid passcode=passcode %}
@@ -25,10 +24,15 @@
{% url 'atlas:collection_case_view_take_user' pk=collection.id case_number=forloop.counter0 %}
{% endif %}
">
<span {% if not answer %}class="unanswered"{% endif %}>Case: {{forloop.counter}}</span>
Case: {{forloop.counter}}
</a>
<br/>
{{answer.answer}}
{% if not answer %}
No answer
{% else %}
{{answer.answer}}
{% endif %}
</span>
</li>
{% endfor %}
</ul>
@@ -1,18 +1,26 @@
{% extends 'atlas/base.html' %}
{% block content %}
<h2>Start: {{collection.name}}</h2>
<h2>Start: {{collection.name}}{% if cid_exam.completed %} <span class="stamp-white">REVIEW</span>{% endif %}</h2>
{% if request.user.is_authenticated and valid_user %}
User: {{request.user}}<br/>
{% if cid_exam %}
Started: {{cid_exam.start_time}} <br/>
{% if cid_exam.completed %}
Ended: {{cid_exam.end_time}} <br/>
{% endif %}
{% endif %}
<a href="{% url 'atlas:collection_case_view_take_user' pk=collection.pk case_number=0 %}">
{% if cid_exam.completed %}
<button>Review</button>
{% else %}
<button>Start</button>
{% endif %}
</a>
{% else %}
Enter your CID and passcode in the below boxes.<br />
@@ -0,0 +1,17 @@
{% extends "atlas/exams.html" %}
{% block js %}
{{ form.media }}
{% endblock %}
{% block content %}
<h2>Update Exam</h2>
<form action="" method="post">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<input type="submit" value="Submit">
</form>
{% endblock %}
+2 -2
View File
@@ -9,8 +9,8 @@
<a href="{% url 'atlas:collection_scores_cid' exam.pk %}">Scores</a> /
<a href="{% url 'atlas:exam_cids' exam.pk %}">Candidates</a>
<div class="floating-header">
<a href="{% url 'atlas:collection_update' exam.id %}" title="Edit the Collection">Edit</a>
\ <a href="{% url 'atlas:collection_delete' exam.id %}" title="Delete the Collection">Delete</a>
<a href="{% url 'atlas:exam_update' exam.id %}" title="Edit the Collection">Edit</a>
\ <a href="{% url 'atlas:exam_deleted' exam.id %}" title="Delete the Collection">Delete</a>
{% if request.user.is_superuser %}
\ <a href="{% url 'admin:atlas_casecollection_change' exam.id %}" title="Edit the Collection using the admin interface">Admin Edit</a>
{% endif %}
+18 -3
View File
@@ -21,17 +21,17 @@ urlpatterns = [
path(
"collection/create",
views.CaseCollectionCreate.as_view(),
name="collection_create",
name="exam_create",
),
path(
"collection/<int:pk>/delete",
views.CaseCollectionDelete.as_view(),
name="collection_delete",
name="exam_deleted",
),
path(
"collection/<int:pk>/update",
views.CaseCollectionUpdate.as_view(),
name="collection_update",
name="exam_update",
),
path("collection/<int:pk>", views.collection_detail, name="collection_detail"),
path(
@@ -44,6 +44,16 @@ urlpatterns = [
views.GenericExamViews.exam_cids,
name="exam_cids",
),
path(
"exam/<int:exam_id>/cids/edit",
views.GenericExamViews.exam_cids_edit,
name="exam_cids_edit",
),
path(
"exam/<int:exam_id>/users/edit",
views.GenericExamViews.exam_users_edit,
name="exam_users_edit",
),
path(
"collection/<int:exam_id>/cids/<int:cid>/delete_answers",
views.delete_collection_cid_answers,
@@ -59,6 +69,11 @@ urlpatterns = [
views.GenericExamViews.exam_cids_edit,
name="exam_cids_edit",
),
path(
"collection/<int:exam_id>/users/edit",
views.GenericExamViews.exam_users_edit,
name="exam_users_edit",
),
path(
"collection/<int:pk>/mark",
views.collection_mark_overview,
+3
View File
@@ -442,6 +442,7 @@ class SeriesCreate(RevisionMixin, LoginRequiredMixin, CreateView):
class CaseCollectionUpdate(RevisionMixin, AuthorOrCheckerRequiredMixin, UpdateView):
model = CaseCollection
form_class = CaseCollectionForm
template_name = "atlas/collection_update_form.html"
# fields = '__all__'
# #fields = [ 'condition' ]
@@ -467,6 +468,8 @@ class CaseCollectionUpdate(RevisionMixin, AuthorOrCheckerRequiredMixin, UpdateVi
context["case_formset"] = CaseCollectionCaseFormSet(
instance=self.object, form_kwargs={"user": self.request.user}
)
context["collection"] = context["casecollection"]
return context
def form_valid(self, form):