.
This commit is contained in:
+19
-14
@@ -17,18 +17,21 @@ from atlas.models import (
|
|||||||
from anatomy.models import Modality
|
from anatomy.models import Modality
|
||||||
|
|
||||||
from generic.models import Examination, Condition, Sign
|
from generic.models import Examination, Condition, Sign
|
||||||
#from generic.models import Examination, Site, Condition, Sign
|
|
||||||
|
# from generic.models import Examination, Site, Condition, Sign
|
||||||
|
|
||||||
from django.contrib.admin.widgets import FilteredSelectMultiple
|
from django.contrib.admin.widgets import FilteredSelectMultiple
|
||||||
from django.forms.widgets import RadioSelect, TextInput, Textarea
|
from django.forms.widgets import RadioSelect, TextInput, Textarea
|
||||||
|
|
||||||
from tinymce.widgets import TinyMCE
|
from tinymce.widgets import TinyMCE
|
||||||
|
|
||||||
|
|
||||||
class ExaminationForm(ModelForm):
|
class ExaminationForm(ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Examination
|
model = Examination
|
||||||
fields = ["examination"]
|
fields = ["examination"]
|
||||||
|
|
||||||
|
|
||||||
class SeriesForm(ModelForm):
|
class SeriesForm(ModelForm):
|
||||||
class Media:
|
class Media:
|
||||||
# Django also includes a few javascript files necessary
|
# Django also includes a few javascript files necessary
|
||||||
@@ -43,10 +46,10 @@ class SeriesForm(ModelForm):
|
|||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super(SeriesForm, self).__init__(*args, **kwargs)
|
super(SeriesForm, self).__init__(*args, **kwargs)
|
||||||
#self.fields["examination"] = ModelChoiceField(
|
# self.fields["examination"] = ModelChoiceField(
|
||||||
# queryset=Examination.objects.all(),
|
# queryset=Examination.objects.all(),
|
||||||
# widget=FilteredSelectMultiple(verbose_name="Examination", is_stacked=False),
|
# widget=FilteredSelectMultiple(verbose_name="Examination", is_stacked=False),
|
||||||
#)
|
# )
|
||||||
|
|
||||||
self.fields["modality"] = ModelChoiceField(
|
self.fields["modality"] = ModelChoiceField(
|
||||||
required=True,
|
required=True,
|
||||||
@@ -62,7 +65,7 @@ class SeriesForm(ModelForm):
|
|||||||
|
|
||||||
class AtlasForm(ModelForm):
|
class AtlasForm(ModelForm):
|
||||||
|
|
||||||
#exams = ModelMultipleChoiceField(required=False, queryset=Exam.objects.all(),widget=FilteredSelectMultiple(verbose_name="Exams", is_stacked=False))
|
# exams = ModelMultipleChoiceField(required=False, queryset=Exam.objects.all(),widget=FilteredSelectMultiple(verbose_name="Exams", is_stacked=False))
|
||||||
|
|
||||||
class Media:
|
class Media:
|
||||||
# Django also includes a few javascript files necessary
|
# Django also includes a few javascript files necessary
|
||||||
@@ -76,7 +79,9 @@ class AtlasForm(ModelForm):
|
|||||||
js = ["jsi18n.js", "tesseract.min.js"]
|
js = ["jsi18n.js", "tesseract.min.js"]
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.user = kwargs.pop('user') # To get request.user. Do not use kwargs.pop('user', None) due to potential security hole
|
self.user = kwargs.pop(
|
||||||
|
"user"
|
||||||
|
) # To get request.user. Do not use kwargs.pop('user', None) due to potential security hole
|
||||||
if kwargs.get("instance"):
|
if kwargs.get("instance"):
|
||||||
# We get the 'initial' keyword argument or initialize it
|
# We get the 'initial' keyword argument or initialize it
|
||||||
# as a dict if it didn't exist.
|
# as a dict if it didn't exist.
|
||||||
@@ -89,14 +94,13 @@ class AtlasForm(ModelForm):
|
|||||||
|
|
||||||
super(AtlasForm, self).__init__(*args, **kwargs)
|
super(AtlasForm, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Case
|
model = Case
|
||||||
# fields = ['due_back']
|
# fields = ['due_back']
|
||||||
# fields = '__all__'
|
# fields = '__all__'
|
||||||
fields = [
|
fields = [
|
||||||
#"examination",
|
# "examination",
|
||||||
#"site",
|
# "site",
|
||||||
"title",
|
"title",
|
||||||
"subspecialty",
|
"subspecialty",
|
||||||
"description",
|
"description",
|
||||||
@@ -111,10 +115,10 @@ class AtlasForm(ModelForm):
|
|||||||
# "normal": RadioSelect(
|
# "normal": RadioSelect(
|
||||||
# choices=[(True, 'Yes'),
|
# choices=[(True, 'Yes'),
|
||||||
# (False, 'No')])
|
# (False, 'No')])
|
||||||
"findings" : TinyMCE(attrs={'cols': 80, 'rows': 20}),
|
"findings": TinyMCE(attrs={"cols": 80, "rows": 20}),
|
||||||
"mark_scheme" : TinyMCE(attrs={'cols': 80, 'rows': 30}),
|
"mark_scheme": TinyMCE(attrs={"cols": 80, "rows": 30}),
|
||||||
"description" : Textarea(attrs={'cols': 80, 'rows': 5}),
|
"description": Textarea(attrs={"cols": 80, "rows": 5}),
|
||||||
"history" : Textarea(attrs={'cols': 80, 'rows': 5}),
|
"history": Textarea(attrs={"cols": 80, "rows": 5}),
|
||||||
}
|
}
|
||||||
|
|
||||||
def save(self, commit=True):
|
def save(self, commit=True):
|
||||||
@@ -126,8 +130,8 @@ class AtlasForm(ModelForm):
|
|||||||
|
|
||||||
def save_m2m():
|
def save_m2m():
|
||||||
old_save_m2m()
|
old_save_m2m()
|
||||||
#instance.exams.clear()
|
# instance.exams.clear()
|
||||||
#for exam in self.cleaned_data["exams"]:
|
# for exam in self.cleaned_data["exams"]:
|
||||||
# # We need the id here
|
# # We need the id here
|
||||||
# instance.exams.add(exam.pk)
|
# instance.exams.add(exam.pk)
|
||||||
|
|
||||||
@@ -140,6 +144,7 @@ class AtlasForm(ModelForm):
|
|||||||
|
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
|
||||||
SeriesFormSet = inlineformset_factory(
|
SeriesFormSet = inlineformset_factory(
|
||||||
Case,
|
Case,
|
||||||
Series.case.through,
|
Series.case.through,
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
{% extends "atlas/base.html" %}
|
{% extends "atlas/base.html" %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<a href="{% url 'atlas:series_update' pk=series.pk %}" title="Edit the Series">Edit</a>
|
<a href="{% url 'atlas:series_update' pk=series.pk %}" title="Edit the Series">Edit</a>
|
||||||
{% if request.user.is_superuser %}
|
{% if request.user.is_superuser %}
|
||||||
<a href="{% url 'admin:atlas_series_change' series.id %}" title="Edit the Series using the admin interface">Admin Edit</a>
|
<a href="{% url 'admin:atlas_series_change' series.id %}" title="Edit the Series using the admin interface">Admin Edit</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% include 'atlas/series_viewer.html' %}
|
{% include 'atlas/series_viewer.html' %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
+9
-1
@@ -197,7 +197,15 @@ SeriesFormSet = inlineformset_factory(
|
|||||||
can_delete=True,
|
can_delete=True,
|
||||||
extra=0,
|
extra=0,
|
||||||
max_num=10,
|
max_num=10,
|
||||||
field_classes="testing",
|
)
|
||||||
|
|
||||||
|
LongFormSet = inlineformset_factory(
|
||||||
|
LongSeries,
|
||||||
|
Long,
|
||||||
|
exclude=[],
|
||||||
|
can_delete=True,
|
||||||
|
extra=0,
|
||||||
|
max_num=10,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+182
-81
@@ -5,7 +5,11 @@ from django import forms
|
|||||||
# from django.contrib.auth.models import User
|
# from django.contrib.auth.models import User
|
||||||
from django.contrib.auth.decorators import login_required, user_passes_test
|
from django.contrib.auth.decorators import login_required, user_passes_test
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.core.exceptions import ObjectDoesNotExist, PermissionDenied, ViewDoesNotExist
|
from django.core.exceptions import (
|
||||||
|
ObjectDoesNotExist,
|
||||||
|
PermissionDenied,
|
||||||
|
ViewDoesNotExist,
|
||||||
|
)
|
||||||
|
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.views.generic.detail import DetailView
|
from django.views.generic.detail import DetailView
|
||||||
@@ -22,7 +26,9 @@ from django.http import HttpResponseRedirect, HttpResponse
|
|||||||
|
|
||||||
from .forms import (
|
from .forms import (
|
||||||
LongForm,
|
LongForm,
|
||||||
LongSeriesForm, LongSeriesImageFormSet,
|
LongFormSet,
|
||||||
|
LongSeriesForm,
|
||||||
|
LongSeriesImageFormSet,
|
||||||
MarkLongQuestionDoubleForm,
|
MarkLongQuestionDoubleForm,
|
||||||
MarkLongQuestionSingleForm,
|
MarkLongQuestionSingleForm,
|
||||||
SeriesFormSet,
|
SeriesFormSet,
|
||||||
@@ -30,7 +36,9 @@ from .forms import (
|
|||||||
ExamForm,
|
ExamForm,
|
||||||
)
|
)
|
||||||
from .models import (
|
from .models import (
|
||||||
AnswerMarks, Long, LongSeries,
|
AnswerMarks,
|
||||||
|
Long,
|
||||||
|
LongSeries,
|
||||||
Examination,
|
Examination,
|
||||||
Exam,
|
Exam,
|
||||||
CidUserAnswer,
|
CidUserAnswer,
|
||||||
@@ -41,7 +49,15 @@ from .filters import LongFilter, LongSeriesFilter, UserAnswerFilter
|
|||||||
from django_tables2 import SingleTableView, SingleTableMixin
|
from django_tables2 import SingleTableView, SingleTableMixin
|
||||||
from django_filters.views import FilterView
|
from django_filters.views import FilterView
|
||||||
|
|
||||||
from .decorators import user_is_author_or_long_checker, user_is_author_or_long_checker_or_long_marker, user_is_author_or_long_series_checker_or_long_marker, user_is_long_checker, user_is_author_or_long_series_checker, user_is_long_marker, user_is_author_or_long_checker_or_long_marker
|
from .decorators import (
|
||||||
|
user_is_author_or_long_checker,
|
||||||
|
user_is_author_or_long_checker_or_long_marker,
|
||||||
|
user_is_author_or_long_series_checker_or_long_marker,
|
||||||
|
user_is_long_checker,
|
||||||
|
user_is_author_or_long_series_checker,
|
||||||
|
user_is_long_marker,
|
||||||
|
user_is_author_or_long_checker_or_long_marker,
|
||||||
|
)
|
||||||
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
import json
|
import json
|
||||||
@@ -74,6 +90,7 @@ import os
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def normaliseScore(score):
|
def normaliseScore(score):
|
||||||
if score >= 25.5 and score <= 28:
|
if score >= 25.5 and score <= 28:
|
||||||
return 4.5
|
return 4.5
|
||||||
@@ -98,7 +115,10 @@ def normaliseScore(score):
|
|||||||
class AuthorOrCheckerRequiredMixin(object):
|
class AuthorOrCheckerRequiredMixin(object):
|
||||||
def get_object(self, *args, **kwargs):
|
def get_object(self, *args, **kwargs):
|
||||||
obj = super().get_object(*args, **kwargs)
|
obj = super().get_object(*args, **kwargs)
|
||||||
if self.request.user.groups.filter(name="long_checker").exists() or self.request.user.is_superuser:
|
if (
|
||||||
|
self.request.user.groups.filter(name="long_checker").exists()
|
||||||
|
or self.request.user.is_superuser
|
||||||
|
):
|
||||||
return obj
|
return obj
|
||||||
if self.request.user not in obj.get_author_objects():
|
if self.request.user not in obj.get_author_objects():
|
||||||
raise PermissionDenied() # or Http404
|
raise PermissionDenied() # or Http404
|
||||||
@@ -126,6 +146,7 @@ def question_detail(request, pk):
|
|||||||
# logging.debug(long.subspecialty.first().name.all())
|
# logging.debug(long.subspecialty.first().name.all())
|
||||||
return render(request, "longs/question_detail.html", {"question": long})
|
return render(request, "longs/question_detail.html", {"question": long})
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@user_is_author_or_long_series_checker_or_long_marker
|
@user_is_author_or_long_series_checker_or_long_marker
|
||||||
def long_series_detail(request, pk):
|
def long_series_detail(request, pk):
|
||||||
@@ -137,6 +158,7 @@ def long_series_detail(request, pk):
|
|||||||
# logging.debug(long.subspecialty.first().name.all())
|
# logging.debug(long.subspecialty.first().name.all())
|
||||||
return render(request, "longs/long_series.html", {"series": series})
|
return render(request, "longs/long_series.html", {"series": series})
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@user_is_author_or_long_checker
|
@user_is_author_or_long_checker
|
||||||
def long_split(request, pk):
|
def long_split(request, pk):
|
||||||
@@ -236,10 +258,12 @@ class LongDelete(RevisionMixin, AuthorOrCheckerRequiredMixin, DeleteView):
|
|||||||
model = Long
|
model = Long
|
||||||
success_url = reverse_lazy("longs:long_view")
|
success_url = reverse_lazy("longs:long_view")
|
||||||
|
|
||||||
|
|
||||||
class LongSeriesDelete(RevisionMixin, AuthorOrCheckerRequiredMixin, DeleteView):
|
class LongSeriesDelete(RevisionMixin, AuthorOrCheckerRequiredMixin, DeleteView):
|
||||||
model = LongSeries
|
model = LongSeries
|
||||||
success_url = reverse_lazy("longs:long_series_view")
|
success_url = reverse_lazy("longs:long_series_view")
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@user_is_author_or_long_checker
|
@user_is_author_or_long_checker
|
||||||
def long_clone(request, pk):
|
def long_clone(request, pk):
|
||||||
@@ -272,6 +296,7 @@ def long_clone(request, pk):
|
|||||||
|
|
||||||
return render(request, "longs/long_form.html", context)
|
return render(request, "longs/long_form.html", context)
|
||||||
|
|
||||||
|
|
||||||
class LongSeriesCreate(RevisionMixin, LoginRequiredMixin, CreateView):
|
class LongSeriesCreate(RevisionMixin, LoginRequiredMixin, CreateView):
|
||||||
model = LongSeries
|
model = LongSeries
|
||||||
form_class = LongSeriesForm
|
form_class = LongSeriesForm
|
||||||
@@ -282,7 +307,7 @@ class LongSeriesCreate(RevisionMixin, LoginRequiredMixin, CreateView):
|
|||||||
initial = super().get_initial()
|
initial = super().get_initial()
|
||||||
long = get_object_or_404(Long, pk=self.kwargs["pk"])
|
long = get_object_or_404(Long, pk=self.kwargs["pk"])
|
||||||
|
|
||||||
initial['long'] = long.id
|
initial["long"] = long.id
|
||||||
|
|
||||||
return initial
|
return initial
|
||||||
|
|
||||||
@@ -294,8 +319,11 @@ class LongSeriesCreate(RevisionMixin, LoginRequiredMixin, CreateView):
|
|||||||
self.request.POST, self.request.FILES
|
self.request.POST, self.request.FILES
|
||||||
)
|
)
|
||||||
context["image_formset"].full_clean()
|
context["image_formset"].full_clean()
|
||||||
|
context["long_formset"] = LongFormSet(self.request.POST, self.request.FILES)
|
||||||
|
context["long_formset"].full_clean()
|
||||||
else:
|
else:
|
||||||
context["image_formset"] = LongSeriesImageFormSet()
|
context["image_formset"] = LongSeriesImageFormSet()
|
||||||
|
context["leng_formset"] = LongFormSet()
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
@@ -307,16 +335,22 @@ class LongSeriesCreate(RevisionMixin, LoginRequiredMixin, CreateView):
|
|||||||
|
|
||||||
context = self.get_context_data(form=form)
|
context = self.get_context_data(form=form)
|
||||||
series_formset = context["image_formset"]
|
series_formset = context["image_formset"]
|
||||||
if series_formset.is_valid():
|
long_formset = context["long_formset"]
|
||||||
|
if series_formset.is_valid() and long_formset.is_valid():
|
||||||
response = super().form_valid(form)
|
response = super().form_valid(form)
|
||||||
series_formset.instance = self.object
|
series_formset.instance = self.object
|
||||||
series_formset.save()
|
series_formset.save()
|
||||||
|
long_formset.instance = self.object
|
||||||
|
long_formset.save()
|
||||||
return response
|
return response
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return super().form_invalid(form)
|
return super().form_invalid(form)
|
||||||
|
|
||||||
class LongSeriesUpdate(RevisionMixin, LoginRequiredMixin, AuthorOrCheckerRequiredMixin, UpdateView):
|
|
||||||
|
class LongSeriesUpdate(
|
||||||
|
RevisionMixin, LoginRequiredMixin, AuthorOrCheckerRequiredMixin, UpdateView
|
||||||
|
):
|
||||||
model = LongSeries
|
model = LongSeries
|
||||||
form_class = LongSeriesForm
|
form_class = LongSeriesForm
|
||||||
|
|
||||||
@@ -341,9 +375,9 @@ class LongSeriesUpdate(RevisionMixin, LoginRequiredMixin, AuthorOrCheckerRequire
|
|||||||
self.object.save()
|
self.object.save()
|
||||||
|
|
||||||
# Do this in the form.save()
|
# Do this in the form.save()
|
||||||
#new_exams = list(form.cleaned_data['exams'].values_list("pk", flat=True))
|
# new_exams = list(form.cleaned_data['exams'].values_list("pk", flat=True))
|
||||||
#self.object.exams.clear()
|
# self.object.exams.clear()
|
||||||
#self.object.exams.add(*new_exams)
|
# self.object.exams.add(*new_exams)
|
||||||
|
|
||||||
form.instance.author.add(self.request.user.id)
|
form.instance.author.add(self.request.user.id)
|
||||||
|
|
||||||
@@ -358,6 +392,7 @@ class LongSeriesUpdate(RevisionMixin, LoginRequiredMixin, AuthorOrCheckerRequire
|
|||||||
else:
|
else:
|
||||||
return super().form_invalid(form)
|
return super().form_invalid(form)
|
||||||
|
|
||||||
|
|
||||||
class LongCreateBase(RevisionMixin, LoginRequiredMixin, CreateView):
|
class LongCreateBase(RevisionMixin, LoginRequiredMixin, CreateView):
|
||||||
model = Long
|
model = Long
|
||||||
form_class = LongForm
|
form_class = LongForm
|
||||||
@@ -387,9 +422,9 @@ class LongCreateBase(RevisionMixin, LoginRequiredMixin, CreateView):
|
|||||||
self.object.save()
|
self.object.save()
|
||||||
|
|
||||||
# Do this in the form.save()
|
# Do this in the form.save()
|
||||||
#new_exams = list(form.cleaned_data['exams'].values_list("pk", flat=True))
|
# new_exams = list(form.cleaned_data['exams'].values_list("pk", flat=True))
|
||||||
#self.object.exams.clear()
|
# self.object.exams.clear()
|
||||||
#self.object.exams.add(*new_exams)
|
# self.object.exams.add(*new_exams)
|
||||||
|
|
||||||
form.instance.author.add(self.request.user.id)
|
form.instance.author.add(self.request.user.id)
|
||||||
|
|
||||||
@@ -433,7 +468,9 @@ class LongCreate(LongCreateBase):
|
|||||||
# return super().form_valid(form)
|
# return super().form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
class LongUpdate(RevisionMixin, LoginRequiredMixin, AuthorOrCheckerRequiredMixin, UpdateView):
|
class LongUpdate(
|
||||||
|
RevisionMixin, LoginRequiredMixin, AuthorOrCheckerRequiredMixin, UpdateView
|
||||||
|
):
|
||||||
model = Long
|
model = Long
|
||||||
form_class = LongForm
|
form_class = LongForm
|
||||||
|
|
||||||
@@ -468,9 +505,9 @@ class LongUpdate(RevisionMixin, LoginRequiredMixin, AuthorOrCheckerRequiredMixin
|
|||||||
self.object.save()
|
self.object.save()
|
||||||
|
|
||||||
# Do this in the form.save()
|
# Do this in the form.save()
|
||||||
#new_exams = list(form.cleaned_data['exams'].values_list("pk", flat=True))
|
# new_exams = list(form.cleaned_data['exams'].values_list("pk", flat=True))
|
||||||
#self.object.exams.clear()
|
# self.object.exams.clear()
|
||||||
#self.object.exams.add(*new_exams)
|
# self.object.exams.add(*new_exams)
|
||||||
|
|
||||||
form.instance.author.add(self.request.user.id)
|
form.instance.author.add(self.request.user.id)
|
||||||
|
|
||||||
@@ -565,6 +602,7 @@ class LongView(LoginRequiredMixin, SingleTableMixin, FilterView):
|
|||||||
|
|
||||||
filterset_class = LongFilter
|
filterset_class = LongFilter
|
||||||
|
|
||||||
|
|
||||||
class LongSeriesView(LoginRequiredMixin, SingleTableMixin, FilterView):
|
class LongSeriesView(LoginRequiredMixin, SingleTableMixin, FilterView):
|
||||||
model = LongSeries
|
model = LongSeries
|
||||||
table_class = LongSeriesTable
|
table_class = LongSeriesTable
|
||||||
@@ -591,7 +629,7 @@ def loadJsonAnswer(answer):
|
|||||||
|
|
||||||
exam = get_object_or_404(Exam, pk=eid)
|
exam = get_object_or_404(Exam, pk=eid)
|
||||||
|
|
||||||
#if not exam.active:
|
# if not exam.active:
|
||||||
# return False, JsonResponse(
|
# return False, JsonResponse(
|
||||||
# {"success": False, "error": "No active exam: {}".format(eid)}
|
# {"success": False, "error": "No active exam: {}".format(eid)}
|
||||||
# )
|
# )
|
||||||
@@ -605,14 +643,12 @@ def loadJsonAnswer(answer):
|
|||||||
# Long cases seperate sections by qidn
|
# Long cases seperate sections by qidn
|
||||||
qidn = answer["qidn"]
|
qidn = answer["qidn"]
|
||||||
|
|
||||||
|
|
||||||
# If the user answer does not exist
|
# If the user answer does not exist
|
||||||
if not exiting_answers:
|
if not exiting_answers:
|
||||||
ans = CidUserAnswer(cid=answer["cid"])
|
ans = CidUserAnswer(cid=answer["cid"])
|
||||||
ans.question_id = answer["qid"]
|
ans.question_id = answer["qid"]
|
||||||
ans.exam_id = eid
|
ans.exam_id = eid
|
||||||
|
|
||||||
|
|
||||||
# If the answer already exists or we have started populating it
|
# If the answer already exists or we have started populating it
|
||||||
else:
|
else:
|
||||||
# Update an existing answer
|
# Update an existing answer
|
||||||
@@ -638,6 +674,7 @@ def loadJsonAnswer(answer):
|
|||||||
|
|
||||||
return True, None
|
return True, None
|
||||||
|
|
||||||
|
|
||||||
@user_is_long_marker
|
@user_is_long_marker
|
||||||
@reversion.create_revision()
|
@reversion.create_revision()
|
||||||
def mark_answer_override(request, exam_id, question_number, cid):
|
def mark_answer_override(request, exam_id, question_number, cid):
|
||||||
@@ -668,7 +705,9 @@ def mark_answer(request, exam_id, question_number, cid, override=False):
|
|||||||
except ObjectDoesNotExist:
|
except ObjectDoesNotExist:
|
||||||
raise Http404("User answer does not exist")
|
raise Http404("User answer does not exist")
|
||||||
|
|
||||||
cid_list = list(question.cid_user_answers.filter(exam__id=exam_id).values_list("cid", flat=True))
|
cid_list = list(
|
||||||
|
question.cid_user_answers.filter(exam__id=exam_id).values_list("cid", flat=True)
|
||||||
|
)
|
||||||
|
|
||||||
previous_cid = False
|
previous_cid = False
|
||||||
next_cid = False
|
next_cid = False
|
||||||
@@ -679,12 +718,14 @@ def mark_answer(request, exam_id, question_number, cid, override=False):
|
|||||||
elif cid_list[-1] == cid:
|
elif cid_list[-1] == cid:
|
||||||
previous_cid = cid_list[-2]
|
previous_cid = cid_list[-2]
|
||||||
else:
|
else:
|
||||||
next_cid = cid_list[cid_list.index(cid)+1]
|
next_cid = cid_list[cid_list.index(cid) + 1]
|
||||||
previous_cid = cid_list[cid_list.index(cid)-1]
|
previous_cid = cid_list[cid_list.index(cid) - 1]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if exam.double_mark:
|
if exam.double_mark:
|
||||||
unmarked = question.get_unmarked_user_answers(exam_pk=exam.id, marker=request.user)
|
unmarked = question.get_unmarked_user_answers(
|
||||||
|
exam_pk=exam.id, marker=request.user
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
unmarked = question.get_unmarked_user_answers(exam_pk=exam.id)
|
unmarked = question.get_unmarked_user_answers(exam_pk=exam.id)
|
||||||
next_unmarked_id = unmarked[0].cid
|
next_unmarked_id = unmarked[0].cid
|
||||||
@@ -695,14 +736,16 @@ def mark_answer(request, exam_id, question_number, cid, override=False):
|
|||||||
|
|
||||||
# We use different forms if the exam should be single or double marked
|
# We use different forms if the exam should be single or double marked
|
||||||
|
|
||||||
if not exam.double_mark or (request.method == "POST" and request.POST['form_id'] == 'discrepancy_form'):
|
if not exam.double_mark or (
|
||||||
|
request.method == "POST" and request.POST["form_id"] == "discrepancy_form"
|
||||||
|
):
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
form = MarkLongQuestionSingleForm(request.POST)
|
form = MarkLongQuestionSingleForm(request.POST)
|
||||||
|
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
# If skip button is pressed skip the question without marking
|
# If skip button is pressed skip the question without marking
|
||||||
# Skip is problematic if we skip to the next unmarked answer
|
# Skip is problematic if we skip to the next unmarked answer
|
||||||
#if "skip" in request.POST:
|
# if "skip" in request.POST:
|
||||||
# return redirect("longs:mark_answer", pk=exam_id, sk=question_number, cid=next_unmarked_id)
|
# return redirect("longs:mark_answer", pk=exam_id, sk=question_number, cid=next_unmarked_id)
|
||||||
|
|
||||||
# Extract score from form and save it to the object
|
# Extract score from form and save it to the object
|
||||||
@@ -710,14 +753,24 @@ def mark_answer(request, exam_id, question_number, cid, override=False):
|
|||||||
answer.save()
|
answer.save()
|
||||||
|
|
||||||
if "next" in request.POST:
|
if "next" in request.POST:
|
||||||
return redirect("longs:mark_answer", exam_id=exam_id, question_number=question_number, cid=next_unmarked_id)
|
return redirect(
|
||||||
|
"longs:mark_answer",
|
||||||
|
exam_id=exam_id,
|
||||||
|
question_number=question_number,
|
||||||
|
cid=next_unmarked_id,
|
||||||
|
)
|
||||||
if "save" in request.POST:
|
if "save" in request.POST:
|
||||||
return redirect("longs:mark_answer", exam_id=exam_id, question_number=question_number, cid=cid)
|
return redirect(
|
||||||
#elif "previous" in request.POST:
|
"longs:mark_answer",
|
||||||
|
exam_id=exam_id,
|
||||||
|
question_number=question_number,
|
||||||
|
cid=cid,
|
||||||
|
)
|
||||||
|
# elif "previous" in request.POST:
|
||||||
# return redirect("longs:mark_question_overview", pk=exam_id, sk=n - 1)
|
# return redirect("longs:mark_question_overview", pk=exam_id, sk=n - 1)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
form = MarkLongQuestionSingleForm(initial={'score': answer.score})
|
form = MarkLongQuestionSingleForm(initial={"score": answer.score})
|
||||||
|
|
||||||
else:
|
else:
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
@@ -726,11 +779,12 @@ def mark_answer(request, exam_id, question_number, cid, override=False):
|
|||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
# If skip button is pressed skip the question without marking
|
# If skip button is pressed skip the question without marking
|
||||||
# Skip is problematic if we skip to the next unmarked answer
|
# Skip is problematic if we skip to the next unmarked answer
|
||||||
#if "skip" in request.POST:
|
# if "skip" in request.POST:
|
||||||
# return redirect("longs:mark_answer", pk=exam_id, sk=question_number, cid=next_unmarked_id)
|
# return redirect("longs:mark_answer", pk=exam_id, sk=question_number, cid=next_unmarked_id)
|
||||||
|
|
||||||
|
mark_object = AnswerMarks.objects.get_or_create(
|
||||||
mark_object = AnswerMarks.objects.get_or_create(user_answer=answer, marker=request.user)[0]
|
user_answer=answer, marker=request.user
|
||||||
|
)[0]
|
||||||
# Extract score from form and save it to the object
|
# Extract score from form and save it to the object
|
||||||
mark_object.score = form.cleaned_data["score"]
|
mark_object.score = form.cleaned_data["score"]
|
||||||
mark_object.mark_reason = form.cleaned_data["mark_reason"]
|
mark_object.mark_reason = form.cleaned_data["mark_reason"]
|
||||||
@@ -748,19 +802,35 @@ def mark_answer(request, exam_id, question_number, cid, override=False):
|
|||||||
answer.score = ""
|
answer.score = ""
|
||||||
answer.save()
|
answer.save()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if "next" in request.POST:
|
if "next" in request.POST:
|
||||||
return redirect("longs:mark_answer", exam_id=exam_id, question_number=question_number, cid=next_unmarked_id)
|
return redirect(
|
||||||
|
"longs:mark_answer",
|
||||||
|
exam_id=exam_id,
|
||||||
|
question_number=question_number,
|
||||||
|
cid=next_unmarked_id,
|
||||||
|
)
|
||||||
if "save" in request.POST:
|
if "save" in request.POST:
|
||||||
return redirect("longs:mark_answer", exam_id=exam_id, question_number=question_number, cid=cid)
|
return redirect(
|
||||||
#elif "previous" in request.POST:
|
"longs:mark_answer",
|
||||||
|
exam_id=exam_id,
|
||||||
|
question_number=question_number,
|
||||||
|
cid=cid,
|
||||||
|
)
|
||||||
|
# elif "previous" in request.POST:
|
||||||
# return redirect("longs:mark_question_overview", pk=exam_id, sk=n - 1)
|
# return redirect("longs:mark_question_overview", pk=exam_id, sk=n - 1)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
mark_object = AnswerMarks.objects.get(user_answer=answer, marker=request.user)
|
mark_object = AnswerMarks.objects.get(
|
||||||
form = MarkLongQuestionDoubleForm(initial={'score': mark_object.score,'mark_reason': mark_object.mark_reason,'candidate_feedback': mark_object.candidate_feedback, })
|
user_answer=answer, marker=request.user
|
||||||
|
)
|
||||||
|
form = MarkLongQuestionDoubleForm(
|
||||||
|
initial={
|
||||||
|
"score": mark_object.score,
|
||||||
|
"mark_reason": mark_object.mark_reason,
|
||||||
|
"candidate_feedback": mark_object.candidate_feedback,
|
||||||
|
}
|
||||||
|
)
|
||||||
except AnswerMarks.DoesNotExist:
|
except AnswerMarks.DoesNotExist:
|
||||||
form = MarkLongQuestionDoubleForm()
|
form = MarkLongQuestionDoubleForm()
|
||||||
|
|
||||||
@@ -769,7 +839,9 @@ def mark_answer(request, exam_id, question_number, cid, override=False):
|
|||||||
# if they do automatically update teh scoer
|
# if they do automatically update teh scoer
|
||||||
marks = set(answer.mark.values_list("score", flat=True))
|
marks = set(answer.mark.values_list("score", flat=True))
|
||||||
if len(marks) > 1 or override:
|
if len(marks) > 1 or override:
|
||||||
discrepancy_form = MarkLongQuestionSingleForm(initial={'score': answer.score })
|
discrepancy_form = MarkLongQuestionSingleForm(
|
||||||
|
initial={"score": answer.score}
|
||||||
|
)
|
||||||
|
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
@@ -789,6 +861,7 @@ def mark_answer(request, exam_id, question_number, cid, override=False):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
# @user_passes_test(user_is_admin, login_url="/accounts/login")
|
# @user_passes_test(user_is_admin, login_url="/accounts/login")
|
||||||
@login_required
|
@login_required
|
||||||
@user_is_long_marker
|
@user_is_long_marker
|
||||||
@@ -809,13 +882,16 @@ def mark_question_overview(request, exam_id, sk):
|
|||||||
except IndexError:
|
except IndexError:
|
||||||
raise Http404("Exam question does not exist")
|
raise Http404("Exam question does not exist")
|
||||||
|
|
||||||
|
|
||||||
user_answers = question.cid_user_answers.filter(exam__id=exam_id)
|
user_answers = question.cid_user_answers.filter(exam__id=exam_id)
|
||||||
|
|
||||||
unmarked_count = user_answers.filter(score=CidUserAnswer.ScoreOptions.UNMARKED).count()
|
unmarked_count = user_answers.filter(
|
||||||
|
score=CidUserAnswer.ScoreOptions.UNMARKED
|
||||||
|
).count()
|
||||||
|
|
||||||
if exam.double_mark:
|
if exam.double_mark:
|
||||||
marker_unmarked_count = question.get_unmarked_user_answer_count(exam.pk, marker=request.user)
|
marker_unmarked_count = question.get_unmarked_user_answer_count(
|
||||||
|
exam.pk, marker=request.user
|
||||||
|
)
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
"longs/mark_question_double_overview.html",
|
"longs/mark_question_double_overview.html",
|
||||||
@@ -875,7 +951,7 @@ def exam_scores_cid(request, pk):
|
|||||||
if not s:
|
if not s:
|
||||||
# skip if no answer, (score 4)
|
# skip if no answer, (score 4)
|
||||||
user_answers_marks[cid].append(4)
|
user_answers_marks[cid].append(4)
|
||||||
#user_answers[cid].append("")
|
# user_answers[cid].append("")
|
||||||
by_question[q].append(("", 4))
|
by_question[q].append(("", 4))
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
@@ -884,22 +960,19 @@ def exam_scores_cid(request, pk):
|
|||||||
if answer_score == "":
|
if answer_score == "":
|
||||||
index = exam.get_question_index(q)
|
index = exam.get_question_index(q)
|
||||||
unmarked.add(index)
|
unmarked.add(index)
|
||||||
#user_answers[cid].append(ans)
|
# user_answers[cid].append(ans)
|
||||||
user_answers_marks[cid].append(answer_score)
|
user_answers_marks[cid].append(answer_score)
|
||||||
#user_answers_and_marks[cid].append((ans, answer_score))
|
# user_answers_and_marks[cid].append((ans, answer_score))
|
||||||
|
|
||||||
by_question[q].append((ans, answer_score))
|
by_question[q].append((ans, answer_score))
|
||||||
|
|
||||||
user_scores = {}
|
user_scores = {}
|
||||||
user_scores_normalised = {}
|
user_scores_normalised = {}
|
||||||
for user in user_answers_marks:
|
for user in user_answers_marks:
|
||||||
user_scores[user] = sum(
|
user_scores[user] = sum([i for i in user_answers_marks[user] if i != ""])
|
||||||
[i for i in user_answers_marks[user] if i != ""]
|
user_scores_normalised[user] = normaliseScore(
|
||||||
|
sum([i for i in user_answers_marks[user] if i != ""])
|
||||||
)
|
)
|
||||||
user_scores_normalised[user] = normaliseScore(sum(
|
|
||||||
[i for i in user_answers_marks[user] if i != ""]
|
|
||||||
))
|
|
||||||
|
|
||||||
|
|
||||||
user_scores_list = list(user_scores.values())
|
user_scores_list = list(user_scores.values())
|
||||||
|
|
||||||
@@ -938,13 +1011,13 @@ def exam_scores_cid(request, pk):
|
|||||||
"unmarked": unmarked,
|
"unmarked": unmarked,
|
||||||
"questions": questions,
|
"questions": questions,
|
||||||
"by_question": by_question,
|
"by_question": by_question,
|
||||||
#"user_answers": dict(user_answers),
|
# "user_answers": dict(user_answers),
|
||||||
"user_answers_marks": dict(user_answers_marks),
|
"user_answers_marks": dict(user_answers_marks),
|
||||||
"user_scores": user_scores,
|
"user_scores": user_scores,
|
||||||
"user_scores_normalised": user_scores_normalised,
|
"user_scores_normalised": user_scores_normalised,
|
||||||
"user_scores_list": user_scores_list,
|
"user_scores_list": user_scores_list,
|
||||||
"user_names": user_names,
|
"user_names": user_names,
|
||||||
#"user_answers_and_marks": user_answers_and_marks,
|
# "user_answers_and_marks": user_answers_and_marks,
|
||||||
"max_score": max_score,
|
"max_score": max_score,
|
||||||
"mean": mean,
|
"mean": mean,
|
||||||
"median": median,
|
"median": median,
|
||||||
@@ -962,12 +1035,11 @@ def exam_scores_cid_user(request, pk, sk):
|
|||||||
|
|
||||||
questions = exam.exam_questions.all()
|
questions = exam.exam_questions.all()
|
||||||
|
|
||||||
#answers_and_marks = []
|
# answers_and_marks = []
|
||||||
answers_marks = []
|
answers_marks = []
|
||||||
#answers = []
|
# answers = []
|
||||||
answer_text = []
|
answer_text = []
|
||||||
|
|
||||||
|
|
||||||
view_all_results = False
|
view_all_results = False
|
||||||
if request.user.groups.filter(name="view_all_results").exists():
|
if request.user.groups.filter(name="view_all_results").exists():
|
||||||
view_all_results = True
|
view_all_results = True
|
||||||
@@ -976,26 +1048,39 @@ def exam_scores_cid_user(request, pk, sk):
|
|||||||
# Get user answer
|
# Get user answer
|
||||||
user_answer = q.cid_user_answers.filter(cid=cid, exam__id=pk).first()
|
user_answer = q.cid_user_answers.filter(cid=cid, exam__id=pk).first()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if not user_answer or user_answer is None:
|
if not user_answer or user_answer is None:
|
||||||
# skip if no answer
|
# skip if no answer
|
||||||
#answers_marks.append("")
|
# answers_marks.append("")
|
||||||
#answers.append("")
|
# answers.append("")
|
||||||
answer_score = 4
|
answer_score = 4
|
||||||
#ans = "Not answered"
|
# ans = "Not answered"
|
||||||
answer_text.append((("Not answered"),("Not answered"),("Not answered"),("Not answered"),("Not answered")))
|
answer_text.append(
|
||||||
|
(
|
||||||
|
("Not answered"),
|
||||||
|
("Not answered"),
|
||||||
|
("Not answered"),
|
||||||
|
("Not answered"),
|
||||||
|
("Not answered"),
|
||||||
|
)
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
answer_score = user_answer.get_answer_score()
|
answer_score = user_answer.get_answer_score()
|
||||||
|
|
||||||
answer_text.append((user_answer.answer_observations, user_answer.answer_interpretation, user_answer.answer_principle_diagnosis, user_answer.answer_differential_diagnosis, user_answer.answer_management))
|
answer_text.append(
|
||||||
|
(
|
||||||
|
user_answer.answer_observations,
|
||||||
|
user_answer.answer_interpretation,
|
||||||
|
user_answer.answer_principle_diagnosis,
|
||||||
|
user_answer.answer_differential_diagnosis,
|
||||||
|
user_answer.answer_management,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
if not exam.publish_results and not view_all_results:
|
if not exam.publish_results and not view_all_results:
|
||||||
answer_score = 0
|
answer_score = 0
|
||||||
#answers.append(ans)
|
# answers.append(ans)
|
||||||
answers_marks.append(answer_score)
|
answers_marks.append(answer_score)
|
||||||
#answers_and_marks.append((ans, answer_score, correct_answer))
|
# answers_and_marks.append((ans, answer_score, correct_answer))
|
||||||
|
|
||||||
answered = [i for i in answers_marks if i != ""]
|
answered = [i for i in answers_marks if i != ""]
|
||||||
if "" in answers_marks:
|
if "" in answers_marks:
|
||||||
@@ -1015,16 +1100,17 @@ def exam_scores_cid_user(request, pk, sk):
|
|||||||
"exam": exam,
|
"exam": exam,
|
||||||
"cid": cid,
|
"cid": cid,
|
||||||
"questions": questions,
|
"questions": questions,
|
||||||
#"answers": answers,
|
# "answers": answers,
|
||||||
"answers_marks": answers_marks,
|
"answers_marks": answers_marks,
|
||||||
"total_score": total_score,
|
"total_score": total_score,
|
||||||
"normalised_score": normalised_score,
|
"normalised_score": normalised_score,
|
||||||
"max_score": max_score,
|
"max_score": max_score,
|
||||||
"answer_text": answer_text
|
"answer_text": answer_text
|
||||||
#"answers_and_marks": answers_and_marks,
|
# "answers_and_marks": answers_and_marks,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@user_is_author_or_long_series_checker
|
@user_is_author_or_long_series_checker
|
||||||
def long_series_order_dicom(request, pk):
|
def long_series_order_dicom(request, pk):
|
||||||
@@ -1032,10 +1118,11 @@ def long_series_order_dicom(request, pk):
|
|||||||
try:
|
try:
|
||||||
series.order_by_dicom()
|
series.order_by_dicom()
|
||||||
except:
|
except:
|
||||||
return HttpResponse('<h1>Series does not appear to contain dicoms</h1>')
|
return HttpResponse("<h1>Series does not appear to contain dicoms</h1>")
|
||||||
|
|
||||||
return redirect("longs:long_series_detail", pk=pk)
|
return redirect("longs:long_series_detail", pk=pk)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@user_is_author_or_long_series_checker
|
@user_is_author_or_long_series_checker
|
||||||
def long_series_order_dicom_instance(request, pk):
|
def long_series_order_dicom_instance(request, pk):
|
||||||
@@ -1043,10 +1130,13 @@ def long_series_order_dicom_instance(request, pk):
|
|||||||
try:
|
try:
|
||||||
series.order_by_dicom("InstanceNumber")
|
series.order_by_dicom("InstanceNumber")
|
||||||
except:
|
except:
|
||||||
return HttpResponse('<h1>Series does not appear to contain dicoms (or field InstanceNumber)</h1>')
|
return HttpResponse(
|
||||||
|
"<h1>Series does not appear to contain dicoms (or field InstanceNumber)</h1>"
|
||||||
|
)
|
||||||
|
|
||||||
return redirect("longs:long_series_detail", pk=pk)
|
return redirect("longs:long_series_detail", pk=pk)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@user_is_author_or_long_series_checker
|
@user_is_author_or_long_series_checker
|
||||||
def long_series_order_dicom_SeriesInstanceUID(request, pk):
|
def long_series_order_dicom_SeriesInstanceUID(request, pk):
|
||||||
@@ -1054,10 +1144,13 @@ def long_series_order_dicom_SeriesInstanceUID(request, pk):
|
|||||||
try:
|
try:
|
||||||
series.order_by_dicom("SeriesInstanceUID")
|
series.order_by_dicom("SeriesInstanceUID")
|
||||||
except:
|
except:
|
||||||
return HttpResponse('<h1>Series does not appear to contain dicoms (or field SeriesInstanceUID)</h1>')
|
return HttpResponse(
|
||||||
|
"<h1>Series does not appear to contain dicoms (or field SeriesInstanceUID)</h1>"
|
||||||
|
)
|
||||||
|
|
||||||
return redirect("longs:long_series_detail", pk=pk)
|
return redirect("longs:long_series_detail", pk=pk)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@user_is_author_or_long_series_checker
|
@user_is_author_or_long_series_checker
|
||||||
def long_series_order_upload_filename(request, pk):
|
def long_series_order_upload_filename(request, pk):
|
||||||
@@ -1066,6 +1159,7 @@ def long_series_order_upload_filename(request, pk):
|
|||||||
|
|
||||||
return redirect("longs:long_series_detail", pk=pk)
|
return redirect("longs:long_series_detail", pk=pk)
|
||||||
|
|
||||||
|
|
||||||
LongExamViews = ExamViews(Exam, Long, "longs", "long", loadJsonAnswer)
|
LongExamViews = ExamViews(Exam, Long, "longs", "long", loadJsonAnswer)
|
||||||
|
|
||||||
|
|
||||||
@@ -1080,6 +1174,7 @@ class ExamCreate(RevisionMixin, LoginRequiredMixin, CreateView):
|
|||||||
form.instance.author.add(self.request.user.id)
|
form.instance.author.add(self.request.user.id)
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
class ExamClone(ExamCloneMixin, ExamCreate):
|
class ExamClone(ExamCloneMixin, ExamCreate):
|
||||||
"""Clone exam view"""
|
"""Clone exam view"""
|
||||||
|
|
||||||
@@ -1105,9 +1200,10 @@ class ExamDelete(RevisionMixin, AuthorOrCheckerRequiredMixin, DeleteView):
|
|||||||
|
|
||||||
|
|
||||||
class ExamViewSet(RevisionMixin, viewsets.ModelViewSet):
|
class ExamViewSet(RevisionMixin, viewsets.ModelViewSet):
|
||||||
queryset = Exam.objects.all().order_by('name')
|
queryset = Exam.objects.all().order_by("name")
|
||||||
serializer_class = ExamSerializer
|
serializer_class = ExamSerializer
|
||||||
|
|
||||||
|
|
||||||
def question_json_unbased(request, pk):
|
def question_json_unbased(request, pk):
|
||||||
"""
|
"""
|
||||||
No (file based) caching is enabled for unbased quesitons
|
No (file based) caching is enabled for unbased quesitons
|
||||||
@@ -1117,15 +1213,16 @@ def question_json_unbased(request, pk):
|
|||||||
question_json = question.get_question_json(based=False)
|
question_json = question.get_question_json(based=False)
|
||||||
return JsonResponse(question_json)
|
return JsonResponse(question_json)
|
||||||
|
|
||||||
|
|
||||||
def question_json(request, pk):
|
def question_json(request, pk):
|
||||||
question = get_object_or_404(Long, pk=pk)
|
question = get_object_or_404(Long, pk=pk)
|
||||||
#exam = get_object_or_404(self.Exam, pk=pk)
|
# exam = get_object_or_404(self.Exam, pk=pk)
|
||||||
|
|
||||||
#if not exam.active:
|
# if not exam.active:
|
||||||
# raise Http404("No available exam")
|
# raise Http404("No available exam")
|
||||||
|
|
||||||
path= "{0}longs/questions/{1}.json".format(settings.MEDIA_ROOT, pk)
|
path = "{0}longs/questions/{1}.json".format(settings.MEDIA_ROOT, pk)
|
||||||
url= "{0}longs/questions/{1}.json".format(settings.MEDIA_URL, pk)
|
url = "{0}longs/questions/{1}.json".format(settings.MEDIA_URL, pk)
|
||||||
|
|
||||||
if os.path.isfile(path) and not question.recreate_json:
|
if os.path.isfile(path) and not question.recreate_json:
|
||||||
return redirect(url)
|
return redirect(url)
|
||||||
@@ -1137,6 +1234,7 @@ def question_json(request, pk):
|
|||||||
|
|
||||||
return redirect(question_json)
|
return redirect(question_json)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@user_is_author_or_long_checker
|
@user_is_author_or_long_checker
|
||||||
def question_json_recreate(request, pk):
|
def question_json_recreate(request, pk):
|
||||||
@@ -1151,7 +1249,7 @@ def question_json_recreate(request, pk):
|
|||||||
exam.recreate_json = True
|
exam.recreate_json = True
|
||||||
exam.save()
|
exam.save()
|
||||||
|
|
||||||
#question.get_question_json()
|
# question.get_question_json()
|
||||||
|
|
||||||
return redirect("longs:question_detail", pk=pk)
|
return redirect("longs:question_detail", pk=pk)
|
||||||
|
|
||||||
@@ -1173,6 +1271,7 @@ class UserAnswerDelete(SuperuserRequiredMixin, DeleteView):
|
|||||||
template_name = "user_answer_delete.html"
|
template_name = "user_answer_delete.html"
|
||||||
success_url = reverse_lazy("longs:user_answer_table_view")
|
success_url = reverse_lazy("longs:user_answer_table_view")
|
||||||
|
|
||||||
|
|
||||||
@user_passes_test(lambda u: u.is_superuser)
|
@user_passes_test(lambda u: u.is_superuser)
|
||||||
def user_answer_delete_multiple(request):
|
def user_answer_delete_multiple(request):
|
||||||
if request.is_ajax():
|
if request.is_ajax():
|
||||||
@@ -1195,14 +1294,16 @@ def refresh_exam_question_json(request, pk):
|
|||||||
|
|
||||||
return redirect("longs:exam_overview", pk=pk)
|
return redirect("longs:exam_overview", pk=pk)
|
||||||
|
|
||||||
|
|
||||||
class SeriesImagesZipView(SuperuserRequiredMixin, BaseZipView):
|
class SeriesImagesZipView(SuperuserRequiredMixin, BaseZipView):
|
||||||
"""Download all images from an image series"""
|
"""Download all images from an image series"""
|
||||||
|
|
||||||
def get_files(self):
|
def get_files(self):
|
||||||
series = LongSeries.objects.get(pk=self.kwargs['pk'])
|
series = LongSeries.objects.get(pk=self.kwargs["pk"])
|
||||||
|
|
||||||
return [i.image.file for i in series.images.all()]
|
return [i.image.file for i in series.images.all()]
|
||||||
|
|
||||||
|
|
||||||
def question_review(request, pk):
|
def question_review(request, pk):
|
||||||
"""
|
"""
|
||||||
Return a json representation of the question when the exam is active
|
Return a json representation of the question when the exam is active
|
||||||
|
|||||||
Reference in New Issue
Block a user