From 11f3836e539880aac00082301b2c80475e903621 Mon Sep 17 00:00:00 2001 From: Ross Date: Fri, 8 Apr 2022 23:14:32 +0100 Subject: [PATCH 01/22] . --- atlas/models.py | 2 +- .../templates/atlas/collection_case_view.html | 48 ++----- .../atlas/collection_case_view_take.html | 104 ++++++++++++++ atlas/views.py | 128 +++++++++++------- 4 files changed, 192 insertions(+), 90 deletions(-) create mode 100644 atlas/templates/atlas/collection_case_view_take.html diff --git a/atlas/models.py b/atlas/models.py index 311a061c..40d4b766 100644 --- a/atlas/models.py +++ b/atlas/models.py @@ -598,7 +598,7 @@ class CaseCollection(models.Model): cases = models.ManyToManyField(Case, through="CaseDetail") publish_results = models.BooleanField( - help_text="If a collection should published", default=True + help_text="If a collection should published", default=False ) active = models.BooleanField( diff --git a/atlas/templates/atlas/collection_case_view.html b/atlas/templates/atlas/collection_case_view.html index 1f9f58cc..2ef86e7e 100644 --- a/atlas/templates/atlas/collection_case_view.html +++ b/atlas/templates/atlas/collection_case_view.html @@ -59,45 +59,17 @@ {% endif %} - {% if take %} -
{% csrf_token %} -
- {% if collection.publish_results %}
{% endif %} - {{form}} - {% if collection.publish_results %}
{% endif %} -
- {% if previous > -1 %} - - {% endif %} - {% if next %} - - {% else %} - {% if not exam.publish_results %} - - {% endif %} - {% endif %} +
+ {% if previous %} + Previous + {% endif %} + {% if next %} + Next + {% endif %} +
+
+ Return to collection - {% if collection.publish_results %} - Answer score: {{answer.score}}
- Answer feedback: {{answer.feedback|safe}} - {% endif %} -
- - -
- {% else %} -
- {% if previous %} - Previous - {% endif %} - {% if next %} - Next - {% endif %} -
-
- Return to collection - - {% endif %} {% endblock %} {% block js %} diff --git a/atlas/templates/atlas/collection_case_view_take.html b/atlas/templates/atlas/collection_case_view_take.html new file mode 100644 index 00000000..bf054d2a --- /dev/null +++ b/atlas/templates/atlas/collection_case_view_take.html @@ -0,0 +1,104 @@ +{% extends 'atlas/base.html' %} + +{% block content %} +

Case {{case_number|add:1}} + + {% if collection.show_title %} + : {{case.title}} + {% endif %} + +

+ + {% if collection.show_description and case.description%} +
+ Description: {{case.description}} +
+ {% endif %} + {% if collection.show_history and case.history%} +
+ History: {{case.history}} +
+ {% endif %} + +
+ {% for series in series_list %} + + + Series {{ forloop.counter }}: + + {{series.get_block}} + + + + {% endfor %} +
+ +
+
+
+ +
+ {% if collection.show_discussion and case.discussion%} +
+ + Discussion: + +
+ {{case.discussion}} +
+
+ {% endif %} + {% if collection.show_report and case.report%} +
+ + Report: + +
+ {{case.report}} +
+
+ {% endif %} + +
{% csrf_token %} +
+ {% if collection.publish_results %}
{% endif %} + {{form}} + {% if collection.publish_results %}
{% endif %} +
+ {% if previous > -1 %} + + {% endif %} + {% if next %} + + {% else %} + {% if not exam.publish_results %} + + {% endif %} + {% endif %} + + {% if collection.publish_results %} + Answer score: {{answer.score}}
+ Answer feedback: {{answer.feedback|safe}} + {% endif %} +
+ + +
+ +{% endblock %} +{% block js %} + + +{% endblock js %} diff --git a/atlas/views.py b/atlas/views.py index fd56d8e2..2b671085 100755 --- a/atlas/views.py +++ b/atlas/views.py @@ -1275,13 +1275,6 @@ def collection_take_overview(request, pk, cid, passcode): def collection_case_view_take(request, pk, case_number, cid, passcode): - return collection_case_view_base(request, pk, case_number, True, cid, passcode) - -@user_is_collection_author_or_atlas_editor -def collection_case_view(request, pk, case_number, take=False, cid=None, passcode=None): - return collection_case_view_base(request, pk, case_number, take=False, cid=None, passcode=None) - -def collection_case_view_base(request, pk, case_number, take=False, cid=None, passcode=None): collection = get_object_or_404(CaseCollection, pk=pk) form = None @@ -1291,58 +1284,57 @@ def collection_case_view_base(request, pk, case_number, take=False, cid=None, pa if not collection.active: raise Http404("Exam not found") - if take and not collection.check_cid_user(cid, passcode, request): + if not collection.check_cid_user(cid, passcode, request): raise Http404("Error accessing exam") cases = collection.cases.all().order_by("casedetail__sort_order").prefetch_related() case = cases[case_number] - if take: - c = CidUser.objects.filter(cid=cid).first() - cid_user_exam = collection.get_or_create_cid_user_exam(cid_user=c) - case_detail = CaseDetail.objects.get(case=case, collection=collection) - answer = case_detail.cidreportanswer_set.filter(cid=cid).first() + c = CidUser.objects.filter(cid=cid).first() + cid_user_exam = collection.get_or_create_cid_user_exam(cid_user=c) + case_detail = CaseDetail.objects.get(case=case, collection=collection) + answer = case_detail.cidreportanswer_set.filter(cid=cid).first() - if request.method == "POST": - if not collection.publish_results: - if answer: - form = CidReportAnswerForm(request.POST, instance=answer) - else: - form = CidReportAnswerForm(request.POST) - if form.is_valid(): - answer = form.save(commit=False) - answer.cid = cid - answer.question = case_detail - # answer.published_date = timezone.now() - answer.save() + if request.method == "POST": + if not collection.publish_results: + if answer: + form = CidReportAnswerForm(request.POST, instance=answer) + else: + form = CidReportAnswerForm(request.POST) + if form.is_valid(): + answer = form.save(commit=False) + answer.cid = cid + answer.question = case_detail + # answer.published_date = timezone.now() + answer.save() - cid_user_exam.end_time = timezone.now() - cid_user_exam.save() + cid_user_exam.end_time = timezone.now() + cid_user_exam.save() - kwargs = {"pk": pk, "cid": cid, "passcode": passcode} + kwargs = {"pk": pk, "cid": cid, "passcode": passcode} - if "next" in request.POST: - return redirect( - "atlas:collection_case_view_take", - case_number=case_number + 1, - **kwargs - ) - elif "previous" in request.POST: - return redirect( - "atlas:collection_case_view_take", - case_number=case_number - 1, - **kwargs - ) - elif "finish" in request.POST: - return redirect("atlas:collection_take_overview", **kwargs) - elif "goto" in request.POST: - return redirect( - "atlas:collection_case_view_take", - case_number=int(request.POST.get("goto")), - **kwargs - ) - else: - form = CidReportAnswerForm(instance=answer) + if "next" in request.POST: + return redirect( + "atlas:collection_case_view_take", + case_number=case_number + 1, + **kwargs + ) + elif "previous" in request.POST: + return redirect( + "atlas:collection_case_view_take", + case_number=case_number - 1, + **kwargs + ) + elif "finish" in request.POST: + return redirect("atlas:collection_take_overview", **kwargs) + elif "goto" in request.POST: + return redirect( + "atlas:collection_case_view_take", + case_number=int(request.POST.get("goto")), + **kwargs + ) + else: + form = CidReportAnswerForm(instance=answer) series_list = case.series.all().prefetch_related("images", "examination", "plane") @@ -1360,9 +1352,43 @@ def collection_case_view_base(request, pk, case_number, take=False, cid=None, pa "case_number": case_number, "previous": previous, "next": next, - "take": take, "cid": cid, "passcode": passcode, "answer": answer, }, ) + +@user_is_collection_author_or_atlas_editor +def collection_case_view(request, pk, case_number): + collection = get_object_or_404(CaseCollection, pk=pk) + + form = None + answer = None + + if collection.collection_type != collection.COLLECTION_TYPE_CHOICES.REVIEW: + if not collection.active: + raise Http404("Exam not found") + + + cases = collection.cases.all().order_by("casedetail__sort_order").prefetch_related() + case = cases[case_number] + + series_list = case.series.all().prefetch_related("images", "examination", "plane") + + previous = case_number > 0 + next = case_number < (len(cases) - 1) + + return render( + request, + "atlas/collection_case_view.html", + { + "form": form, + "collection": collection, + "case": case, + "series_list": series_list, + "case_number": case_number, + "previous": previous, + "next": next, + "answer": answer, + }, + ) From e98fa204c016c01e5db9fdbe4687b22831e80a41 Mon Sep 17 00:00:00 2001 From: Ross Date: Fri, 8 Apr 2022 23:18:28 +0100 Subject: [PATCH 02/22] . --- atlas/templates/atlas/case_detail.html | 14 ++++++++++++++ atlas/views.py | 1 + 2 files changed, 15 insertions(+) diff --git a/atlas/templates/atlas/case_detail.html b/atlas/templates/atlas/case_detail.html index 1c170a17..da2a854f 100755 --- a/atlas/templates/atlas/case_detail.html +++ b/atlas/templates/atlas/case_detail.html @@ -16,6 +16,20 @@ Admin Edit {% endif %} + + {% if collection %} +
+ + {% if previous %} + Previous question + {% endif %} + Viewing question as part of collection: {{collection.name}} [{{pos}}/{{collection_length}}] + {% if next %} + Next question + {% endif %} +
+ {% endif %} + {% include 'atlas/case_display_block.html' %} {% endblock %} diff --git a/atlas/views.py b/atlas/views.py index 2b671085..d673aa1c 100755 --- a/atlas/views.py +++ b/atlas/views.py @@ -1384,6 +1384,7 @@ def collection_case_view(request, pk, case_number): { "form": form, "collection": collection, + "collection_length": len(collection), "case": case, "series_list": series_list, "case_number": case_number, From c44f0e4dfcededcb0808ba9b59d0d8b33252999f Mon Sep 17 00:00:00 2001 From: Ross Date: Fri, 8 Apr 2022 23:20:33 +0100 Subject: [PATCH 03/22] . --- atlas/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/atlas/views.py b/atlas/views.py index d673aa1c..a3c1828a 100755 --- a/atlas/views.py +++ b/atlas/views.py @@ -1384,7 +1384,7 @@ def collection_case_view(request, pk, case_number): { "form": form, "collection": collection, - "collection_length": len(collection), + "collection_length": len(cases), "case": case, "series_list": series_list, "case_number": case_number, From ccae69faab74bab08cf56ac79b0572097aeadd1b Mon Sep 17 00:00:00 2001 From: Ross Date: Fri, 8 Apr 2022 23:21:21 +0100 Subject: [PATCH 04/22] . --- atlas/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/atlas/views.py b/atlas/views.py index a3c1828a..a3de2d65 100755 --- a/atlas/views.py +++ b/atlas/views.py @@ -1380,7 +1380,7 @@ def collection_case_view(request, pk, case_number): return render( request, - "atlas/collection_case_view.html", + "atlas/case_detail.html", { "form": form, "collection": collection, From 5cd3f0aad2423af1226c38bad0426f1311b198b3 Mon Sep 17 00:00:00 2001 From: Ross Date: Fri, 8 Apr 2022 23:22:49 +0100 Subject: [PATCH 05/22] . --- atlas/templates/atlas/case_detail.html | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/atlas/templates/atlas/case_detail.html b/atlas/templates/atlas/case_detail.html index da2a854f..974b00fa 100755 --- a/atlas/templates/atlas/case_detail.html +++ b/atlas/templates/atlas/case_detail.html @@ -15,20 +15,21 @@ {% if request.user.is_superuser %} Admin Edit {% endif %} - - {% if collection %} -
+ {% if collection %} +
- {% if previous %} - Previous question - {% endif %} - Viewing question as part of collection: {{collection.name}} [{{pos}}/{{collection_length}}] - {% if next %} - Next question + {% if previous %} + Previous question + {% endif %} + Viewing question as part of collection: {{collection.name}} [{{case_number|add:1}}/{{collection_length}}] + {% if next %} + Next question + {% endif %} +
{% endif %}
- {% endif %} + {% include 'atlas/case_display_block.html' %} {% endblock %} From 957bf0ea359b900123806105ac222a5da1896f31 Mon Sep 17 00:00:00 2001 From: Ross Date: Fri, 8 Apr 2022 23:24:22 +0100 Subject: [PATCH 06/22] . --- atlas/templates/atlas/case_detail.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/atlas/templates/atlas/case_detail.html b/atlas/templates/atlas/case_detail.html index 974b00fa..e7713e0b 100755 --- a/atlas/templates/atlas/case_detail.html +++ b/atlas/templates/atlas/case_detail.html @@ -20,11 +20,11 @@
{% if previous %} - Previous question + Previous question {% endif %} Viewing question as part of collection: {{collection.name}} [{{case_number|add:1}}/{{collection_length}}] {% if next %} - Next question + Next question {% endif %}
{% endif %} From e5e76657329b17175c23edad55372c28f96db40d Mon Sep 17 00:00:00 2001 From: Ross Date: Fri, 8 Apr 2022 23:24:56 +0100 Subject: [PATCH 07/22] . --- atlas/templates/atlas/case_detail.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/atlas/templates/atlas/case_detail.html b/atlas/templates/atlas/case_detail.html index e7713e0b..fc17999c 100755 --- a/atlas/templates/atlas/case_detail.html +++ b/atlas/templates/atlas/case_detail.html @@ -20,11 +20,11 @@
{% if previous %} - Previous question + Previous question {% endif %} Viewing question as part of collection: {{collection.name}} [{{case_number|add:1}}/{{collection_length}}] {% if next %} - Next question + Next question {% endif %}
{% endif %} From 4b93a29c3d8991b71b5e5f8a7b8453ea0d344f88 Mon Sep 17 00:00:00 2001 From: Ross Date: Fri, 8 Apr 2022 23:28:20 +0100 Subject: [PATCH 08/22] . --- .../templates/atlas/collection_case_view.html | 90 ------------------- atlas/templates/atlas/collection_detail.html | 6 ++ 2 files changed, 6 insertions(+), 90 deletions(-) delete mode 100644 atlas/templates/atlas/collection_case_view.html diff --git a/atlas/templates/atlas/collection_case_view.html b/atlas/templates/atlas/collection_case_view.html deleted file mode 100644 index 2ef86e7e..00000000 --- a/atlas/templates/atlas/collection_case_view.html +++ /dev/null @@ -1,90 +0,0 @@ -{% extends 'atlas/base.html' %} - -{% block content %} -

Case {{case_number|add:1}} - - {% if collection.show_title %} - : {{case.title}} - {% endif %} - -

- - {% if collection.show_description and case.description%} -
- Description: {{case.description}} -
- {% endif %} - {% if collection.show_history and case.history%} -
- History: {{case.history}} -
- {% endif %} - -
- {% for series in series_list %} - - - Series {{ forloop.counter }}: - - {{series.get_block}} - - - - {% endfor %} -
- -
-
-
- -
- {% if collection.show_discussion and case.discussion%} -
- - Discussion: - -
- {{case.discussion}} -
-
- {% endif %} - {% if collection.show_report and case.report%} -
- - Report: - -
- {{case.report}} -
-
- {% endif %} - -
- {% if previous %} - Previous - {% endif %} - {% if next %} - Next - {% endif %} -
-
- Return to collection - - -{% endblock %} -{% block js %} - - -{% endblock js %} diff --git a/atlas/templates/atlas/collection_detail.html b/atlas/templates/atlas/collection_detail.html index 5fe25d02..940b5ca0 100644 --- a/atlas/templates/atlas/collection_detail.html +++ b/atlas/templates/atlas/collection_detail.html @@ -2,6 +2,12 @@ {% block content %} {% include 'atlas/collection_headers.html' %} +
+ Exam mode: {{collection.exam_mode}} + Publish results: {{collection.publish_results}} + Active: {{collection.active}} + Collection Type: {{collection.collection_type}} +
    {% for case in collection.cases.all %}
  • Case {{forloop.counter}} From e95bf2a448514b532d0e97829b0477c8cf9e1460 Mon Sep 17 00:00:00 2001 From: Ross Date: Fri, 8 Apr 2022 23:30:21 +0100 Subject: [PATCH 09/22] . --- atlas/templates/atlas/collection_detail.html | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/atlas/templates/atlas/collection_detail.html b/atlas/templates/atlas/collection_detail.html index 940b5ca0..a6740be7 100644 --- a/atlas/templates/atlas/collection_detail.html +++ b/atlas/templates/atlas/collection_detail.html @@ -3,10 +3,10 @@ {% block content %} {% include 'atlas/collection_headers.html' %}
    - Exam mode: {{collection.exam_mode}} - Publish results: {{collection.publish_results}} - Active: {{collection.active}} - Collection Type: {{collection.collection_type}} + Exam mode: {{collection.exam_mode}}
    + Publish results: {{collection.publish_results}}
    + Active: {{collection.active}}
    + Collection Type: {{collection.collection_type}}
      {% for case in collection.cases.all %} @@ -17,4 +17,6 @@ {% endfor %}
    +

    This collection will be available to view here + {% endblock %} From 32ac02f74985d50aa67e652a9f28cf2e2c3ac503 Mon Sep 17 00:00:00 2001 From: Ross Date: Fri, 8 Apr 2022 23:31:43 +0100 Subject: [PATCH 10/22] . --- atlas/templates/atlas/collection_detail.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/atlas/templates/atlas/collection_detail.html b/atlas/templates/atlas/collection_detail.html index a6740be7..d8b24790 100644 --- a/atlas/templates/atlas/collection_detail.html +++ b/atlas/templates/atlas/collection_detail.html @@ -17,6 +17,6 @@ {% endfor %}

-

This collection will be available to view here +

This collection will be available to view here {% endblock %} From d28557ac5cf3a8008df010a546b09c66ede52f00 Mon Sep 17 00:00:00 2001 From: Ross Date: Fri, 8 Apr 2022 23:33:42 +0100 Subject: [PATCH 11/22] . --- atlas/templates/atlas/collection_detail.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/atlas/templates/atlas/collection_detail.html b/atlas/templates/atlas/collection_detail.html index d8b24790..ff688cd5 100644 --- a/atlas/templates/atlas/collection_detail.html +++ b/atlas/templates/atlas/collection_detail.html @@ -17,6 +17,6 @@ {% endfor %} -

This collection will be available to view here +

This collection will be available to view here {% endblock %} From 1b8b6b78d3bd58c141b91474f50f43584c281da3 Mon Sep 17 00:00:00 2001 From: Ross Date: Fri, 8 Apr 2022 23:34:37 +0100 Subject: [PATCH 12/22] . --- atlas/templates/atlas/collection_detail.html | 1 + 1 file changed, 1 insertion(+) diff --git a/atlas/templates/atlas/collection_detail.html b/atlas/templates/atlas/collection_detail.html index ff688cd5..e35fb88d 100644 --- a/atlas/templates/atlas/collection_detail.html +++ b/atlas/templates/atlas/collection_detail.html @@ -8,6 +8,7 @@ Active: {{collection.active}}
Collection Type: {{collection.collection_type}}
+

Cases

    {% for case in collection.cases.all %}
  • Case {{forloop.counter}} From 0b12d9aee49a619df7148be34e0300395fc1f403 Mon Sep 17 00:00:00 2001 From: Ross Date: Fri, 8 Apr 2022 23:37:14 +0100 Subject: [PATCH 13/22] . --- atlas/templates/atlas/case_display_block.html | 6 ++++++ atlas/templates/atlas/collection_detail.html | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/atlas/templates/atlas/case_display_block.html b/atlas/templates/atlas/case_display_block.html index c09c2123..fe8c02be 100755 --- a/atlas/templates/atlas/case_display_block.html +++ b/atlas/templates/atlas/case_display_block.html @@ -110,6 +110,12 @@
  • {{p.get_link}}
  • {% endfor %}
+ Collections: +
    + {% for p in case.casecollection_set.all %} +
  • {{p.name}}
  • + {% endfor %} +

Previous case: {{ case.previous_case }}

Next case: {{ case.next_case }}

diff --git a/atlas/templates/atlas/collection_detail.html b/atlas/templates/atlas/collection_detail.html index e35fb88d..403edfba 100644 --- a/atlas/templates/atlas/collection_detail.html +++ b/atlas/templates/atlas/collection_detail.html @@ -18,6 +18,6 @@ {% endfor %} -

This collection will be available to view here +

This collection will be available to view/take here {% endblock %} From 6efb72ddeca7030bb544fada2ce35a34bf811b97 Mon Sep 17 00:00:00 2001 From: Ross Date: Fri, 8 Apr 2022 23:51:09 +0100 Subject: [PATCH 14/22] . --- atlas/forms.py | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/atlas/forms.py b/atlas/forms.py index 264cf824..a18ebb6f 100755 --- a/atlas/forms.py +++ b/atlas/forms.py @@ -337,15 +337,6 @@ CaseDifferentialFormSet = inlineformset_factory( ) -class BaseSeriesFormSet(BaseInlineFormSet): - def __init__(self, *args, **kwargs): - - super(BaseSeriesFormSet, self).__init__(*args, **kwargs) - - self.queryset = Series.case.through.objects.filter(series__author__id=99) - - # for form in self.forms: - # form.fields['series'].queryset = series_queryset class CaseSeriesForm(ModelForm): @@ -361,10 +352,15 @@ class CaseSeriesForm(ModelForm): class CaseCollectionCaseForm(ModelForm): def __init__(self, *args, user, **kwargs): super(CaseCollectionCaseForm, self).__init__(*args, **kwargs) + + if not user.groups.filter(name="atlas_editor").exists(): + queryset = Case.objects.filter(author__id=user.id) + else: + queryset = Case.objects.all() self.fields["case"] = ModelChoiceField( required=False, - queryset=Case.objects.filter(author__id=user.id), + queryset=queryset, # widget=Select(verbose_name="Series", is_stacked=False), ) @@ -372,7 +368,6 @@ SeriesFormSet = inlineformset_factory( Case, Series.case.through, form=CaseSeriesForm, - # formset=BaseSeriesFormSet, exclude=[], can_delete=True, extra=0, @@ -383,7 +378,6 @@ CaseCollectionCaseFormSet = inlineformset_factory( CaseCollection, Case.casecollection_set.through, form=CaseCollectionCaseForm, - # formset=BaseSeriesFormSet, exclude=[], can_delete=True, extra=0, From 1b6cb4f63a71b6d9d019dad94c5efb4794f98da3 Mon Sep 17 00:00:00 2001 From: Ross Date: Fri, 8 Apr 2022 23:57:24 +0100 Subject: [PATCH 15/22] . --- atlas/migrations/0038_auto_20220408_2355.py | 43 +++++++++++++ atlas/migrations/0039_auto_20220408_2357.py | 63 +++++++++++++++++++ atlas/models.py | 41 ++++++++---- .../atlas/collection_case_view_take.html | 10 +-- requirements.txt | 3 +- 5 files changed, 142 insertions(+), 18 deletions(-) create mode 100644 atlas/migrations/0038_auto_20220408_2355.py create mode 100644 atlas/migrations/0039_auto_20220408_2357.py diff --git a/atlas/migrations/0038_auto_20220408_2355.py b/atlas/migrations/0038_auto_20220408_2355.py new file mode 100644 index 00000000..4ce699e5 --- /dev/null +++ b/atlas/migrations/0038_auto_20220408_2355.py @@ -0,0 +1,43 @@ +# Generated by Django 3.2.4 on 2022-04-08 22:55 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('atlas', '0037_cidreportanswer_feedback'), + ] + + operations = [ + migrations.RenameField( + model_name='casecollection', + old_name='show_description', + new_name='show_description_pre', + ), + migrations.RenameField( + model_name='casecollection', + old_name='show_discussion', + new_name='show_discussion_pre', + ), + migrations.RenameField( + model_name='casecollection', + old_name='show_history', + new_name='show_history_pre', + ), + migrations.RenameField( + model_name='casecollection', + old_name='show_report', + new_name='show_report_pre', + ), + migrations.RenameField( + model_name='casecollection', + old_name='show_title', + new_name='show_title_pre', + ), + migrations.AlterField( + model_name='casecollection', + name='publish_results', + field=models.BooleanField(default=False, help_text='If a collection should published'), + ), + ] diff --git a/atlas/migrations/0039_auto_20220408_2357.py b/atlas/migrations/0039_auto_20220408_2357.py new file mode 100644 index 00000000..2940c1ee --- /dev/null +++ b/atlas/migrations/0039_auto_20220408_2357.py @@ -0,0 +1,63 @@ +# Generated by Django 3.2.4 on 2022-04-08 22:57 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('atlas', '0038_auto_20220408_2355'), + ] + + operations = [ + migrations.AddField( + model_name='casecollection', + name='show_description_post', + field=models.BooleanField(default=False, help_text='Show the description of the cases (post exam)'), + ), + migrations.AddField( + model_name='casecollection', + name='show_discussion_post', + field=models.BooleanField(default=False, help_text='Show the case discussion (post exam)'), + ), + migrations.AddField( + model_name='casecollection', + name='show_history_post', + field=models.BooleanField(default=False, help_text='Show the history of the cases (post exam)'), + ), + migrations.AddField( + model_name='casecollection', + name='show_report_post', + field=models.BooleanField(default=False, help_text='Show the case report (post exam)'), + ), + migrations.AddField( + model_name='casecollection', + name='show_title_post', + field=models.BooleanField(default=False, help_text='Show the title of the cases (post exam)'), + ), + migrations.AlterField( + model_name='casecollection', + name='show_description_pre', + field=models.BooleanField(default=False, help_text='Show the description of the cases (pre exam)'), + ), + migrations.AlterField( + model_name='casecollection', + name='show_discussion_pre', + field=models.BooleanField(default=False, help_text='Show the case discussion (pre exam)'), + ), + migrations.AlterField( + model_name='casecollection', + name='show_history_pre', + field=models.BooleanField(default=False, help_text='Show the history of the cases (pre exam)'), + ), + migrations.AlterField( + model_name='casecollection', + name='show_report_pre', + field=models.BooleanField(default=False, help_text='Show the case report (pre exam)'), + ), + migrations.AlterField( + model_name='casecollection', + name='show_title_pre', + field=models.BooleanField(default=False, help_text='Show the title of the cases (pre exam)'), + ), + ] diff --git a/atlas/models.py b/atlas/models.py index 40d4b766..8b9c8f6a 100644 --- a/atlas/models.py +++ b/atlas/models.py @@ -11,7 +11,6 @@ import tagulous import tagulous.models - import pydicom import dicognito.anonymizer @@ -591,7 +590,6 @@ class Series(models.Model): pass - class CaseCollection(models.Model): name = models.CharField(max_length=255, unique=True) @@ -605,19 +603,37 @@ class CaseCollection(models.Model): help_text="If a collection should be available", default=True ) - show_title = models.BooleanField( - default=False, help_text="Show the title of the cases" + show_title_pre = models.BooleanField( + default=False, help_text="Show the title of the cases (pre exam)" ) - show_history = models.BooleanField( - default=False, help_text="Show the history of the cases" + show_history_pre = models.BooleanField( + default=False, help_text="Show the history of the cases (pre exam)" ) - show_description = models.BooleanField( - default=False, help_text="Show the description of the cases" + show_description_pre = models.BooleanField( + default=False, help_text="Show the description of the cases (pre exam)" ) - show_discussion = models.BooleanField( - default=False, help_text="Show the case discussion" + show_discussion_pre = models.BooleanField( + default=False, help_text="Show the case discussion (pre exam)" + ) + show_report_pre = models.BooleanField( + default=False, help_text="Show the case report (pre exam)" + ) + + show_title_post = models.BooleanField( + default=False, help_text="Show the title of the cases (post exam)" + ) + show_history_post = models.BooleanField( + default=False, help_text="Show the history of the cases (post exam)" + ) + show_description_post = models.BooleanField( + default=False, help_text="Show the description of the cases (post exam)" + ) + show_discussion_post = models.BooleanField( + default=False, help_text="Show the case discussion (post exam)" + ) + show_report_post = models.BooleanField( + default=False, help_text="Show the case report (post exam)" ) - show_report = models.BooleanField(default=False, help_text="Show the case report") author = models.ManyToManyField( settings.AUTH_USER_MODEL, @@ -709,6 +725,7 @@ class CaseCollection(models.Model): content_type=content_type, object_id=self.pk, cid_user=cid_user ) + class CaseDetail(models.Model): case = models.ForeignKey(Case, on_delete=models.CASCADE) collection = models.ForeignKey(CaseCollection, on_delete=models.CASCADE) @@ -720,7 +737,7 @@ class CaseDetail(models.Model): class CidReportAnswer(models.Model): - question = models.ForeignKey(CaseDetail, on_delete=models.CASCADE ) + question = models.ForeignKey(CaseDetail, on_delete=models.CASCADE) answer = models.TextField(blank=True) diff --git a/atlas/templates/atlas/collection_case_view_take.html b/atlas/templates/atlas/collection_case_view_take.html index bf054d2a..afaae560 100644 --- a/atlas/templates/atlas/collection_case_view_take.html +++ b/atlas/templates/atlas/collection_case_view_take.html @@ -3,18 +3,18 @@ {% block content %}

Case {{case_number|add:1}} - {% if collection.show_title %} + {% if collection.show_title_pre %} : {{case.title}} {% endif %}

- {% if collection.show_description and case.description%} + {% if collection.show_description_pre and case.description%}
Description: {{case.description}}
{% endif %} - {% if collection.show_history and case.history%} + {% if collection.show_history_pre and case.history%}
History: {{case.history}}
@@ -38,7 +38,7 @@ - {% if collection.show_discussion and case.discussion%} + {% if collection.show_discussion_pre and case.discussion%}
Discussion: @@ -48,7 +48,7 @@
{% endif %} - {% if collection.show_report and case.report%} + {% if collection.show_report_pre and case.report%}
Report: diff --git a/requirements.txt b/requirements.txt index 86d0fcd6..e84314d9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -23,4 +23,5 @@ django-zipview pymemcache django-autocomplete-light django-querysetsequence -dicognito \ No newline at end of file +dicognito +django_unused_media \ No newline at end of file From cee45774d0893a25d336b9388f9ec6f84585ad06 Mon Sep 17 00:00:00 2001 From: Ross Date: Sat, 9 Apr 2022 00:07:41 +0100 Subject: [PATCH 16/22] . --- .../atlas/collection_case_view_take.html | 8 +-- atlas/views.py | 51 +++++++++++-------- 2 files changed, 35 insertions(+), 24 deletions(-) diff --git a/atlas/templates/atlas/collection_case_view_take.html b/atlas/templates/atlas/collection_case_view_take.html index afaae560..e2051593 100644 --- a/atlas/templates/atlas/collection_case_view_take.html +++ b/atlas/templates/atlas/collection_case_view_take.html @@ -3,13 +3,13 @@ {% block content %}

Case {{case_number|add:1}} - {% if collection.show_title_pre %} + {% if show_title %} : {{case.title}} {% endif %}

- {% if collection.show_description_pre and case.description%} + {% if show_description and case.description %}
Description: {{case.description}}
@@ -38,7 +38,7 @@ - {% if collection.show_discussion_pre and case.discussion%} + {% if show_discussion and case.discussion%}
Discussion: @@ -48,7 +48,7 @@
{% endif %} - {% if collection.show_report_pre and case.report%} + {% if show_report and case.report%}
Report: diff --git a/atlas/views.py b/atlas/views.py index a3de2d65..0f70c615 100755 --- a/atlas/views.py +++ b/atlas/views.py @@ -873,6 +873,7 @@ def series_order_dicom(request, pk): return redirect("atlas:series_detail", pk=pk) + @login_required @user_is_author_or_atlas_series_checker def series_anonymise_dicom(request, pk): @@ -1138,13 +1139,15 @@ def collection_mark_question(request, pk, case_number): if request.method == "POST": for answer in answers: if answer: - form = CidReportAnswerMarkForm(request.POST, instance=answer, prefix=answer.id) + form = CidReportAnswerMarkForm( + request.POST, instance=answer, prefix=answer.id + ) else: form = CidReportAnswerMarkForm(request.POST, prefix=answer.id) if form.is_valid(): answer = form.save(commit=False) - #answer.cid = cid - #answer.question = case_detail + # answer.cid = cid + # answer.question = case_detail # answer.published_date = timezone.now() answer.save() @@ -1152,21 +1155,15 @@ def collection_mark_question(request, pk, case_number): if "save" in request.POST: return redirect( - "atlas:collection_mark_question", - case_number=case_number, - **kwargs + "atlas:collection_mark_question", case_number=case_number, **kwargs ) if "next" in request.POST: return redirect( - "atlas:collection_mark_question", - case_number=case_number + 1, - **kwargs + "atlas:collection_mark_question", case_number=case_number + 1, **kwargs ) elif "previous" in request.POST: return redirect( - "atlas:collection_mark_question", - case_number=case_number - 1, - **kwargs + "atlas:collection_mark_question", case_number=case_number - 1, **kwargs ) elif "goto" in request.POST: return redirect( @@ -1193,7 +1190,7 @@ def collection_mark_question(request, pk, case_number): "case_number": case_number, "answer_forms": answer_forms, "previous": previous, - "next": next + "next": next, }, ) @@ -1315,15 +1312,11 @@ def collection_case_view_take(request, pk, case_number, cid, passcode): if "next" in request.POST: return redirect( - "atlas:collection_case_view_take", - case_number=case_number + 1, - **kwargs + "atlas:collection_case_view_take", case_number=case_number + 1, **kwargs ) elif "previous" in request.POST: return redirect( - "atlas:collection_case_view_take", - case_number=case_number - 1, - **kwargs + "atlas:collection_case_view_take", case_number=case_number - 1, **kwargs ) elif "finish" in request.POST: return redirect("atlas:collection_take_overview", **kwargs) @@ -1341,6 +1334,19 @@ def collection_case_view_take(request, pk, case_number, cid, passcode): previous = case_number > 0 next = case_number < (len(cases) - 1) + if collection.publish_results: + show_title = collection.show_title_post + show_history = collection.show_history_post + show_description = collection.show_description_post + show_discussion = collection.show_discussion_post + show_report = collection.show_report_post + else: + show_title = collection.show_title_pre + show_history = collection.show_history_pre + show_description = collection.show_description_pre + show_discussion = collection.show_discussion_pre + show_report = collection.show_report_pre + return render( request, "atlas/collection_case_view.html", @@ -1355,9 +1361,15 @@ def collection_case_view_take(request, pk, case_number, cid, passcode): "cid": cid, "passcode": passcode, "answer": answer, + "show_title": show_title, + "show_history": show_history, + "show_description": show_description, + "show_discussion": show_discussion, + "show_report": show_report, }, ) + @user_is_collection_author_or_atlas_editor def collection_case_view(request, pk, case_number): collection = get_object_or_404(CaseCollection, pk=pk) @@ -1369,7 +1381,6 @@ def collection_case_view(request, pk, case_number): if not collection.active: raise Http404("Exam not found") - cases = collection.cases.all().order_by("casedetail__sort_order").prefetch_related() case = cases[case_number] From 88092d68462de1ccfae080631b24d32093934937 Mon Sep 17 00:00:00 2001 From: Ross Date: Sat, 9 Apr 2022 00:13:50 +0100 Subject: [PATCH 17/22] . --- atlas/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/atlas/views.py b/atlas/views.py index 0f70c615..0d98cf39 100755 --- a/atlas/views.py +++ b/atlas/views.py @@ -1349,7 +1349,7 @@ def collection_case_view_take(request, pk, case_number, cid, passcode): return render( request, - "atlas/collection_case_view.html", + "atlas/collection_case_view_take.html", { "form": form, "collection": collection, From 57f88af28f4884dfe6349f1705267a77fc46fecd Mon Sep 17 00:00:00 2001 From: Ross Date: Sat, 9 Apr 2022 00:28:09 +0100 Subject: [PATCH 18/22] . --- atlas/models.py | 3 + atlas/templates/atlas/collection_scores.html | 80 ++++++++++ atlas/views.py | 154 +++++++++++++++++-- 3 files changed, 228 insertions(+), 9 deletions(-) create mode 100644 atlas/templates/atlas/collection_scores.html diff --git a/atlas/models.py b/atlas/models.py index 8b9c8f6a..3b632d59 100644 --- a/atlas/models.py +++ b/atlas/models.py @@ -762,3 +762,6 @@ class CidReportAnswer(models.Model): def clean(self): if self.answer: self.answer = self.answer.strip() + + def get_answer_score(self): + return self.score \ No newline at end of file diff --git a/atlas/templates/atlas/collection_scores.html b/atlas/templates/atlas/collection_scores.html new file mode 100644 index 00000000..b41dc8fa --- /dev/null +++ b/atlas/templates/atlas/collection_scores.html @@ -0,0 +1,80 @@ +{% extends 'atlas/base.html' %} + +{% block content %} +
{{plot|safe}}
+
+

{{ exam.name }}

+ + {% if unmarked %} + + + {% endif %} + +
+
+

Stats

+ Candidates: {{cids|length}}
+ Max score: {{max_score}}
+ Mean: {{mean}}, Median {{median}}, Mode {{mode}} + +
+
+ + + + + {% comment %} {% endcomment %} + + {% comment %} {% for user, value in user_answers_marks.items %} {% endcomment %} + {% for cid in cids %} + + + + {% comment %} {% endcomment %} + + {% endfor %} +
Candidate IDScoreNormalised Score
{{cid}}{{user_scores|get_item:cid}}{{user_scores_normalised|get_item:user}}
+
+
+

Answers as a table

+ + + + + {% for cid in cids %} + + {% comment %} {% endcomment %} + {% endfor %} + + + + {% for question in questions %} + + + {% comment %} {% for cid in cids %} + + {% endfor %} {% endcomment %} + {% for cid in cids %} + {% with by_question|get_item:question|get_item:cid as ans_score %} + + {% endwith %} + {% endfor %} + + {% endfor %} + + + {% for cid in cids %} + + {% endfor %} + +
Candidate{{cid}}{{cid}}
Question {{forloop.counter}} + {{ans_by_question|get_item:question|get_item:cid}}{{ans_score.0}}
Score:{{user_scores|get_item:cid}}
+ +
+{% endblock %} + diff --git a/atlas/views.py b/atlas/views.py index 0d98cf39..59a55411 100755 --- a/atlas/views.py +++ b/atlas/views.py @@ -1169,7 +1169,7 @@ def collection_mark_question(request, pk, case_number): return redirect( "atlas:collection_mark_question", case_number=int(request.POST.get("goto")), - **kwargs + **kwargs, ) else: for answer in answers: @@ -1195,13 +1195,6 @@ def collection_mark_question(request, pk, case_number): ) -@user_is_collection_author_or_atlas_editor -def collection_scores_cid(request, pk): - collection = get_object_or_404(CaseCollection, pk=pk) - - return render(request, "atlas/collection_take.html", {"collection": collection}) - - @user_is_collection_author_or_atlas_editor def collection_candidates(request, pk): collection = get_object_or_404(CaseCollection, pk=pk) @@ -1324,7 +1317,7 @@ def collection_case_view_take(request, pk, case_number, cid, passcode): return redirect( "atlas:collection_case_view_take", case_number=int(request.POST.get("goto")), - **kwargs + **kwargs, ) else: form = CidReportAnswerForm(instance=answer) @@ -1404,3 +1397,146 @@ def collection_case_view(request, pk, case_number): "answer": answer, }, ) + + +@user_is_collection_author_or_atlas_editor +def collection_scores_cid(request, pk): + collection = get_object_or_404(CaseCollection, pk=pk) + + if not collection.exam_mode: + raise Http404("Collection not in exam mode") + + user_answers_and_marks = defaultdict(list) + user_answers_marks = defaultdict(list) + user_answers = defaultdict(list) + user_names = {} + # cid_passcodes = {} + + by_question = defaultdict(dict) + unmarked = set() + + cases = collection.cases.all().order_by("casedetail__sort_order").prefetch_related() + + case_details = CaseDetail.objects.filter( + case__in=cases, collection=collection + ).prefetch_related("case") + + cid_user_answers = CidReportAnswer.objects.filter(question__in=case_details) + + # questions = exam.exam_questions.all().prefetch_related("answers") + + # cid_user_answers = CidReportAnswer.objects.select_related("question").filter( + # question__in=questions, exam__id=pk + # ) + + cids = set() + + # Loop through all candidates + for cid_user_answer in cid_user_answers: + # Convoluted (probably...) + cid = cid_user_answer.cid + # cid_passcodes[cid] = cid_user_answer.passcode + cids.add(cid) + s = cid_user_answer + user_names[cid] = cid + + q = cid_user_answer.question + + # if not s: + # # skip if no answer + # user_answers_marks[cid].append(0) + # user_answers[cid].append("") + # by_question[q].append(("", 0)) + # continue + + ans = s.answer + + answer_score = s.get_answer_score() + if answer_score == "unmarked": + index = cases.index(q) + unmarked.add(index) + user_answers[cid].append(ans) + user_answers_marks[cid].append(answer_score) + user_answers_and_marks[cid].append((ans, answer_score)) + + by_question[q][cid] = (ans, answer_score) + + user_scores = {} + user_scores_normalised = {} + for user in user_answers_marks: + + user_scores[user] = sum( + [i for i in user_answers_marks[user] if i != "unmarked"] + ) + + user_scores_normalised[user] = user_scores[user] + + # ignore scores of 0 for stats + user_scores_list = [i for i in user_scores.values() if i > 0] + + max_score = len(cases) + + if len(user_scores_list) < 1: + mean = 0 + median = 0 + mode = 0 + fig_html = "" + else: + mean = statistics.mean(user_scores_list) + median = statistics.median(user_scores_list) + try: + mode = statistics.mode(user_scores_list) + except statistics.StatisticsError: + mode = "No unique mode" + + df = user_scores_list + fig = px.histogram( + df, + x=0, + title="{}: distribution of scores".format(collection), + labels={"0": "Score"}, + height=400, + width=600, + ) + fig_html = fig.to_html() + + collection.stats_mean = mean + collection.stats_median = median + collection.stats_mode = mode + + collection.stats_candidates = len(user_scores_list) + collection.stats_max_possible = max_score + + collection.stats_min = min(user_scores_list) + collection.stats_max = max(user_scores_list) + + collection.stats_graph = fig_html + + collection.user_scores = user_scores + + collection.save() + + return render( + request, + f"atlas/collection_scores.html", + { + "cids": sorted(cids), + # "cid_passcodes": cid_passcodes, + "exam": collection, + "unmarked": unmarked, + "questions": cases, + "by_question": by_question, + "user_answers": dict(user_answers), + "user_answers_marks": dict(user_answers_marks), + "user_scores": user_scores, + "user_scores_normalised": user_scores_normalised, + "user_scores_list": user_scores_list, + "user_names": user_names, + "user_answers_and_marks": user_answers_and_marks, + "max_score": max_score, + "mean": mean, + "median": median, + "mode": mode, + "plot": fig_html, + }, + ) From 3f5ef753d5dd0001948d890cbd7fe5f3a44435fe Mon Sep 17 00:00:00 2001 From: Ross Date: Sat, 9 Apr 2022 00:29:28 +0100 Subject: [PATCH 19/22] . --- atlas/templates/atlas/collection_scores.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/atlas/templates/atlas/collection_scores.html b/atlas/templates/atlas/collection_scores.html index b41dc8fa..07f2b275 100644 --- a/atlas/templates/atlas/collection_scores.html +++ b/atlas/templates/atlas/collection_scores.html @@ -9,7 +9,7 @@ From b35914de885ca41bfed5eb8fd28d86c6a9a4b374 Mon Sep 17 00:00:00 2001 From: Ross Date: Sat, 9 Apr 2022 00:30:26 +0100 Subject: [PATCH 20/22] . --- atlas/templates/atlas/collection_scores.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/atlas/templates/atlas/collection_scores.html b/atlas/templates/atlas/collection_scores.html index 07f2b275..fed6ed98 100644 --- a/atlas/templates/atlas/collection_scores.html +++ b/atlas/templates/atlas/collection_scores.html @@ -9,7 +9,7 @@ @@ -55,7 +55,7 @@ {% for question in questions %} - Question {{forloop.counter}} + Question {{forloop.counter}} {% comment %} {% for cid in cids %} {{ans_by_question|get_item:question|get_item:cid}} From 7594c2a1bc1c4c40bb36e9592e16198f7f46b581 Mon Sep 17 00:00:00 2001 From: Ross Date: Sat, 9 Apr 2022 00:31:10 +0100 Subject: [PATCH 21/22] . --- atlas/templates/atlas/collection_scores.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/atlas/templates/atlas/collection_scores.html b/atlas/templates/atlas/collection_scores.html index fed6ed98..f79051a0 100644 --- a/atlas/templates/atlas/collection_scores.html +++ b/atlas/templates/atlas/collection_scores.html @@ -9,7 +9,7 @@ @@ -55,7 +55,7 @@ {% for question in questions %} - Question {{forloop.counter}} + Question {{forloop.counter}} {% comment %} {% for cid in cids %} {{ans_by_question|get_item:question|get_item:cid}} From 3e130864f89086fc86d815acf5ccc13c62d968ec Mon Sep 17 00:00:00 2001 From: Ross Date: Sat, 9 Apr 2022 00:32:15 +0100 Subject: [PATCH 22/22] . --- atlas/templates/atlas/collection_scores.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/atlas/templates/atlas/collection_scores.html b/atlas/templates/atlas/collection_scores.html index f79051a0..6fefdb1f 100644 --- a/atlas/templates/atlas/collection_scores.html +++ b/atlas/templates/atlas/collection_scores.html @@ -47,7 +47,8 @@ Candidate {% for cid in cids %} - {{cid}} + {{cid}} + {% comment %} {{cid}} {% endcomment %} {% comment %} {{cid}} {% endcomment %} {% endfor %}