fix a number of things
This commit is contained in:
+4
-2
@@ -583,8 +583,10 @@ class SelfReviewForm(ModelForm):
|
|||||||
widgets = {"user_exam": HiddenInput(), "case": HiddenInput()}
|
widgets = {"user_exam": HiddenInput(), "case": HiddenInput()}
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
user_exam_id = kwargs.pop("user_exam_id")
|
if "user_exam_id" in kwargs:
|
||||||
case_id = kwargs.pop("case_id")
|
user_exam_id = kwargs.pop("user_exam_id")
|
||||||
|
if "case_id" in kwargs:
|
||||||
|
case_id = kwargs.pop("case_id")
|
||||||
|
|
||||||
ModelForm.__init__(self, *args, **kwargs)
|
ModelForm.__init__(self, *args, **kwargs)
|
||||||
super(SelfReviewForm, self).__init__(*args, **kwargs)
|
super(SelfReviewForm, self).__init__(*args, **kwargs)
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# Generated by Django 4.1.4 on 2023-08-07 08:14
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('atlas', '0007_selfreview'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='case',
|
||||||
|
name='scrapped',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='case',
|
||||||
|
name='archive',
|
||||||
|
field=models.BooleanField(default=False, help_text='Case has been archived, this will remain on the system but will not be shown'),
|
||||||
|
),
|
||||||
|
]
|
||||||
+120
-9
@@ -13,6 +13,7 @@ from django.utils import timezone
|
|||||||
|
|
||||||
|
|
||||||
import pydicom
|
import pydicom
|
||||||
|
import pydicom.multival
|
||||||
|
|
||||||
from django.core.files.storage import FileSystemStorage
|
from django.core.files.storage import FileSystemStorage
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
@@ -272,8 +273,9 @@ class Case(models.Model):
|
|||||||
related_name="atlas_edited_cases",
|
related_name="atlas_edited_cases",
|
||||||
)
|
)
|
||||||
|
|
||||||
scrapped = models.BooleanField(
|
archive = models.BooleanField(
|
||||||
default=False, help_text="Question has been scrapped and will not be shown"
|
default=False,
|
||||||
|
help_text="Case has been archived, this will remain on the system but will not be shown",
|
||||||
)
|
)
|
||||||
|
|
||||||
open_access = models.BooleanField(
|
open_access = models.BooleanField(
|
||||||
@@ -325,6 +327,30 @@ class Case(models.Model):
|
|||||||
examinations = [series.get_examination() for series in self.series.all()]
|
examinations = [series.get_examination() for series in self.series.all()]
|
||||||
return f"{self.pk}:{self.title} {'/'.join([str(c) for c in self.condition.all()])} [{', '.join(examinations)}]"
|
return f"{self.pk}:{self.title} {'/'.join([str(c) for c in self.condition.all()])} [{', '.join(examinations)}]"
|
||||||
|
|
||||||
|
def get_case_dicom_json(self):
|
||||||
|
series_json = []
|
||||||
|
for series in self.series.all():
|
||||||
|
series_json.append(series.get_series_dicom_json())
|
||||||
|
|
||||||
|
print(series_json)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"studies": [
|
||||||
|
{
|
||||||
|
"StudyInstanceUID": "1.3.6.1.4.1.14519.5.2.1.6279.6001.298806137288633453246975630178",
|
||||||
|
"StudyDate": "20000101",
|
||||||
|
"StudyTime": "",
|
||||||
|
"PatientName": "",
|
||||||
|
"PatientID": "LIDC-IDRI-0001",
|
||||||
|
"AccessionNumber": "",
|
||||||
|
"PatientAge": "",
|
||||||
|
"PatientSex": "",
|
||||||
|
"series": series_json,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class SeriesImage(SeriesImageBase):
|
class SeriesImage(SeriesImageBase):
|
||||||
image = models.FileField(upload_to=image_directory_path)
|
image = models.FileField(upload_to=image_directory_path)
|
||||||
@@ -332,6 +358,50 @@ class SeriesImage(SeriesImageBase):
|
|||||||
"Series", related_name="images", on_delete=models.SET_NULL, null=True
|
"Series", related_name="images", on_delete=models.SET_NULL, null=True
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_image_dicom_json(self):
|
||||||
|
try:
|
||||||
|
with pydicom.dcmread(self.image) as ds:
|
||||||
|
to_keep = [
|
||||||
|
"Columns",
|
||||||
|
"Rows",
|
||||||
|
"InstanceNumber",
|
||||||
|
"SOPClassUID",
|
||||||
|
"PhotometricInterpretation",
|
||||||
|
"BitsAllocated",
|
||||||
|
"BitsStored",
|
||||||
|
"PixelRepresentation",
|
||||||
|
"SamplesPerPixel",
|
||||||
|
"PixelSpacing",
|
||||||
|
"HighBit",
|
||||||
|
"ImageOrientationPatient",
|
||||||
|
"ImagePositionPatient",
|
||||||
|
"FrameOfReferenceUID",
|
||||||
|
"ImageType",
|
||||||
|
"Modality",
|
||||||
|
"SOPInstanceUID",
|
||||||
|
"SeriesInstanceUID",
|
||||||
|
"StudyInstanceUID",
|
||||||
|
"WindowCenter",
|
||||||
|
"WindowWidth",
|
||||||
|
"SeriesDate",
|
||||||
|
]
|
||||||
|
|
||||||
|
d = {}
|
||||||
|
|
||||||
|
for key in to_keep:
|
||||||
|
val = ds[key].value
|
||||||
|
|
||||||
|
if type(val) == pydicom.multival.MultiValue:
|
||||||
|
d[key] = list(val)
|
||||||
|
else:
|
||||||
|
d[key] = val
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return {"metadata": d, "url": self.image.url}
|
||||||
|
except FileNotFoundError:
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
class SeriesFinding(models.Model):
|
class SeriesFinding(models.Model):
|
||||||
series = models.ForeignKey(
|
series = models.ForeignKey(
|
||||||
@@ -411,6 +481,23 @@ class Series(SeriesBase):
|
|||||||
"<a href='{}'>{}</a>", self.get_absolute_url(), self.get_full_str()
|
"<a href='{}'>{}</a>", self.get_absolute_url(), self.get_full_str()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_series_dicom_json(self):
|
||||||
|
instances = []
|
||||||
|
|
||||||
|
image: SeriesImage
|
||||||
|
for image in self.images.all():
|
||||||
|
instances.append(image.get_image_dicom_json())
|
||||||
|
|
||||||
|
series_json = {
|
||||||
|
"SeriesInstanceUID": "1.3.6.1.4.1.14519.5.2.1.6279.6001.179049373636438705059720603192",
|
||||||
|
"SeriesNumber": 3000566,
|
||||||
|
"Modality": "CT",
|
||||||
|
"SliceThickness": 2.5,
|
||||||
|
"instances": instances,
|
||||||
|
}
|
||||||
|
|
||||||
|
return series_json
|
||||||
|
|
||||||
|
|
||||||
class CaseCollection(ExamCollectionGenericBase):
|
class CaseCollection(ExamCollectionGenericBase):
|
||||||
app_name = "atlas"
|
app_name = "atlas"
|
||||||
@@ -522,10 +609,14 @@ class CaseCollection(ExamCollectionGenericBase):
|
|||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def get_case_by_index(self, case_index:int, case_count=False) -> Case | Tuple[Case, int]:
|
def get_case_by_index(
|
||||||
|
self, case_index: int, case_count=False
|
||||||
|
) -> Case | Tuple[Case, int]:
|
||||||
# Seems to be a bug when case_number is 0 or 1 the same (first) object gets returned
|
# Seems to be a bug when case_number is 0 or 1 the same (first) object gets returned
|
||||||
# forces a list fixes (but is likely inefficient)
|
# forces a list fixes (but is likely inefficient)
|
||||||
cases = list(self.cases.all().order_by("casedetail__sort_order").prefetch_related())
|
cases = list(
|
||||||
|
self.cases.all().order_by("casedetail__sort_order").prefetch_related()
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
case = cases[case_index]
|
case = cases[case_index]
|
||||||
@@ -534,10 +625,20 @@ class CaseCollection(ExamCollectionGenericBase):
|
|||||||
raise Http404(s)
|
raise Http404(s)
|
||||||
|
|
||||||
if case_count:
|
if case_count:
|
||||||
return case, len(cases)
|
return case, len(cases)
|
||||||
else:
|
else:
|
||||||
return case
|
return case
|
||||||
|
|
||||||
|
def get_case_take_url(self, case):
|
||||||
|
cases = list(
|
||||||
|
self.cases.all().order_by("casedetail__sort_order").prefetch_related()
|
||||||
|
)
|
||||||
|
|
||||||
|
return reverse(
|
||||||
|
"atlas:collection_case_view_take_user",
|
||||||
|
kwargs={"pk": self.pk, "case_number": cases.index(case)},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class CaseDetail(models.Model):
|
class CaseDetail(models.Model):
|
||||||
case = models.ForeignKey(Case, on_delete=models.CASCADE)
|
case = models.ForeignKey(Case, on_delete=models.CASCADE)
|
||||||
@@ -617,9 +718,7 @@ class SelfReview(models.Model):
|
|||||||
review_update_date = models.DateTimeField(auto_now=True)
|
review_update_date = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
return reverse(
|
return self.user_exam.exam.get_case_take_url(self.case)
|
||||||
"atlas:collection_take_overview_user", kwargs={"pk": self.user_exam.exam.pk}
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_display_block(self):
|
def get_display_block(self):
|
||||||
html = """<div class='self-feedback-block'>
|
html = """<div class='self-feedback-block'>
|
||||||
@@ -628,15 +727,27 @@ class SelfReview(models.Model):
|
|||||||
Findings: <span class="findings">{findings}</span><br/>
|
Findings: <span class="findings">{findings}</span><br/>
|
||||||
|
|
||||||
Interpretation: <span class="interpretation">{interpretation}</span><br/>
|
Interpretation: <span class="interpretation">{interpretation}</span><br/>
|
||||||
<span class="date">Date: {date}</span><br/>
|
<span class="date">Date: {date}</span><br/><a href="{edit_url}">Edit</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
edit_url = reverse(
|
||||||
|
"atlas:self_review_update",
|
||||||
|
kwargs={
|
||||||
|
"user_exam_id": self.user_exam.pk,
|
||||||
|
"case_id": self.case.pk,
|
||||||
|
"pk": self.pk,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
# edit_url = "TEST"
|
||||||
|
|
||||||
return format_html(
|
return format_html(
|
||||||
html,
|
html,
|
||||||
comments=self.comments,
|
comments=self.comments,
|
||||||
findings=self.findings,
|
findings=self.findings,
|
||||||
interpretation=self.interpretation,
|
interpretation=self.interpretation,
|
||||||
date=self.review_date,
|
date=self.review_date,
|
||||||
|
id=self.pk,
|
||||||
|
edit_url=edit_url,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{% extends 'atlas/base.html' %}
|
{% extends 'atlas/base.html' %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
<span class="collection-name-blend">Collection: {{collection}}</span>
|
||||||
<h2>Case {{case_number|add:1}}
|
<h2>Case {{case_number|add:1}}
|
||||||
|
|
||||||
{% if show_title %}
|
{% if show_title %}
|
||||||
@@ -75,6 +76,10 @@
|
|||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% if collection.publish_results or cid_user_exam.completed %}
|
||||||
|
<a href="{% url 'atlas:add_self_review' cid_user_exam.id case.id %}"><button>Add self review</button></a>
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
<form method="POST" class="post-form">{% csrf_token %}
|
<form method="POST" class="post-form">{% csrf_token %}
|
||||||
{% if collection.collection_type == "REP" %}
|
{% if collection.collection_type == "REP" %}
|
||||||
<div class="form-contents">
|
<div class="form-contents">
|
||||||
@@ -84,21 +89,22 @@
|
|||||||
</div>
|
</div>
|
||||||
<p>
|
<p>
|
||||||
{% if completed %}
|
{% if completed %}
|
||||||
<h4>Answer score: {{answer.score}}</h4>
|
|
||||||
Answer feedback: {{answer.feedback|safe}}
|
|
||||||
<br/>
|
|
||||||
|
|
||||||
{% if self_review %}
|
{% if collection.self_review %}
|
||||||
<h4>Self Feedback</h4>
|
{% if self_review %}
|
||||||
|
<h4>Self Feedback</h4>
|
||||||
|
|
||||||
{% for review in self_review %}
|
{% for review in self_review %}
|
||||||
{{review.get_display_block}}
|
{{review.get_display_block}}
|
||||||
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
<h4>Answer score: {{answer.score}}</h4>
|
||||||
|
Answer feedback: {{answer.feedback|safe}}
|
||||||
|
<br/>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -117,10 +123,6 @@
|
|||||||
<br />
|
<br />
|
||||||
<button type="submit" name="finish" class="save btn btn-default">Overview</button>
|
<button type="submit" name="finish" class="save btn btn-default">Overview</button>
|
||||||
<button type="submit" id="goto-button" value="test" name="goto" class="hide">goto</button>
|
<button type="submit" id="goto-button" value="test" name="goto" class="hide">goto</button>
|
||||||
{% if collection.publish_results or cid_user_exam.completed %}
|
|
||||||
<a href="{% url 'atlas:add_self_review' cid_user_exam.id case.id %}">Add self review</a>
|
|
||||||
|
|
||||||
{% endif %}
|
|
||||||
</form>
|
</form>
|
||||||
<style>
|
<style>
|
||||||
label {
|
label {
|
||||||
|
|||||||
@@ -15,8 +15,8 @@
|
|||||||
{{answer_count}} out of {{collection_length}} cases answered. Click to go to case.
|
{{answer_count}} out of {{collection_length}} cases answered. Click to go to case.
|
||||||
<div class="sba-finish-list">
|
<div class="sba-finish-list">
|
||||||
<ul>
|
<ul>
|
||||||
{% for question, answer in question_answer_tuples %}
|
{% for question, answer, self_review in question_answer_tuples %}
|
||||||
<li> <span {% if not collection.review_only and not answer %}class="unanswered"{% endif %}>
|
<li> <span class="{% if not collection.review_only and not answer %}unanswered{% endif %}">
|
||||||
<a href="
|
<a href="
|
||||||
{% if cid is not None %}
|
{% if cid is not None %}
|
||||||
{% url 'atlas:collection_case_view_take' pk=collection.id case_number=forloop.counter0 cid=cid passcode=passcode %}
|
{% url 'atlas:collection_case_view_take' pk=collection.id case_number=forloop.counter0 cid=cid passcode=passcode %}
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
{% url 'atlas:collection_case_view_take_user' pk=collection.id case_number=forloop.counter0 %}
|
{% url 'atlas:collection_case_view_take_user' pk=collection.id case_number=forloop.counter0 %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
">
|
">
|
||||||
Case: {{forloop.counter}}
|
Case: {{forloop.counter}}{% if self_review %} <span class="self-review-complete" title="You have reviewed this case."> <i class="bi-card-checklist"></i></span>{% endif %}
|
||||||
</a>
|
</a>
|
||||||
<br/>
|
<br/>
|
||||||
{% if not collection.review_only %}
|
{% if not collection.review_only %}
|
||||||
|
|||||||
@@ -22,6 +22,9 @@
|
|||||||
<button>Start</button>
|
<button>Start</button>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</a>
|
</a>
|
||||||
|
{% if cid_exam.completed %}
|
||||||
|
<a href="{% url 'atlas:collection_take_overview_user' pk=collection.pk %}"><button>Overview</button></a>
|
||||||
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
Enter your CID and passcode in the below boxes.<br />
|
Enter your CID and passcode in the below boxes.<br />
|
||||||
<p><input id="cid-box" type="text" value="Candidate ID"></p>
|
<p><input id="cid-box" type="text" value="Candidate ID"></p>
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
{% extends 'atlas/base.html' %}
|
{% extends 'atlas/base.html' %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
<h2>Atlas</h2>
|
||||||
|
|
||||||
|
<a href="{% url 'atlas:user_collections' %}">Available collections</a>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -33,4 +33,5 @@
|
|||||||
</div> {% endcomment %}
|
</div> {% endcomment %}
|
||||||
<input type="submit" value="Submit">
|
<input type="submit" value="Submit">
|
||||||
</form>
|
</form>
|
||||||
|
{% if review %}<br/><a href="{% url 'atlas:self_review_delete' user_exam.id case.id review.id %}"><button>Delete review</button></a>{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
{% extends 'atlas/base.html' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h2>Collections</h2>
|
||||||
|
|
||||||
|
|
||||||
|
<ul>
|
||||||
|
{% for collection in collections %}
|
||||||
|
<li>
|
||||||
|
<a href="{% url 'atlas:collection_take_start' collection.pk %}">{{collection}} {{collelction.is_complete}}</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
+19
-2
@@ -8,21 +8,32 @@ from . import views
|
|||||||
app_name = "atlas"
|
app_name = "atlas"
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("", views.CaseView.as_view(), name="index"),
|
path("", views.index, name="index"),
|
||||||
path(
|
path(
|
||||||
"create/",
|
"create/",
|
||||||
RedirectView.as_view(pattern_name="atlas:case_create", permanent=False),
|
RedirectView.as_view(pattern_name="atlas:case_create", permanent=False),
|
||||||
name="create",
|
name="create",
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"add_self_review/<int:user_exam_id>/<int:case_id>",
|
"self_review/<int:user_exam_id>/<int:case_id>/add",
|
||||||
views.AddSelfReview.as_view(),
|
views.AddSelfReview.as_view(),
|
||||||
name="add_self_review",
|
name="add_self_review",
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"self_review/<int:user_exam_id>/<int:case_id>/update/<int:pk>",
|
||||||
|
views.SelfReviewUpdate.as_view(),
|
||||||
|
name="self_review_update",
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"self_review/<int:user_exam_id>/<int:case_id>/delete/<int:pk>",
|
||||||
|
views.SelfReviewDelete.as_view(),
|
||||||
|
name="self_review_delete",
|
||||||
|
),
|
||||||
path("author/<int:pk>/", views.author_detail, name="author_detail"),
|
path("author/<int:pk>/", views.author_detail, name="author_detail"),
|
||||||
path("author/", views.author_list, name="author_list"),
|
path("author/", views.author_list, name="author_list"),
|
||||||
path("case/", views.CaseView.as_view(), name="case_view"),
|
path("case/", views.CaseView.as_view(), name="case_view"),
|
||||||
path("collection/", views.CollectionView.as_view(), name="collection_view"),
|
path("collection/", views.CollectionView.as_view(), name="collection_view"),
|
||||||
|
path("collection/user", views.user_collections, name="user_collections"),
|
||||||
path(
|
path(
|
||||||
"collection/create",
|
"collection/create",
|
||||||
views.CaseCollectionCreate.as_view(),
|
views.CaseCollectionCreate.as_view(),
|
||||||
@@ -125,6 +136,12 @@ urlpatterns = [
|
|||||||
views.GenericExamViews.exam_json_edit,
|
views.GenericExamViews.exam_json_edit,
|
||||||
name="exam_json_edit",
|
name="exam_json_edit",
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"case/<int:pk>/dicom_json",
|
||||||
|
views.case_dicom_json,
|
||||||
|
name="case_dicom_json",
|
||||||
|
),
|
||||||
|
|
||||||
path("condition/", views.ConditionView.as_view(), name="condition_view"),
|
path("condition/", views.ConditionView.as_view(), name="condition_view"),
|
||||||
path("categories/", views.categories_list, name="categories_list"),
|
path("categories/", views.categories_list, name="categories_list"),
|
||||||
path("condition/<int:pk>", views.condition_detail, name="condition_detail"),
|
path("condition/<int:pk>", views.condition_detail, name="condition_detail"),
|
||||||
|
|||||||
+85
-4
@@ -290,6 +290,13 @@ def author_list(request):
|
|||||||
|
|
||||||
return render(request, "atlas/author_list.html", {"authors": authors})
|
return render(request, "atlas/author_list.html", {"authors": authors})
|
||||||
|
|
||||||
|
def index(request):
|
||||||
|
return render(request, "atlas/index.html", {})
|
||||||
|
|
||||||
|
def user_collections(request):
|
||||||
|
collections = request.user.user_casecollection_exams.all()
|
||||||
|
|
||||||
|
return render(request, "atlas/user_collections.html", {"collections": collections})
|
||||||
|
|
||||||
class CaseDelete(RevisionMixin, AuthorOrCheckerRequiredMixin, DeleteView):
|
class CaseDelete(RevisionMixin, AuthorOrCheckerRequiredMixin, DeleteView):
|
||||||
model = Case
|
model = Case
|
||||||
@@ -1325,12 +1332,13 @@ def collection_take_overview(
|
|||||||
|
|
||||||
question_answer_tuples = []
|
question_answer_tuples = []
|
||||||
answer_count = 0
|
answer_count = 0
|
||||||
for q in case_details:
|
for cd in case_details:
|
||||||
if q in answer_question_map and answer_question_map[q].answer:
|
self_review = cid_user_exam.selfreview_set.filter(case=cd.case)
|
||||||
question_answer_tuples.append((q, answer_question_map[q]))
|
if cd in answer_question_map and answer_question_map[cd].answer:
|
||||||
|
question_answer_tuples.append((cd, answer_question_map[cd], self_review))
|
||||||
answer_count += 1
|
answer_count += 1
|
||||||
else:
|
else:
|
||||||
question_answer_tuples.append((q, None))
|
question_answer_tuples.append((cd, None, self_review))
|
||||||
|
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
@@ -1347,6 +1355,7 @@ def collection_take_overview(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def collection_case_view_take_user(request, pk: int, case_number: int):
|
def collection_case_view_take_user(request, pk: int, case_number: int):
|
||||||
return collection_case_view_take(request, pk, case_number)
|
return collection_case_view_take(request, pk, case_number)
|
||||||
|
|
||||||
@@ -1543,6 +1552,10 @@ def collection_case_view(request, pk, case_number):
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def case_dicom_json(request, pk):
|
||||||
|
case = get_object_or_404(Case, pk=pk)
|
||||||
|
|
||||||
|
return JsonResponse(case.get_case_dicom_json())
|
||||||
|
|
||||||
@user_is_collection_author_or_atlas_editor
|
@user_is_collection_author_or_atlas_editor
|
||||||
def delete_collection_cid_answers(request, exam_id, cid):
|
def delete_collection_cid_answers(request, exam_id, cid):
|
||||||
@@ -1761,6 +1774,70 @@ class CollectionCloneMixin:
|
|||||||
class CollectionClone(CollectionCloneMixin, CaseCollectionCreate):
|
class CollectionClone(CollectionCloneMixin, CaseCollectionCreate):
|
||||||
"""Clone exam view"""
|
"""Clone exam view"""
|
||||||
|
|
||||||
|
class SelfReviewDelete(DeleteView):
|
||||||
|
model = SelfReview
|
||||||
|
template_name = "confirm_delete.html"
|
||||||
|
#success_url = reverse_lazy("atlas:finding_view")
|
||||||
|
|
||||||
|
def get_initial(self):
|
||||||
|
user_exam_id = self.kwargs["user_exam_id"]
|
||||||
|
case_id = self.kwargs["case_id"]
|
||||||
|
review_id = self.kwargs["pk"]
|
||||||
|
|
||||||
|
self.user_exam = get_object_or_404(CidUserExam, pk=user_exam_id)
|
||||||
|
self.case = get_object_or_404(Case, pk=case_id)
|
||||||
|
self.review = get_object_or_404(SelfReview, pk=review_id)
|
||||||
|
return {"user_exam": user_exam_id, "case": case_id, "review": review_id}
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
# This can also be accessed via view.**** in the template
|
||||||
|
context = super(SelfReviewDelete, self).get_context_data(**kwargs)
|
||||||
|
context["user_exam"] = self.user_exam
|
||||||
|
context["case"] = self.case
|
||||||
|
context["review"] = self.review
|
||||||
|
return context
|
||||||
|
|
||||||
|
def get_success_url(self) -> str:
|
||||||
|
return self.user_exam.exam.get_case_take_url(self.case)
|
||||||
|
|
||||||
|
class SelfReviewUpdate(
|
||||||
|
UpdateView
|
||||||
|
):
|
||||||
|
model = SelfReview
|
||||||
|
form_class = SelfReviewForm
|
||||||
|
|
||||||
|
def get_initial(self):
|
||||||
|
user_exam_id = self.kwargs["user_exam_id"]
|
||||||
|
case_id = self.kwargs["case_id"]
|
||||||
|
review_id = self.kwargs["pk"]
|
||||||
|
|
||||||
|
self.user_exam = get_object_or_404(CidUserExam, pk=user_exam_id)
|
||||||
|
self.case = get_object_or_404(Case, pk=case_id)
|
||||||
|
self.review = get_object_or_404(SelfReview, pk=review_id)
|
||||||
|
return {"user_exam": user_exam_id, "case": case_id, "review": review_id}
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
# This can also be accessed via view.**** in the template
|
||||||
|
context = super(SelfReviewUpdate, self).get_context_data(**kwargs)
|
||||||
|
context["user_exam"] = self.user_exam
|
||||||
|
context["case"] = self.case
|
||||||
|
context["review"] = self.review
|
||||||
|
return context
|
||||||
|
|
||||||
|
def get_success_url(self) -> str:
|
||||||
|
return self.user_exam.exam.get_case_take_url(self.case)
|
||||||
|
|
||||||
|
#def get_object(self):
|
||||||
|
# return self.review
|
||||||
|
# return
|
||||||
|
#def get_initial(self):
|
||||||
|
# user_exam_id = self.kwargs["user_exam_id"]
|
||||||
|
# case_id = self.kwargs["case_id"]
|
||||||
|
|
||||||
|
# self.user_exam = get_object_or_404(CidUserExam, pk=user_exam_id)
|
||||||
|
# self.case = get_object_or_404(Case, pk=case_id)
|
||||||
|
# return {"user_exam": user_exam_id, "case": case_id}
|
||||||
|
|
||||||
class AddSelfReview(CreateView):
|
class AddSelfReview(CreateView):
|
||||||
model = SelfReview
|
model = SelfReview
|
||||||
form_class = SelfReviewForm
|
form_class = SelfReviewForm
|
||||||
@@ -1789,6 +1866,10 @@ class AddSelfReview(CreateView):
|
|||||||
context["case"] = self.case
|
context["case"] = self.case
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
def get_success_url(self) -> str:
|
||||||
|
return self.user_exam.exam.get_case_take_url(self.case)
|
||||||
|
#return reverse("atlas:collection_take_overview_user", kwargs={"pk": self.user_exam.collection.id })
|
||||||
|
|
||||||
# def form_valid(self, form):
|
# def form_valid(self, form):
|
||||||
# model = form.save(commit=False)
|
# model = form.save(commit=False)
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ services:
|
|||||||
volumes:
|
volumes:
|
||||||
- ./rad/:/usr/src/rad/
|
- ./rad/:/usr/src/rad/
|
||||||
- ../rts/:/usr/src/rad/rts
|
- ../rts/:/usr/src/rad/rts
|
||||||
|
- ../Viewers/platform/app/dist/:/usr/src/rad/viewer
|
||||||
- ./backups:/usr/src/backups
|
- ./backups:/usr/src/backups
|
||||||
- ./media:/usr/src/media
|
- ./media:/usr/src/media
|
||||||
- ./static:/usr/src/static
|
- ./static:/usr/src/static
|
||||||
|
|||||||
@@ -1013,4 +1013,9 @@ h1, h2, h3, h4, h5, h6 {
|
|||||||
.self-feedback-block.data {
|
.self-feedback-block.data {
|
||||||
opacity: 60%;
|
opacity: 60%;
|
||||||
font-size: small;
|
font-size: small;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collection-name-blend {
|
||||||
|
opacity: 70%;
|
||||||
|
font-size: small;
|
||||||
}
|
}
|
||||||
@@ -164,6 +164,7 @@ if settings.DEBUG:
|
|||||||
#urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
#urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||||
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
||||||
urlpatterns += static("/rts/", document_root="rts")
|
urlpatterns += static("/rts/", document_root="rts")
|
||||||
|
urlpatterns += static("/viewer/", document_root="viewer")
|
||||||
import debug_toolbar
|
import debug_toolbar
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
|||||||
@@ -17,6 +17,8 @@
|
|||||||
<link href="{% static 'css/widgets.css' %}" type="text/css" media="all" rel="stylesheet">
|
<link href="{% static 'css/widgets.css' %}" type="text/css" media="all" rel="stylesheet">
|
||||||
<link rel="stylesheet" href="{% static 'css/dicomViewer.css' %}">
|
<link rel="stylesheet" href="{% static 'css/dicomViewer.css' %}">
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/2.1.4/toastr.min.css" integrity="sha512-6S2HWzVFxruDlZxI3sXOZZ4/eJ8AcxkQH1+JjSe/ONCEqR9L4Ysq5JdT5ipqtzU7WHalNwzwBv+iE51gNHJNqQ==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/2.1.4/toastr.min.css" integrity="sha512-6S2HWzVFxruDlZxI3sXOZZ4/eJ8AcxkQH1+JjSe/ONCEqR9L4Ysq5JdT5ipqtzU7WHalNwzwBv+iE51gNHJNqQ==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css">
|
||||||
|
|
||||||
|
|
||||||
<link href="/static/css/widgets.css" type="text/css" media="all" rel="stylesheet">
|
<link href="/static/css/widgets.css" type="text/css" media="all" rel="stylesheet">
|
||||||
<link href="/static/admin/css/vendor/select2/select2.min.css" type="text/css" media="screen" rel="stylesheet">
|
<link href="/static/admin/css/vendor/select2/select2.min.css" type="text/css" media="screen" rel="stylesheet">
|
||||||
|
|||||||
Reference in New Issue
Block a user