Numerous improvements to case questions
This commit is contained in:
+32
-13
@@ -521,9 +521,9 @@ SeriesImageFormSet = inlineformset_factory(
|
||||
|
||||
class JsonAnswerForm(Form):
|
||||
json_answer = JSONSchemaField(
|
||||
schema = "schema/report.json",
|
||||
schema = {},
|
||||
#options = 'schema/options.json'
|
||||
options = {}
|
||||
options = 'schema/options.json'
|
||||
)
|
||||
class Meta:
|
||||
fields = ["json_answer"]
|
||||
@@ -531,16 +531,11 @@ class JsonAnswerForm(Form):
|
||||
def __init__(self, *args, case_detail, **kwargs):
|
||||
super(JsonAnswerForm, self).__init__(*args, **kwargs)
|
||||
if case_detail.question_schema is not None:
|
||||
self.fields["json_answer"] = JSONSchemaField(schema=case_detail.question_schema, options={})
|
||||
self.fields["json_answer"] = JSONSchemaField(schema=case_detail.question_schema, options='schema/options.json')
|
||||
|
||||
class BaseReportAnswerForm(ModelForm):
|
||||
json_answer = JSONSchemaField(
|
||||
schema = "schema/report.json",
|
||||
#options = 'schema/options.json'
|
||||
options = {}
|
||||
)
|
||||
class Meta:
|
||||
fields = ["answer", "json_answer"]
|
||||
fields = ["answer"]
|
||||
|
||||
widgets = {
|
||||
# "normal": RadioSelect(
|
||||
@@ -548,7 +543,7 @@ class BaseReportAnswerForm(ModelForm):
|
||||
# (False, 'No')])
|
||||
# "findings": TinyMCE(attrs={"cols": 80, "rows": 20}),
|
||||
# "mark_scheme": TinyMCE(attrs={"cols": 80, "rows": 30}),
|
||||
"answer": Textarea(attrs={"cols": 100, "rows": 5}),
|
||||
"answer": Textarea(attrs={"cols": 100, "rows": 5, "placeholder":"Report text"}),
|
||||
}
|
||||
|
||||
# help_texts = {
|
||||
@@ -558,9 +553,26 @@ class BaseReportAnswerForm(ModelForm):
|
||||
def __init__(self, *args, case_detail, **kwargs):
|
||||
super(BaseReportAnswerForm, self).__init__(*args, **kwargs)
|
||||
self.fields["answer"].required = False
|
||||
|
||||
class BaseQuestionAnswerForm(ModelForm):
|
||||
json_answer = JSONSchemaField(
|
||||
schema = {},
|
||||
options = 'schema/options.json'
|
||||
)
|
||||
class Meta:
|
||||
fields = ["json_answer"]
|
||||
labels = {"json_answer": ""}
|
||||
|
||||
# help_texts = {
|
||||
# "answer": "Write your answer in here."
|
||||
# }
|
||||
|
||||
def __init__(self, *args, case_detail, **kwargs):
|
||||
super(BaseQuestionAnswerForm, self).__init__(*args, **kwargs)
|
||||
#self.fields["json_answer"].schema = case_detail.question_schema
|
||||
if case_detail.question_schema is not None:
|
||||
self.fields["json_answer"] = JSONSchemaField(schema=case_detail.question_schema, options={})
|
||||
self.fields["json_answer"] = JSONSchemaField(schema=case_detail.question_schema, options="schema/options.json")
|
||||
self.fields["json_answer"].label = ""
|
||||
|
||||
|
||||
class CidReportAnswerForm(BaseReportAnswerForm):
|
||||
@@ -571,6 +583,14 @@ class UserReportAnswerForm(BaseReportAnswerForm):
|
||||
class Meta(BaseReportAnswerForm.Meta):
|
||||
model = UserReportAnswer
|
||||
|
||||
class CidQuestionAnswerForm(BaseQuestionAnswerForm):
|
||||
class Meta(BaseQuestionAnswerForm.Meta):
|
||||
model = CidReportAnswer
|
||||
|
||||
class UserQuestionAnswerForm(BaseQuestionAnswerForm):
|
||||
class Meta(BaseQuestionAnswerForm.Meta):
|
||||
model = UserReportAnswer
|
||||
|
||||
class CidReportAnswerMarkForm(ModelForm):
|
||||
class Meta:
|
||||
model = CidReportAnswer
|
||||
@@ -732,8 +752,7 @@ class AnswerJSONForm(Form):
|
||||
|
||||
json = JSONSchemaField(
|
||||
schema = 'schema/schema.json',
|
||||
#options = 'schema/options.json'
|
||||
options = {}
|
||||
options = 'schema/options.json'
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
# Generated by Django 5.0.2 on 2024-06-10 22:07
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('atlas', '0053_casedetail_question_answers'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='casecollection',
|
||||
name='feedback_once_collection_complete',
|
||||
field=models.BooleanField(default=True, help_text='If true feedback is only given once the collection is complete. If false feedback is given after each case.'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='casecollection',
|
||||
name='collection_type',
|
||||
field=models.CharField(choices=[('REV', 'Review'), ('REP', 'Report'), ('QUE', 'Question'), ('VIV', 'Viva')], default='REP', max_length=3),
|
||||
),
|
||||
]
|
||||
@@ -774,6 +774,8 @@ class CaseCollection(ExamOrCollectionGenericBase):
|
||||
help_text="If true allows users self complete and review cases in a self directed way.",
|
||||
)
|
||||
|
||||
feedback_once_collection_complete = models.BooleanField(default=True, help_text="If true feedback is only given once the collection is complete. If false feedback is given after each case.")
|
||||
|
||||
class COLLECTION_TYPE_CHOICES(models.TextChoices):
|
||||
REVIEW = (
|
||||
"REV",
|
||||
@@ -783,6 +785,10 @@ class CaseCollection(ExamOrCollectionGenericBase):
|
||||
"REP",
|
||||
_("Report"),
|
||||
)
|
||||
QUESTION = (
|
||||
"QUE",
|
||||
_("Question"),
|
||||
)
|
||||
VIVA = (
|
||||
"VIV",
|
||||
_("Viva"),
|
||||
@@ -874,6 +880,33 @@ class CaseCollection(ExamOrCollectionGenericBase):
|
||||
"""Returns the cases in the collection in order of the CaseDetail sort_order"""
|
||||
return self.cases.all().order_by("casedetail__sort_order")
|
||||
|
||||
def get_next_case(self, case):
|
||||
cases = list(self.get_cases())
|
||||
|
||||
try:
|
||||
return cases[cases.index(case) + 1]
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
def get_previous_case(self, case):
|
||||
cases = list(self.get_cases())
|
||||
|
||||
new_index = cases.index(case) - 1
|
||||
|
||||
if new_index < 0:
|
||||
return None
|
||||
else:
|
||||
return cases[new_index]
|
||||
|
||||
|
||||
def get_index_of_case(self, case, case_count=False):
|
||||
cases = list(self.get_cases())
|
||||
|
||||
if case_count:
|
||||
return cases.index(case), len(cases)
|
||||
else:
|
||||
return cases.index(case)
|
||||
|
||||
def get_case_by_index(
|
||||
self, case_index: int, case_count=False
|
||||
) -> Case | Tuple[Case, int]:
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"disable_edit_json": true,
|
||||
"disable_collapse": true,
|
||||
"disable_properties": true
|
||||
}
|
||||
@@ -3,14 +3,25 @@
|
||||
|
||||
|
||||
{% block content %}
|
||||
<h2>{{case_detail.collection.name}} / {{case_detail.case.pk}}</h2>
|
||||
|
||||
<div>
|
||||
|
||||
<p>Collection: <a href="{% url 'atlas:collection_detail' case_detail.collection.pk %}">{{case_detail.collection.name}}</a>, Case: <a href="{% url 'atlas:case_detail' case_detail.case.pk %}">{{case_detail.case.title}}</a></p>
|
||||
{% if previous %}
|
||||
<a href="{% url 'atlas:collection_case_details' collection.id previous.id %}">Previous question</a>
|
||||
{% endif %}
|
||||
Viewing question as part of collection: <a href="{% url 'atlas:collection_detail' collection.id %}">{{collection.name}}</a> [{{case_number|add:1}}/{{collection_length}}]
|
||||
{% if next %}
|
||||
<a href="{% url 'atlas:collection_case_details' collection.id next.id %}">Next question</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
|
||||
<h2>Case: <a href="{% url 'atlas:case_detail' case_detail.case.pk %}">{{case_detail.case.title}}</a></h2>
|
||||
|
||||
<p>This form allows you to add questions to a case. A example of how the quesiton will be displayed is shown below.</p>
|
||||
<p>If answers are supplied the question will be automarked</p>
|
||||
|
||||
<p>See https://django-jsonform.readthedocs.io/en/latest/guide/inputs.html for formatting</p>
|
||||
<p>The question system is built using https://github.com/json-editor/json-editor, this allows for highly flexible forms / question design at a cost of some complexity. To help with this a number of preset questions are avalible to choose from (these can then be edited if needed). It is also possible to copy questions from other cases in the collection. </p>
|
||||
|
||||
Import schema from:
|
||||
<button class="btn btn-primary btn-sm" type="button" data-bs-toggle="offcanvas" data-bs-target="#offcanvasRight" aria-controls="offcanvasRight"
|
||||
@@ -44,14 +55,17 @@
|
||||
{{ blank_form}}
|
||||
</form> {% endcomment %}
|
||||
|
||||
<h2>Question display</h2>
|
||||
This shows the form as it will be displayed (with the correct answers). If you wish to edit a correct answer you can change it below.
|
||||
|
||||
<form method="POST" class="post-form">
|
||||
{% csrf_token %}
|
||||
{{example_form}}
|
||||
<button type="submit" value="answer" name="submit">Save Correct Answers</button>
|
||||
</form>
|
||||
{% if case_detail.question_schema %}
|
||||
<h2>Question display</h2>
|
||||
This shows the form as it will be displayed (with the correct answers). If you wish to edit a correct answer you can change it below.
|
||||
|
||||
<form method="POST" class="post-form">
|
||||
{% csrf_token %}
|
||||
{{example_form}}
|
||||
<button type="submit" value="answer" name="submit">Save Correct Answers</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% if completed %}
|
||||
{% if question_completed %}
|
||||
<span class="stamp-white">REVIEW</span>
|
||||
{% endif %}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
|
||||
|
||||
{% if not completed %}
|
||||
{% if not question_completed %}
|
||||
{% if resources %}
|
||||
<h5>Resources</h4>
|
||||
<ul class="no-list-style">
|
||||
@@ -109,7 +109,7 @@
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
{% if completed %}
|
||||
{% if question_completed %}
|
||||
{% if resources %}
|
||||
<h5>Resources</h4>
|
||||
<ul class="no-list-style">
|
||||
@@ -127,15 +127,15 @@
|
||||
|
||||
{% endif %}
|
||||
<form method="POST" class="post-form">{% csrf_token %}
|
||||
{% if collection.collection_type == "REP" %}
|
||||
{% if collection.collection_type == "REP" or collection.collection_type == "QUE" %}
|
||||
{{form.json.errors}}
|
||||
<div class="form-contents">
|
||||
<fieldset {% if completed %}disabled="disabled"{% endif %}>
|
||||
<fieldset {% if question_completed %}disabled="disabled"{% endif %}>
|
||||
{{form}}
|
||||
</fieldset>
|
||||
</div>
|
||||
<p>
|
||||
{% if completed %}
|
||||
{% if question_completed %}
|
||||
|
||||
{% if collection.self_review %}
|
||||
{% if self_review %}
|
||||
@@ -167,7 +167,7 @@
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if collection.self_review and not completed %}
|
||||
{% if collection.self_review and not collection.feedback_once_collection_complete and not question_completed %}
|
||||
<button type="submit" name="complete_case" class="save btn btn-default">Finish Case</button>
|
||||
{% endif %}
|
||||
|
||||
@@ -202,6 +202,7 @@
|
||||
{% endblock %}
|
||||
{% block js %}
|
||||
{{ form.media }}
|
||||
{% comment %} <script src="https://cdn.jsdelivr.net/npm/@json-editor/json-editor@latest/dist/jsoneditor.min.js"></script> {% endcomment %}
|
||||
<script type="text/javascript">
|
||||
window.images = {
|
||||
{% for series in series_list %}
|
||||
@@ -214,6 +215,92 @@
|
||||
window.loadDicomViewer(window.images[0])
|
||||
}, 500);
|
||||
})
|
||||
</script>
|
||||
|
||||
{% comment %} $('document').ready(function() {
|
||||
|
||||
// Get value from either a json string or url pointing to a json file
|
||||
function process(value) {
|
||||
var isjson=true;
|
||||
var result;
|
||||
|
||||
try {
|
||||
result = JSON.parse(value);
|
||||
} catch(e) {
|
||||
isjson=false;
|
||||
}
|
||||
|
||||
if (isjson) {
|
||||
return result;
|
||||
} else {
|
||||
return $.getJSON(value)
|
||||
.then(function (response) {
|
||||
return response;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$('.editor_holder').each(function() {
|
||||
// Get the DOM Element
|
||||
var element = $(this).get(0);
|
||||
console.log("el", element)
|
||||
|
||||
var options_text = $(this).attr('options')
|
||||
var schema_text = $(this).attr('schema')
|
||||
|
||||
var schema = process(schema_text);
|
||||
var options = process(options_text);
|
||||
|
||||
var name = $(this).attr('name');
|
||||
var hidden_identifier = 'input[name=' + name + ']';
|
||||
var initial = $(hidden_identifier).val();
|
||||
|
||||
// Check if editor is within form
|
||||
var form = $(this).closest('form')
|
||||
console.log("form", form)
|
||||
|
||||
//Wait for any ajax requests to complete
|
||||
$.when(schema, options).done(function(schemaresult, optionsresult) {
|
||||
optionsresult.form_name_root = name;
|
||||
|
||||
// Pass initial value though to editor
|
||||
if (initial) {
|
||||
optionsresult.startval = JSON.parse(initial);
|
||||
}
|
||||
|
||||
optionsresult.schema = schemaresult;
|
||||
// console.log(options);
|
||||
var editor = new JSONEditor(element, optionsresult);
|
||||
|
||||
console.log("editor", editor)
|
||||
if (form) {
|
||||
$(form).submit(function(e) {
|
||||
console.log("submitting")
|
||||
// Set the hidden field value to the editors value
|
||||
$(hidden_identifier).val(JSON.stringify(editor.getValue()));
|
||||
// Disable the editor so it's values wont be submitted
|
||||
//editor.disable();
|
||||
// Validate the editor's current value against the schema
|
||||
const errors = editor.validate();
|
||||
|
||||
if (errors.length) {
|
||||
// errors is an array of objects, each with a `path`, `property`, and `message` parameter
|
||||
// `property` is the schema keyword that triggered the validation error (e.g. "minLength")
|
||||
// `path` is a dot separated path into the JSON object (e.g. "root.path.to.field")
|
||||
console.log(errors);
|
||||
}
|
||||
else {
|
||||
console.log("valid");
|
||||
}
|
||||
console.log(editor.getValue());
|
||||
//e.preventDefault();
|
||||
})
|
||||
} else {
|
||||
console.log("No form found")
|
||||
}
|
||||
})
|
||||
});
|
||||
})
|
||||
{% endcomment %}
|
||||
</script>
|
||||
|
||||
{% endblock js %}
|
||||
|
||||
@@ -15,7 +15,11 @@
|
||||
<ol id="full-question-list" class="sortable">
|
||||
{% for casedetail in casesdetails %}
|
||||
<li data-question_pk={{casedetail.case.pk}}><a title="sort_order: {{casedetail.sort_order}}" href="{% url 'atlas:collection_case_view' pk=collection.pk case_number=forloop.counter0 %}">Case {{forloop.counter}}</a>
|
||||
: {{casedetail.case.title}} (<a href='{% url "atlas:collection_case_details" casedetail.collection.pk casedetail.case.pk %}'>edit</a>)
|
||||
: {{casedetail.case.title}}
|
||||
|
||||
{% if collection.collection_type == "QUE" %}
|
||||
(<a href='{% url "atlas:collection_case_details" casedetail.collection.pk casedetail.case.pk %}'>edit</a>)
|
||||
{% endif %}
|
||||
</li>
|
||||
|
||||
{% endfor %}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<h2>Case: {{ case_detail.case.title }}</h2>
|
||||
|
||||
{% if case_detail.question_schema %}
|
||||
<button class="btn btn-sm btn-secondary use-layout">Use layout</button>
|
||||
<button class="btn btn-sm btn-secondary use-layout">Use schema</button>
|
||||
<details><summary>Schema:</summary>
|
||||
<div class="schema" data-schema='{{ case_detail.get_question_schema }}'>{{ case_detail.question_schema }}</div>
|
||||
</details>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
{% for schema in schemas %}
|
||||
|
||||
<li class="schemas">
|
||||
<button class="btn btn-sm btn-secondary use-layout">Use layout</button>
|
||||
<button class="btn btn-sm btn-secondary use-layout">Use schema</button>
|
||||
<details><summary>Schema:</summary>
|
||||
<div class="schema" data-schema='{{schema|safe}}'>{{schema}}</div>
|
||||
</details>
|
||||
|
||||
+127
-39
@@ -42,6 +42,7 @@ from .forms import (
|
||||
CaseDetailForm,
|
||||
CaseForm,
|
||||
CaseResourceFormSet,
|
||||
CidQuestionAnswerForm,
|
||||
CidReportAnswerForm,
|
||||
CidReportAnswerMarkForm,
|
||||
ConditionAutocompleteForm,
|
||||
@@ -57,6 +58,7 @@ from .forms import (
|
||||
SeriesFindingForm,
|
||||
CaseDifferentialFormSet,
|
||||
StructureForm,
|
||||
UserQuestionAnswerForm,
|
||||
UserReportAnswerForm,
|
||||
)
|
||||
from .models import (
|
||||
@@ -186,6 +188,7 @@ def case_detail(request, pk):
|
||||
# logging.debug(atlas.subspecialty.first().name.all())
|
||||
return render(request, "atlas/case_detail.html", {"case": case})
|
||||
|
||||
|
||||
@login_required
|
||||
@user_is_author_or_atlas_series_checker_or_atlas_marker
|
||||
def series_thumbnail(request, pk, finding_pk=None):
|
||||
@@ -372,6 +375,7 @@ def resource_detail(request, pk):
|
||||
|
||||
return render(request, "atlas/resource_detail.html", {"resource": resource})
|
||||
|
||||
|
||||
@login_required
|
||||
def resource_view(request, pk):
|
||||
resource = get_object_or_404(Resource, pk=pk)
|
||||
@@ -452,10 +456,12 @@ def user_uploads_series(request, series_instance_uid: str):
|
||||
{"dicoms": dicoms, "series_id": series_instance_uid},
|
||||
)
|
||||
|
||||
|
||||
@login_required
|
||||
def new_uploads(request):
|
||||
return render(request, "atlas/new_uploads.html", {})
|
||||
|
||||
|
||||
@login_required
|
||||
@user_passes_test(lambda u: u.is_superuser)
|
||||
def all_uploads(request, case_id: int | None = None):
|
||||
@@ -712,7 +718,8 @@ class CaseCollectionUpdate(RevisionMixin, AuthorOrCheckerRequiredMixin, UpdateVi
|
||||
context["case_formset"].full_clean()
|
||||
else:
|
||||
context["case_formset"] = CaseCollectionCaseFormSet(
|
||||
instance=self.object, form_kwargs={"user": self.request.user, "collection": self.object}
|
||||
instance=self.object,
|
||||
form_kwargs={"user": self.request.user, "collection": self.object},
|
||||
)
|
||||
|
||||
context["collection"] = context["casecollection"]
|
||||
@@ -1554,7 +1561,7 @@ def collection_take_start(request, pk):
|
||||
}
|
||||
|
||||
match collection.collection_type:
|
||||
case "REP":
|
||||
case x if x in ("REP", "QUE"):
|
||||
return render(
|
||||
request, "atlas/collection_take_start.html", template_variables
|
||||
)
|
||||
@@ -1563,28 +1570,37 @@ def collection_take_start(request, pk):
|
||||
request, "atlas/collection_review_start.html", template_variables
|
||||
)
|
||||
|
||||
|
||||
@user_is_collection_author_or_atlas_editor
|
||||
def collection_case_details(request, exam_id, case_id):
|
||||
case_detail = CaseDetail.objects.get(case=case_id, collection=exam_id)
|
||||
|
||||
collection = case_detail.collection
|
||||
|
||||
if not collection.collection_type == "QUE":
|
||||
raise Http404("Collection not in question mode")
|
||||
|
||||
if request.method == "POST":
|
||||
|
||||
if request.method == 'POST':
|
||||
|
||||
# Called if the user saves the correct answer form
|
||||
if request.POST.get('submit') == 'answer':
|
||||
#answers = request.POST
|
||||
if request.POST.get("submit") == "answer":
|
||||
# answers = request.POST
|
||||
case_detail.question_answers = json.loads(request.POST.get("json_answer"))
|
||||
case_detail.save()
|
||||
example_form = JsonAnswerForm(request.POST, case_detail=case_detail)
|
||||
form = CaseDetailForm(instance=case_detail)
|
||||
# Called if the user saves the main form
|
||||
elif request.POST.get('submit') == 'save':
|
||||
elif request.POST.get("submit") == "save":
|
||||
form = CaseDetailForm(request.POST, instance=case_detail)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
# Add any additional logic or redirection here
|
||||
#example_form = JsonAnswerForm(request.POST, case_detail=case_detail)
|
||||
if case_detail.question_schema is not None and case_detail.question_answers is not None:
|
||||
# example_form = JsonAnswerForm(request.POST, case_detail=case_detail)
|
||||
post_data = request.POST.copy()
|
||||
if (
|
||||
case_detail.question_schema is not None
|
||||
and case_detail.question_answers is not None
|
||||
):
|
||||
question_keys = set(case_detail.question_schema["properties"].keys())
|
||||
answer_keys = set(case_detail.question_answers.keys())
|
||||
|
||||
@@ -1598,10 +1614,8 @@ def collection_case_details(request, exam_id, case_id):
|
||||
for key in diff:
|
||||
answers[key] = ""
|
||||
|
||||
|
||||
post_data = request.POST.copy()
|
||||
post_data["json_answer"]= json.dumps(answers)
|
||||
case_detail.question_answers = answers
|
||||
post_data["json_answer"] = json.dumps(answers)
|
||||
case_detail.question_answers = answers
|
||||
case_detail.save()
|
||||
example_form = JsonAnswerForm(post_data, case_detail=case_detail)
|
||||
|
||||
@@ -1613,14 +1627,14 @@ def collection_case_details(request, exam_id, case_id):
|
||||
|
||||
else:
|
||||
post_data = request.POST.copy()
|
||||
post_data["json_answer"]= json.dumps(case_detail.question_answers)
|
||||
post_data["json_answer"] = json.dumps(case_detail.question_answers)
|
||||
example_form = JsonAnswerForm(post_data, case_detail=case_detail)
|
||||
|
||||
form = CaseDetailForm(instance=case_detail)
|
||||
pass
|
||||
#post_data = None
|
||||
# post_data = None
|
||||
|
||||
#if case_detail.question_schema is not None and case_detail.question_answers is not None:
|
||||
# if case_detail.question_schema is not None and case_detail.question_answers is not None:
|
||||
# question_keys = set(case_detail.question_schema["properties"].keys())
|
||||
# answer_keys = set(case_detail.question_answers.keys())
|
||||
|
||||
@@ -1634,23 +1648,37 @@ def collection_case_details(request, exam_id, case_id):
|
||||
# for key in diff:
|
||||
# answers[key] = ""
|
||||
|
||||
|
||||
# post_data = request.POST.copy()
|
||||
# post_data["json_answer"]= json.dumps(answers)
|
||||
# case_detail.question_answers = answers
|
||||
#example_form = JsonAnswerForm(post_data, case_detail=case_detail)
|
||||
# example_form = JsonAnswerForm(post_data, case_detail=case_detail)
|
||||
|
||||
#form = CaseDetailForm(instance=case_detail)
|
||||
# form = CaseDetailForm(instance=case_detail)
|
||||
|
||||
#blank_form = JsonAnswerForm(case_detail=case_detail)
|
||||
# blank_form = JsonAnswerForm(case_detail=case_detail)
|
||||
case_number, case_count = collection.get_index_of_case(case_detail.case, case_count=True)
|
||||
|
||||
previous = collection.get_previous_case(case_detail.case)
|
||||
next = collection.get_next_case(case_detail.case)
|
||||
|
||||
|
||||
return render(
|
||||
request, "atlas/collection_case_detail.html", {"case_detail": case_detail,
|
||||
"form": form, "example_form": example_form,
|
||||
}
|
||||
request,
|
||||
"atlas/collection_case_detail.html",
|
||||
{
|
||||
"case_detail": case_detail,
|
||||
"form": form,
|
||||
"example_form": example_form,
|
||||
"collection": collection,
|
||||
"case": case_detail.case,
|
||||
"previous": previous,
|
||||
"next": next,
|
||||
"collection_length": case_count,
|
||||
"case_number": case_number,
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@user_is_collection_author_or_atlas_editor
|
||||
def collection_mark_overview(request, pk):
|
||||
collection = get_object_or_404(CaseCollection, pk=pk)
|
||||
@@ -1861,14 +1889,15 @@ def collection_take_overview(
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def collection_case_view_take_user_answers(request, pk: int, case_number: int):
|
||||
return collection_case_view_take_answers(request, pk, case_number)
|
||||
|
||||
|
||||
def collection_case_view_take_answers(
|
||||
request, pk: int, case_number: int, cid=None, passcode=None
|
||||
):
|
||||
"""
|
||||
"""
|
||||
""" """
|
||||
collection = get_object_or_404(CaseCollection, pk=pk)
|
||||
|
||||
collection.check_user_can_take(cid, passcode, request.user)
|
||||
@@ -1891,6 +1920,7 @@ def collection_case_view_take_answers(
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def collection_case_view_take_user(request, pk: int, case_number: int):
|
||||
return collection_case_view_take(request, pk, case_number)
|
||||
|
||||
@@ -1932,9 +1962,8 @@ def collection_case_view_take(
|
||||
cid_user_exam.save()
|
||||
|
||||
if not collection.review_only():
|
||||
case_detail = CaseDetail.objects.get(case=case, collection=collection)
|
||||
if collection.collection_type == "REP":
|
||||
case_detail = CaseDetail.objects.get(case=case, collection=collection)
|
||||
|
||||
if cid is not None:
|
||||
answer = case_detail.cidreportanswer_set.filter(cid=cid).first()
|
||||
ReportAnswerForm = CidReportAnswerForm
|
||||
@@ -1943,12 +1972,24 @@ def collection_case_view_take(
|
||||
user=request.user
|
||||
).first()
|
||||
ReportAnswerForm = UserReportAnswerForm
|
||||
elif collection.collection_type == "QUE":
|
||||
if cid is not None:
|
||||
answer = case_detail.cidreportanswer_set.filter(cid=cid).first()
|
||||
ReportAnswerForm = CidQuestionAnswerForm
|
||||
else:
|
||||
answer = case_detail.userreportanswer_set.filter(
|
||||
user=request.user
|
||||
).first()
|
||||
ReportAnswerForm = UserQuestionAnswerForm
|
||||
|
||||
|
||||
if request.method == "POST":
|
||||
if collection.collection_type == "REP":
|
||||
if collection.collection_type in ("REP", "QUE"):
|
||||
if not collection.publish_results:
|
||||
if answer:
|
||||
form = ReportAnswerForm(request.POST, instance=answer, case_detail=case_detail)
|
||||
form = ReportAnswerForm(
|
||||
request.POST, instance=answer, case_detail=case_detail
|
||||
)
|
||||
else:
|
||||
form = ReportAnswerForm(request.POST, case_detail=case_detail)
|
||||
|
||||
@@ -1993,7 +2034,7 @@ def collection_case_view_take(
|
||||
**kwargs,
|
||||
)
|
||||
else:
|
||||
if collection.collection_type == "REP":
|
||||
if collection.collection_type in ("REP", "QUE"):
|
||||
form = ReportAnswerForm(instance=answer, case_detail=case_detail)
|
||||
|
||||
series_list = case.series.all().prefetch_related("images", "examination", "plane")
|
||||
@@ -2007,9 +2048,13 @@ def collection_case_view_take(
|
||||
if (
|
||||
collection.publish_results
|
||||
or cid_user_exam.completed
|
||||
or (answer is not None and answer.completed)
|
||||
or (
|
||||
not collection.feedback_once_collection_complete
|
||||
and answer is not None
|
||||
and answer.completed
|
||||
)
|
||||
):
|
||||
completed = True
|
||||
question_completed = True
|
||||
show_title = collection.show_title_post
|
||||
show_history = collection.show_history_post
|
||||
show_description = collection.show_description_post
|
||||
@@ -2020,7 +2065,7 @@ def collection_case_view_take(
|
||||
|
||||
resources = case.caseresource_set.all()
|
||||
else:
|
||||
completed = False
|
||||
question_completed = False
|
||||
show_title = collection.show_title_pre
|
||||
show_history = collection.show_history_pre
|
||||
show_description = collection.show_description_pre
|
||||
@@ -2051,7 +2096,7 @@ def collection_case_view_take(
|
||||
"show_report": show_report,
|
||||
"resources": resources,
|
||||
"cid_user_exam": cid_user_exam,
|
||||
"completed": completed,
|
||||
"question_completed": question_completed,
|
||||
"self_review": self_review,
|
||||
},
|
||||
)
|
||||
@@ -2525,6 +2570,7 @@ class CaseAuthorUpdate(RevisionMixin, AuthorRequiredMixin, UpdateView):
|
||||
context["collection"] = context["object"]
|
||||
return context
|
||||
|
||||
|
||||
class SeriesAuthorUpdate(RevisionMixin, AuthorRequiredMixin, UpdateView):
|
||||
model = Series
|
||||
form_class = SeriesAuthorForm
|
||||
@@ -2535,9 +2581,48 @@ class SeriesAuthorUpdate(RevisionMixin, AuthorRequiredMixin, UpdateView):
|
||||
context["collection"] = context["object"]
|
||||
return context
|
||||
|
||||
|
||||
def question_schemas_preset(request):
|
||||
schemas = [
|
||||
json.dumps({'type': 'object', 'title': 'Questions', 'required': ['laterality', 'diagnosis'], 'properties': {'test': {'type': 'string', 'title': 'test string', 'description': 'What side is the lesion on?'}, 'diagnosis': {'enum': ['Meningioma', 'Schwannoma', 'Neurofibroma', 'Ependymoma'], 'type': 'string', 'title': 'Diagnosis', 'description': 'What is the diagnosis?'}, 'laterality': {'enum': ['left', 'right'], 'type': 'string', 'title': 'Laterality', 'widget': 'radio', 'description': 'What side is the lesion on?'}, 'laterality (copy 2)': {'enum': ['left', 'right'], 'type': 'string', 'title': 'Lateralityaoue', 'widget': 'radio', 'description': 'What side is the lesion on?'}}}),
|
||||
json.dumps(
|
||||
{
|
||||
"type": "object",
|
||||
"title": "Questions",
|
||||
"required": ["laterality", "diagnosis"],
|
||||
"properties": {
|
||||
"test": {
|
||||
"type": "string",
|
||||
"title": "test string",
|
||||
"description": "What side is the lesion on?",
|
||||
},
|
||||
"diagnosis": {
|
||||
"enum": [
|
||||
"Meningioma",
|
||||
"Schwannoma",
|
||||
"Neurofibroma",
|
||||
"Ependymoma",
|
||||
],
|
||||
"type": "string",
|
||||
"title": "Diagnosis",
|
||||
"description": "What is the diagnosis?",
|
||||
},
|
||||
"laterality": {
|
||||
"enum": ["left", "right"],
|
||||
"type": "string",
|
||||
"title": "Laterality",
|
||||
"widget": "radio",
|
||||
"description": "What side is the lesion on?",
|
||||
},
|
||||
"laterality (copy 2)": {
|
||||
"enum": ["left", "right"],
|
||||
"type": "string",
|
||||
"title": "Lateralityaoue",
|
||||
"widget": "radio",
|
||||
"description": "What side is the lesion on?",
|
||||
},
|
||||
},
|
||||
}
|
||||
),
|
||||
]
|
||||
|
||||
return render(
|
||||
@@ -2548,14 +2633,17 @@ def question_schemas_preset(request):
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
def collection_question_schemas(request, exam_id: int):
|
||||
collection = get_object_or_404(CaseCollection, pk=exam_id)
|
||||
|
||||
questions = collection.cases.all().prefetch_related()
|
||||
|
||||
case_details = CaseDetail.objects.filter(
|
||||
case__in=questions, collection=collection
|
||||
).prefetch_related("case").order_by("sort_order")
|
||||
case_details = (
|
||||
CaseDetail.objects.filter(case__in=questions, collection=collection)
|
||||
.prefetch_related("case")
|
||||
.order_by("sort_order")
|
||||
)
|
||||
|
||||
return render(
|
||||
request,
|
||||
@@ -2564,4 +2652,4 @@ def collection_question_schemas(request, exam_id: int):
|
||||
"collection": collection,
|
||||
"case_details": case_details,
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
+2
-1
@@ -47,5 +47,6 @@ django-sortedm2m # remove once migrations squashed
|
||||
loguru
|
||||
pylibjpeg
|
||||
pylibjpeg-libjpeg
|
||||
django-jsonforms
|
||||
#django-jsonforms
|
||||
git+https://github.com/xkjq/django-jsonforms.git@bump-json-editor-lib
|
||||
django_svelte_jsoneditor
|
||||
Reference in New Issue
Block a user