some refactoring and atlas improvements
This commit is contained in:
@@ -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
@@ -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
@@ -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")
|
||||
|
||||
@@ -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 %}
|
||||
@@ -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 %}
|
||||
@@ -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
@@ -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,
|
||||
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user