many Longs fixes
This commit is contained in:
@@ -7,5 +7,5 @@
|
|||||||
Answer: {{ciduseranswer.answer}}<br />
|
Answer: {{ciduseranswer.answer}}<br />
|
||||||
Score: {{ciduseranswer.get_answer_score}}<br />
|
Score: {{ciduseranswer.get_answer_score}}<br />
|
||||||
<a href="{% url 'anatomy:user_answer_delete' ciduseranswer.id %}">Delete answer</a>
|
<a href="{% url 'anatomy:user_answer_delete' ciduseranswer.id %}">Delete answer</a>
|
||||||
<a href="{% url 'admin:anatomy_ciduseranswer_change' ciduseranswer.id %}">Admin Edit</a>
|
<a href="{% url 'admin:anatomy_useranswer_change' ciduseranswer.id %}">Admin Edit</a>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|||||||
+26
-5
@@ -72,9 +72,9 @@ from .models import (
|
|||||||
get_next_cid,
|
get_next_cid,
|
||||||
)
|
)
|
||||||
|
|
||||||
from rapids.models import Rapid as RapidQuestion
|
from rapids.models import Rapid as RapidQuestion, RapidImage
|
||||||
from rapids.models import Exam as RapidExam
|
from rapids.models import Exam as RapidExam
|
||||||
from longs.models import Long as LongQuestion
|
from longs.models import Long as LongQuestion, LongSeries
|
||||||
from longs.models import Exam as LongExam
|
from longs.models import Exam as LongExam
|
||||||
from anatomy.models import AnatomyQuestion as AnatomyQuestion
|
from anatomy.models import AnatomyQuestion as AnatomyQuestion
|
||||||
from anatomy.models import Exam as AnatomyExam
|
from anatomy.models import Exam as AnatomyExam
|
||||||
@@ -795,6 +795,26 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
elif self.app_name == "longs":
|
||||||
|
questions = (
|
||||||
|
exam.exam_questions.select_related()
|
||||||
|
.prefetch_related(
|
||||||
|
Prefetch(
|
||||||
|
"series",
|
||||||
|
queryset=LongSeries.objects.select_related("modality", "examination", "plane", "contrast").prefetch_related(
|
||||||
|
Prefetch("images")
|
||||||
|
),
|
||||||
|
),
|
||||||
|
"author"
|
||||||
|
#to_attr="prefetched_primary_answer"
|
||||||
|
# queryset=self.Answer.objects.filter(),
|
||||||
|
#"series",
|
||||||
|
#"abnormality",
|
||||||
|
#"region",
|
||||||
|
#"examination",
|
||||||
|
)
|
||||||
|
.all()
|
||||||
|
)
|
||||||
elif self.app_name == "rapids":
|
elif self.app_name == "rapids":
|
||||||
questions = (
|
questions = (
|
||||||
exam.exam_questions.select_related()
|
exam.exam_questions.select_related()
|
||||||
@@ -812,6 +832,7 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
"abnormality",
|
"abnormality",
|
||||||
"region",
|
"region",
|
||||||
"examination",
|
"examination",
|
||||||
|
"author"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
# questions = (
|
# questions = (
|
||||||
@@ -1868,7 +1889,7 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
user_answers_callstates = defaultdict(list)
|
user_answers_callstates = defaultdict(list)
|
||||||
user_answers_callstates_counted = {}
|
user_answers_callstates_counted = {}
|
||||||
|
|
||||||
if self.app_name in ("physics", "sbas"):
|
if self.app_name in ("physics", "sbas", "longs"):
|
||||||
cached_scores = False
|
cached_scores = False
|
||||||
questions = exam.exam_questions.all()
|
questions = exam.exam_questions.all()
|
||||||
else:
|
else:
|
||||||
@@ -1932,7 +1953,7 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
callstate = s.get_answer_callstate()
|
callstate = s.get_answer_callstate()
|
||||||
by_question[q][cid] = (ans, answer_score, callstate)
|
by_question[q][cid] = (ans, answer_score, callstate)
|
||||||
user_answers_callstates[cid].append(callstate)
|
user_answers_callstates[cid].append(callstate)
|
||||||
elif self.app_name in ("rapids", "anatomy", "sbas"):
|
elif self.app_name in ("anatomy", "sbas", "longs"):
|
||||||
by_question[q][cid] = (ans, answer_score)
|
by_question[q][cid] = (ans, answer_score)
|
||||||
else:
|
else:
|
||||||
zipped_ans_scores = zip(ans, answer_score)
|
zipped_ans_scores = zip(ans, answer_score)
|
||||||
@@ -1942,7 +1963,7 @@ class ExamViews(View, LoginRequiredMixin):
|
|||||||
user_scores_normalised = {}
|
user_scores_normalised = {}
|
||||||
user_answer_count = {}
|
user_answer_count = {}
|
||||||
for user in user_answers_marks:
|
for user in user_answers_marks:
|
||||||
if self.app_name in ("rapids", "anatomy", "sbas"):
|
if self.app_name in ("rapids", "anatomy", "sbas", "longs"):
|
||||||
user_scores[user] = sum(
|
user_scores[user] = sum(
|
||||||
[i for i in user_answers_marks[user] if i != "unmarked"]
|
[i for i in user_answers_marks[user] if i != "unmarked"]
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
from django.forms import (
|
from django.forms import (
|
||||||
|
BaseInlineFormSet,
|
||||||
Form,
|
Form,
|
||||||
ModelForm,
|
ModelForm,
|
||||||
ModelMultipleChoiceField,
|
ModelMultipleChoiceField,
|
||||||
@@ -20,6 +21,9 @@ from longs.models import (
|
|||||||
Exam,
|
Exam,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from django.db.models import Prefetch
|
||||||
|
|
||||||
|
|
||||||
from anatomy.models import Modality
|
from anatomy.models import Modality
|
||||||
|
|
||||||
from generic.models import Examination#, Condition, Sign
|
from generic.models import Examination#, Condition, Sign
|
||||||
@@ -174,9 +178,11 @@ class LongForm(ModelForm):
|
|||||||
# a list of primary key for the selected data.
|
# a list of primary key for the selected data.
|
||||||
initial["exams"] = [t.pk for t in kwargs["instance"].exams.all()]
|
initial["exams"] = [t.pk for t in kwargs["instance"].exams.all()]
|
||||||
|
|
||||||
|
|
||||||
ModelForm.__init__(self, *args, **kwargs)
|
ModelForm.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
super(LongForm, self).__init__(*args, **kwargs)
|
super(LongForm, self).__init__(*args, **kwargs)
|
||||||
|
print(self.fields)
|
||||||
|
|
||||||
if self.user.groups.filter(name="long_checker").exists():
|
if self.user.groups.filter(name="long_checker").exists():
|
||||||
exam_queryset = Exam.objects.all()
|
exam_queryset = Exam.objects.all()
|
||||||
@@ -245,14 +251,44 @@ class LongForm(ModelForm):
|
|||||||
|
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
#class CustomInlineFormSet(BaseInlineFormSet):
|
||||||
|
# def get_queryset(self):
|
||||||
|
# if not hasattr(self, '_queryset'):
|
||||||
|
# qs = super(CustomInlineFormSet, self).get_queryset()#.values()
|
||||||
|
# self._queryset = qs
|
||||||
|
# return self._queryset
|
||||||
|
|
||||||
|
#class LongForm(ModelForm):
|
||||||
|
class CaseSeriesForm(ModelForm):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
|
||||||
|
super(CaseSeriesForm, self).__init__(*args, **kwargs)
|
||||||
|
ModelForm.__init__(self, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
# We can limit the series available to choose from here
|
||||||
|
# as well a dramatically reducing the sql queries
|
||||||
|
queryset = LongSeries.objects.select_related("modality", "examination", "plane").prefetch_related("long").all()#.filter(pk=62)#.values()
|
||||||
|
self.fields["longseries"] = ModelChoiceField(queryset=queryset)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = LongSeries.long.through
|
||||||
|
#fields = [ modality ]
|
||||||
|
#exclude = []
|
||||||
|
fields = ["longseries", "sort_value"]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
SeriesFormSet = inlineformset_factory(
|
SeriesFormSet = inlineformset_factory(
|
||||||
Long,
|
Long,
|
||||||
LongSeries.long.through,
|
LongSeries.long.through,
|
||||||
|
form=CaseSeriesForm,
|
||||||
exclude=[],
|
exclude=[],
|
||||||
can_delete=True,
|
can_delete=True,
|
||||||
extra=0,
|
extra=0,
|
||||||
max_num=10,
|
max_num=10,
|
||||||
|
#formset=CustomInlineFormSet
|
||||||
)
|
)
|
||||||
|
|
||||||
LongSeriesImageFormSet = inlineformset_factory(
|
LongSeriesImageFormSet = inlineformset_factory(
|
||||||
|
|||||||
+36
-20
@@ -32,8 +32,8 @@ from generic.models import (
|
|||||||
CidUserGroup,
|
CidUserGroup,
|
||||||
ExamUserStatus,
|
ExamUserStatus,
|
||||||
Examination,
|
Examination,
|
||||||
#Condition,
|
# Condition,
|
||||||
#Sign,
|
# Sign,
|
||||||
ExamBase,
|
ExamBase,
|
||||||
Plane,
|
Plane,
|
||||||
Contrast,
|
Contrast,
|
||||||
@@ -90,15 +90,15 @@ class Long(models.Model):
|
|||||||
feedback = models.TextField(null=True, blank=True)
|
feedback = models.TextField(null=True, blank=True)
|
||||||
|
|
||||||
# TODO: merge with atlas condition / signs / finding
|
# TODO: merge with atlas condition / signs / finding
|
||||||
#condition = tagulous.models.TagField(
|
# condition = tagulous.models.TagField(
|
||||||
# to=Condition,
|
# to=Condition,
|
||||||
# blank=True,
|
# blank=True,
|
||||||
# help_text='Associated condition. Will allow searching / filtering and tips / hints to be displayed. Conditions with spaces must be enclosed in quotes "..."',
|
# help_text='Associated condition. Will allow searching / filtering and tips / hints to be displayed. Conditions with spaces must be enclosed in quotes "..."',
|
||||||
#)
|
# )
|
||||||
|
|
||||||
#sign = tagulous.models.TagField(
|
# sign = tagulous.models.TagField(
|
||||||
# to=Sign, blank=True, help_text="Radiological signs in the question"
|
# to=Sign, blank=True, help_text="Radiological signs in the question"
|
||||||
#)
|
# )
|
||||||
|
|
||||||
# Model answers
|
# Model answers
|
||||||
model_observations = models.TextField(null=True, blank=True)
|
model_observations = models.TextField(null=True, blank=True)
|
||||||
@@ -224,16 +224,17 @@ class Long(models.Model):
|
|||||||
"management": self.model_management,
|
"management": self.model_management,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def get_question_json(
|
||||||
def get_question_json(self, based: bool=True, answers: bool=True, feedback: bool=False):
|
self, based: bool = True, answers: bool = True, feedback: bool = False
|
||||||
|
):
|
||||||
"""Returns a json representation of the question
|
"""Returns a json representation of the question
|
||||||
|
|
||||||
if based = False return is a dict
|
if based = False return is a dict
|
||||||
otherwise a url to the json file
|
otherwise a url to the json file
|
||||||
|
|
||||||
answers arg is only parsed if based = False
|
answers arg is only parsed if based = False
|
||||||
"""
|
"""
|
||||||
|
|
||||||
question_id = self.pk
|
question_id = self.pk
|
||||||
|
|
||||||
if not based:
|
if not based:
|
||||||
@@ -285,7 +286,6 @@ class Long(models.Model):
|
|||||||
Path(path).parents[0].mkdir(parents=True, exist_ok=True)
|
Path(path).parents[0].mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
with open(path, "w+") as f:
|
with open(path, "w+") as f:
|
||||||
|
|
||||||
print("start writing file")
|
print("start writing file")
|
||||||
# We manually create the json for long questions to reducem memroy usade
|
# We manually create the json for long questions to reducem memroy usade
|
||||||
f.write(
|
f.write(
|
||||||
@@ -431,13 +431,13 @@ class LongSeries(models.Model):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
if self.long:
|
#if self.long:
|
||||||
long_id = ", ".format([long.pk for long in self.long.all()])
|
# long_id = ", ".format([long.pk for long in self.long.all()])
|
||||||
# long_id = self.long.pk
|
# # long_id = self.long.pk
|
||||||
else:
|
#else:
|
||||||
long_id = "None"
|
# long_id = "None"
|
||||||
return "{}/{} : {} [{}]".format(
|
return "{}/{} : {}".format(
|
||||||
self.pk, self.get_examination(), self.description, long_id
|
self.pk, self.get_examination(), self.description
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_author_objects(self):
|
def get_author_objects(self):
|
||||||
@@ -695,7 +695,6 @@ class Exam(ExamBase):
|
|||||||
for q in questions:
|
for q in questions:
|
||||||
exam_order.append(q.id)
|
exam_order.append(q.id)
|
||||||
if based:
|
if based:
|
||||||
|
|
||||||
# If it is a new question we need to for a question json refresh
|
# If it is a new question we need to for a question json refresh
|
||||||
if q.json_creation_time is None:
|
if q.json_creation_time is None:
|
||||||
q.get_question_json()
|
q.get_question_json()
|
||||||
@@ -755,6 +754,7 @@ class Exam(ExamBase):
|
|||||||
@reversion.register
|
@reversion.register
|
||||||
class UserAnswer(UserAnswerBase):
|
class UserAnswer(UserAnswerBase):
|
||||||
"""User answers by candidate"""
|
"""User answers by candidate"""
|
||||||
|
|
||||||
app_name = "longs"
|
app_name = "longs"
|
||||||
|
|
||||||
question = models.ForeignKey(
|
question = models.ForeignKey(
|
||||||
@@ -869,7 +869,7 @@ class UserAnswer(UserAnswerBase):
|
|||||||
|
|
||||||
def get_answer_score(self):
|
def get_answer_score(self):
|
||||||
if self.score == "":
|
if self.score == "":
|
||||||
return ""
|
return "unmarked"
|
||||||
return float(self.score)
|
return float(self.score)
|
||||||
|
|
||||||
def discrepant_answers(self):
|
def discrepant_answers(self):
|
||||||
@@ -878,7 +878,23 @@ class UserAnswer(UserAnswerBase):
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def get_answer_string(self) -> str:
|
||||||
|
return f"""
|
||||||
|
Observations:
|
||||||
|
{self.answer_observations},
|
||||||
|
|
||||||
|
Interpretation:
|
||||||
|
{self.answer_interpretation},
|
||||||
|
|
||||||
|
Principle Diagnosis:
|
||||||
|
{self.answer_principle_diagnosis},
|
||||||
|
|
||||||
|
Differential:
|
||||||
|
{self.answer_differential_diagnosis},
|
||||||
|
|
||||||
|
Management:
|
||||||
|
{self.answer_management},
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
@reversion.register
|
@reversion.register
|
||||||
|
|||||||
@@ -1,4 +1,27 @@
|
|||||||
{% extends 'longs/exams.html' %}
|
|
||||||
|
{% extends 'generic/exam_scores_base.html' %}
|
||||||
|
|
||||||
|
{% block table_answers %}
|
||||||
|
{% for question in questions %}
|
||||||
|
<tr>
|
||||||
|
<td><a href="{% url 'anatomy:mark' exam_pk=exam.pk sk=forloop.counter0 %}">Question {{forloop.counter}}</a>
|
||||||
|
</td>
|
||||||
|
{% comment %} {% for cid in cids %}
|
||||||
|
<td class="anatomy-ans user-answer-score-{{score_by_question|get_item:question|get_item:cid}}" title="answer score: {{score_by_question|get_item:question|get_item:cid}}">{{ans_by_question|get_item:question|get_item:cid}}</td>
|
||||||
|
{% endfor %} {% endcomment %}
|
||||||
|
{% for cid in cids %}
|
||||||
|
{% with by_question|get_item:question|get_item:cid as ans_score %}
|
||||||
|
<td class="longs-ans user-answer-score-{{ans_score.1}}" title="{{ans_score.0}}">{{ans_score.1}}</td>
|
||||||
|
{% endwith %}
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
{% endblock table_answers %}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{% comment %} {% extends 'longs/exams.html' %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="longs">
|
<div class="longs">
|
||||||
@@ -8,7 +31,7 @@
|
|||||||
<div class="alert alert-warning" role="alert">
|
<div class="alert alert-warning" role="alert">
|
||||||
The following questions need marking
|
The following questions need marking
|
||||||
{% for exam_index in unmarked %}
|
{% for exam_index in unmarked %}
|
||||||
<a href="{% url 'longs:mark_question_overview' exam.pk exam_index %}">{{ exam_index|add:1 }}</a>
|
<a href="{% url 'longs:mark' exam.pk exam_index %}">{{ exam_index|add:1 }}</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -53,7 +76,7 @@
|
|||||||
</thead>
|
</thead>
|
||||||
{% for question in questions %}
|
{% for question in questions %}
|
||||||
<tr>
|
<tr>
|
||||||
<td><a href="{% url 'longs:mark_question_overview' exam_id=exam.pk sk=forloop.counter0 %}">Question
|
<td><a href="{% url 'longs:mark' exam_id=exam.pk sk=forloop.counter0 %}">Question
|
||||||
{{forloop.counter}}</a></td>
|
{{forloop.counter}}</a></td>
|
||||||
{% for ans, score in by_question|get_item:question %}
|
{% for ans, score in by_question|get_item:question %}
|
||||||
<td class="user-answer-score-{{score}}" title="answer score: {{score}}">{{score}}</td>
|
<td class="user-answer-score-{{score}}" title="answer score: {{score}}">{{score}}</td>
|
||||||
@@ -69,4 +92,4 @@
|
|||||||
</table>
|
</table>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %} {% endcomment %}
|
||||||
@@ -5,11 +5,11 @@
|
|||||||
<a href="{% url 'longs:long_update' question.id %}" title="Edit the Question">Edit</a>
|
<a href="{% url 'longs:long_update' question.id %}" title="Edit the Question">Edit</a>
|
||||||
{% if request.user.is_superuser %}
|
{% if request.user.is_superuser %}
|
||||||
<a href="{% url 'admin:longs_long_change' question.id %}" title="Edit the Question using the admin interface">Admin
|
<a href="{% url 'admin:longs_long_change' question.id %}" title="Edit the Question using the admin interface">Admin
|
||||||
Edit</a> <a href="{% url 'admin:longs_ciduseranswer_change' answer.id %}"
|
Edit</a> <a href="{% url 'admin:longs_useranswer_change' answer.id %}"
|
||||||
title="Edit the user answer using the admin interface">Admin
|
title="Edit the user answer using the admin interface">Admin
|
||||||
Edit (user answer)</a>
|
Edit (user answer)</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<h2>Marking question <a href="{% url 'longs:mark_question_overview' exam.id question_details.current|add:'-1' %}"
|
<h2>Marking question <a href="{% url 'longs:mark' exam.id question_details.current|add:'-1' %}"
|
||||||
title="View question answers">{{question_details.current}}</a> of {{question_details.total}}</h2>
|
title="View question answers">{{question_details.current}}</a> of {{question_details.total}}</h2>
|
||||||
|
|
||||||
{% if discrepancy_form %}
|
{% if discrepancy_form %}
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
{% if not next_unmarked_id and not unmarked %}
|
{% if not next_unmarked_id and not unmarked %}
|
||||||
<div class="alert alert-info sticky-alert" role="alert">Success! Marking question complete. <br />
|
<div class="alert alert-info sticky-alert" role="alert">Success! Marking question complete. <br />
|
||||||
<a href="{% url 'longs:mark_answer_override' exam_id=exam.pk question_number=question_details.current|add:'-1' answer_id=answer.id %}">Set final score</a><br/>
|
<a href="{% url 'longs:mark_answer_override' exam_id=exam.pk question_number=question_details.current|add:'-1' answer_id=answer.id %}">Set final score</a><br/>
|
||||||
Return to <a href="{% url 'longs:mark_question_overview' exam.id question_details.current|add:'-1' %}">question
|
Return to <a href="{% url 'longs:mark' exam.id question_details.current|add:'-1' %}">question
|
||||||
overview</a>, <a href="{% url 'longs:mark_overview' pk=exam.pk %}">marking
|
overview</a>, <a href="{% url 'longs:mark_overview' pk=exam.pk %}">marking
|
||||||
overview</a><br />
|
overview</a><br />
|
||||||
{% if previous_answer_id %}
|
{% if previous_answer_id %}
|
||||||
|
|||||||
@@ -8,13 +8,13 @@
|
|||||||
<div>
|
<div>
|
||||||
<button class="show-all-button">Show all</button><button class="show-unmarked-button">Show unmarked</button>
|
<button class="show-all-button">Show all</button><button class="show-unmarked-button">Show unmarked</button>
|
||||||
</div>
|
</div>
|
||||||
<div id="stark-marking-button"><a href="{% url 'longs:mark_question_overview' exam_id=exam.pk sk=0 %}"><button>Click here to start
|
<div id="stark-marking-button"><a href="{% url 'longs:mark' exam_id=exam.pk sk=0 %}"><button>Click here to start
|
||||||
marking</button></a></div>
|
marking</button></a></div>
|
||||||
|
|
||||||
<ul id="question-mark-list">
|
<ul id="question-mark-list">
|
||||||
{% for question, unmarked_count, unmarked_count_marker in question_unmarked_map %}
|
{% for question, unmarked_count, unmarked_count_marker in question_unmarked_map %}
|
||||||
<li data-markcount={{unmarked_count}} {% if unmarked_count %}class="unmarked" {% endif %}><a
|
<li data-markcount={{unmarked_count}} {% if unmarked_count %}class="unmarked" {% endif %}><a
|
||||||
href="{% url 'longs:mark_question_overview' exam_id=exam.pk sk=forloop.counter0 %}">Question {{forloop.counter }}:
|
href="{% url 'longs:mark' exam_id=exam.pk sk=forloop.counter0 %}">Question {{forloop.counter }}:
|
||||||
{{ question }}</a><br /> Answers incompletely marked: {{unmarked_count}}
|
{{ question }}</a><br /> Answers incompletely marked: {{unmarked_count}}
|
||||||
{% if exam.double_mark %}
|
{% if exam.double_mark %}
|
||||||
<br/>
|
<br/>
|
||||||
|
|||||||
@@ -32,12 +32,12 @@
|
|||||||
</ul>
|
</ul>
|
||||||
<div>
|
<div>
|
||||||
{% if question_details.current > 1 %}
|
{% if question_details.current > 1 %}
|
||||||
<a href="{% url 'longs:mark_question_overview' exam.id question_details.current|add:'-2' %}"
|
<a href="{% url 'longs:mark' exam.id question_details.current|add:'-2' %}"
|
||||||
title="Previous question">Previous</a>
|
title="Previous question">Previous</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if question_details.current >= question_details.total %}
|
{% if question_details.current >= question_details.total %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="{% url 'longs:mark_question_overview' exam.id question_details.current %}" title="Next question">Next</a>
|
<a href="{% url 'longs:mark' exam.id question_details.current %}" title="Next question">Next</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -32,12 +32,12 @@
|
|||||||
</ul>
|
</ul>
|
||||||
<div>
|
<div>
|
||||||
{% if question_details.current > 1 %}
|
{% if question_details.current > 1 %}
|
||||||
<a href="{% url 'longs:mark_question_overview' exam.id question_details.current|add:'-2' %}"
|
<a href="{% url 'longs:mark' exam.id question_details.current|add:'-2' %}"
|
||||||
title="Previous question">Previous</a>
|
title="Previous question">Previous</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if question_details.current >= question_details.total %}
|
{% if question_details.current >= question_details.total %}
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="{% url 'longs:mark_question_overview' exam.id question_details.current %}" title="Next question">Next</a>
|
<a href="{% url 'longs:mark' exam.id question_details.current %}" title="Next question">Next</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+8
-8
@@ -79,8 +79,8 @@ urlpatterns = [
|
|||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"exam/<int:exam_id>/<int:sk>/mark",
|
"exam/<int:exam_id>/<int:sk>/mark",
|
||||||
views.mark_question_overview,
|
views.mark,
|
||||||
name="mark_question_overview",
|
name="mark",
|
||||||
),
|
),
|
||||||
path("exam/<int:pk>/authors", views.ExamAuthorUpdate.as_view(), name="exam_authors"),
|
path("exam/<int:pk>/authors", views.ExamAuthorUpdate.as_view(), name="exam_authors"),
|
||||||
path(
|
path(
|
||||||
@@ -88,12 +88,12 @@ urlpatterns = [
|
|||||||
views.refresh_exam_question_json,
|
views.refresh_exam_question_json,
|
||||||
name="refresh_exam_question_json",
|
name="refresh_exam_question_json",
|
||||||
),
|
),
|
||||||
path("exam/<int:pk>/scores", views.exam_scores_all, name="exam_scores_all"),
|
#path("exam/<int:pk>/scores", views.exam_scores_all, name="exam_scores_all"),
|
||||||
path(
|
# path(
|
||||||
"exam/<int:pk>/scores/<int:cid>/<str:passcode>/",
|
# "exam/<int:pk>/scores/<int:cid>/<str:passcode>/",
|
||||||
views.exam_scores_cid_user,
|
# views.exam_scores_cid_user,
|
||||||
name="exam_scores_cid_user",
|
# name="exam_scores_cid_user",
|
||||||
),
|
# ),
|
||||||
path("exam/create", views.ExamCreate.as_view(), name="exam_create"),
|
path("exam/create", views.ExamCreate.as_view(), name="exam_create"),
|
||||||
path("exam/<int:exam_id>/clone", views.ExamClone.as_view(), name="exam_clone"),
|
path("exam/<int:exam_id>/clone", views.ExamClone.as_view(), name="exam_clone"),
|
||||||
path("exam/<int:pk>/update", views.ExamUpdate.as_view(), name="exam_update"),
|
path("exam/<int:pk>/update", views.ExamUpdate.as_view(), name="exam_update"),
|
||||||
|
|||||||
+117
-112
@@ -409,13 +409,18 @@ class LongCreateBase(RevisionMixin, LoginRequiredMixin, CreateView):
|
|||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super(LongCreateBase, self).get_context_data(**kwargs)
|
context = super(LongCreateBase, self).get_context_data(**kwargs)
|
||||||
|
|
||||||
|
queryset = LongSeries.objects.select_related("modality", "examination", "plane").prefetch_related("long").filter(pk=62)#.values()
|
||||||
|
print("TEST")
|
||||||
|
print(queryset)
|
||||||
|
#queryset = LongSeries.objects.all()
|
||||||
if self.request.POST:
|
if self.request.POST:
|
||||||
context["series_formset"] = SeriesFormSet(
|
context["series_formset"] = SeriesFormSet(
|
||||||
self.request.POST, self.request.FILES
|
self.request.POST, self.request.FILES, queryset=queryset
|
||||||
)
|
)
|
||||||
context["series_formset"].full_clean()
|
context["series_formset"].full_clean()
|
||||||
else:
|
else:
|
||||||
context["series_formset"] = SeriesFormSet()
|
context["series_formset"] = SeriesFormSet(queryset=queryset)
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
@@ -713,7 +718,7 @@ def mark_answer(request, exam_id, question_number, answer_id, override=False):
|
|||||||
answer_id=answer_id,
|
answer_id=answer_id,
|
||||||
)
|
)
|
||||||
# elif "previous" in request.POST:
|
# elif "previous" in request.POST:
|
||||||
# return redirect("longs:mark_question_overview", pk=exam_id, sk=n - 1)
|
# return redirect("longs:mark", pk=exam_id, sk=n - 1)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
form = MarkLongQuestionSingleForm(initial={"score": answer.score})
|
form = MarkLongQuestionSingleForm(initial={"score": answer.score})
|
||||||
@@ -763,7 +768,7 @@ def mark_answer(request, exam_id, question_number, answer_id, override=False):
|
|||||||
answer_id=answer_id,
|
answer_id=answer_id,
|
||||||
)
|
)
|
||||||
# elif "previous" in request.POST:
|
# elif "previous" in request.POST:
|
||||||
# return redirect("longs:mark_question_overview", pk=exam_id, sk=n - 1)
|
# return redirect("longs:mark", pk=exam_id, sk=n - 1)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
@@ -811,7 +816,7 @@ def mark_answer(request, exam_id, question_number, answer_id, 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
|
||||||
def mark_question_overview(request, exam_id, sk):
|
def mark(request, exam_id, sk):
|
||||||
exam = get_object_or_404(Exam, pk=exam_id)
|
exam = get_object_or_404(Exam, pk=exam_id)
|
||||||
|
|
||||||
questions = exam.exam_questions.all()
|
questions = exam.exam_questions.all()
|
||||||
@@ -864,113 +869,113 @@ def mark_question_overview(request, exam_id, sk):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
#@login_required
|
||||||
@user_is_long_marker
|
#@user_is_long_marker
|
||||||
def exam_scores_all(request, pk):
|
#def exam_scores_all(request, pk):
|
||||||
exam = get_object_or_404(Exam, pk=pk)
|
# exam = get_object_or_404(Exam, pk=pk)
|
||||||
|
#
|
||||||
questions = exam.exam_questions.all()
|
# questions = exam.exam_questions.all()
|
||||||
|
#
|
||||||
cids = (
|
# cids = (
|
||||||
UserAnswer.objects.filter(question__in=questions, exam__id=pk)
|
# UserAnswer.objects.filter(question__in=questions, exam__id=pk)
|
||||||
.order_by("cid")
|
# .order_by("cid")
|
||||||
.values_list("cid", flat=True)
|
# .values_list("cid", flat=True)
|
||||||
.distinct()
|
# .distinct()
|
||||||
)
|
# )
|
||||||
|
#
|
||||||
user_answers_and_marks = defaultdict(list)
|
# user_answers_and_marks = defaultdict(list)
|
||||||
user_answers_marks = defaultdict(list)
|
# user_answers_marks = defaultdict(list)
|
||||||
user_answers = defaultdict(list)
|
# user_answers = defaultdict(list)
|
||||||
user_names = {}
|
# user_names = {}
|
||||||
|
#
|
||||||
by_question = defaultdict(list)
|
# by_question = defaultdict(list)
|
||||||
unmarked = set()
|
# unmarked = set()
|
||||||
|
#
|
||||||
# Loop through all candidates
|
# # Loop through all candidates
|
||||||
for cid in cids:
|
# for cid in cids:
|
||||||
# Convoluted (probably...)
|
# # Convoluted (probably...)
|
||||||
user_names[cid] = cid
|
# user_names[cid] = cid
|
||||||
for q in questions:
|
# for q in questions:
|
||||||
# Get user answer
|
# # Get user answer
|
||||||
s = q.cid_user_answers.filter(cid=cid, exam__id=pk).first()
|
# s = q.cid_user_answers.filter(cid=cid, exam__id=pk).first()
|
||||||
|
#
|
||||||
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:
|
||||||
ans = ""
|
# ans = ""
|
||||||
answer_score = s.get_answer_score()
|
# answer_score = s.get_answer_score()
|
||||||
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([i for i in user_answers_marks[user] if i != ""])
|
# user_scores[user] = sum([i for i in user_answers_marks[user] if i != ""])
|
||||||
user_scores_normalised[user] = normaliseScore(
|
# user_scores_normalised[user] = normaliseScore(
|
||||||
sum([i for i in user_answers_marks[user] if i != ""])
|
# 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())
|
||||||
|
#
|
||||||
if len(user_scores_list) < 1:
|
# if len(user_scores_list) < 1:
|
||||||
mean = 0
|
# mean = 0
|
||||||
median = 0
|
# median = 0
|
||||||
mode = 0
|
# mode = 0
|
||||||
fig_html = ""
|
# fig_html = ""
|
||||||
else:
|
# else:
|
||||||
mean = statistics.mean(user_scores_list)
|
# mean = statistics.mean(user_scores_list)
|
||||||
median = statistics.median(user_scores_list)
|
# median = statistics.median(user_scores_list)
|
||||||
try:
|
# try:
|
||||||
mode = statistics.mode(user_scores_list)
|
# mode = statistics.mode(user_scores_list)
|
||||||
except statistics.StatisticsError:
|
# except statistics.StatisticsError:
|
||||||
mode = "No unique mode"
|
# mode = "No unique mode"
|
||||||
|
#
|
||||||
df = user_scores_list
|
# df = user_scores_list
|
||||||
fig = px.histogram(
|
# fig = px.histogram(
|
||||||
df,
|
# df,
|
||||||
x=0,
|
# x=0,
|
||||||
title="{}: distribution of scores".format(exam),
|
# title="{}: distribution of scores".format(exam),
|
||||||
labels={"0": "Score"},
|
# labels={"0": "Score"},
|
||||||
height=400,
|
# height=400,
|
||||||
width=600,
|
# width=600,
|
||||||
)
|
# )
|
||||||
fig_html = fig.to_html()
|
# fig_html = fig.to_html()
|
||||||
|
#
|
||||||
max_score = len(questions) * 2
|
# max_score = len(questions) * 2
|
||||||
|
#
|
||||||
return render(
|
# return render(
|
||||||
request,
|
# request,
|
||||||
"longs/exam_scores.html",
|
# "longs/exam_scores.html",
|
||||||
{
|
# {
|
||||||
"cids": cids,
|
# "cids": cids,
|
||||||
"exam": exam,
|
# "exam": exam,
|
||||||
"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,
|
||||||
"mode": mode,
|
# "mode": mode,
|
||||||
"plot": fig_html,
|
# "plot": fig_html,
|
||||||
},
|
# },
|
||||||
)
|
# )
|
||||||
|
|
||||||
|
|
||||||
def exam_scores_cid_user(request, pk, cid, passcode):
|
def exam_scores_cid_user(request, pk, cid, passcode):
|
||||||
|
|||||||
Reference in New Issue
Block a user