From 8f5c878babfa8985dea77f545d9fcb48f0c6581c Mon Sep 17 00:00:00 2001 From: Ross Date: Sun, 24 Jul 2022 11:11:58 +0100 Subject: [PATCH] pre series form update --- anatomy/models.py | 2 +- anatomy/static/css/anatomy.css | 6 + anatomy/static/css/dicomViewer.css | 149 ++++++++++++++++- atlas/forms.py | 29 ++++ atlas/templates/atlas/case_display_block.html | 4 +- atlas/templates/atlas/case_form.html | 4 + atlas/templates/atlas/collection_headers.html | 3 +- atlas/templates/atlas/series_form.html | 4 +- atlas/urls.py | 1 + atlas/views.py | 21 ++- rapids/templates/rapids/rapid_form.html | 154 +++++++++--------- 11 files changed, 286 insertions(+), 91 deletions(-) mode change 120000 => 100644 anatomy/static/css/dicomViewer.css diff --git a/anatomy/models.py b/anatomy/models.py index 8515252a..61a9947d 100644 --- a/anatomy/models.py +++ b/anatomy/models.py @@ -78,7 +78,7 @@ class AnatomyQuestion(models.Model): image = models.ImageField( upload_to=image_directory_path, - help_text="The image to use for the question. Ideally use use unmarked images and annotate (arrow) them on the test system. If you wish to reuse an image that is already uploaded 'clone' the question that contains it.As a preference", + help_text="The image to use for the question. Ideally use use unmarked images and annotate (arrow) them on the test system. If you wish to reuse an image that is already uploaded 'clone' the question that contains it.", ) image_annotations = models.TextField( diff --git a/anatomy/static/css/anatomy.css b/anatomy/static/css/anatomy.css index a9b6d3f8..b8f7971c 100644 --- a/anatomy/static/css/anatomy.css +++ b/anatomy/static/css/anatomy.css @@ -280,6 +280,12 @@ button a { border: 1px dashed white; } +.series-drop { + position: sticky; + height: 600px; + bottom: -500px; +} + .submit-button { position: sticky; bottom: 0; diff --git a/anatomy/static/css/dicomViewer.css b/anatomy/static/css/dicomViewer.css deleted file mode 120000 index ca61ad1e..00000000 --- a/anatomy/static/css/dicomViewer.css +++ /dev/null @@ -1 +0,0 @@ -/home/ross/dv/dicomViewer.css \ No newline at end of file diff --git a/anatomy/static/css/dicomViewer.css b/anatomy/static/css/dicomViewer.css new file mode 100644 index 00000000..b10c2033 --- /dev/null +++ b/anatomy/static/css/dicomViewer.css @@ -0,0 +1,148 @@ +.canvas-panel { + height: 100%; + position: relative; + color: white; +} + + +#dicom-overlay { + position: absolute; + bottom: 0; +} + +#image-thumbs { + position: absolute; + right: 0; + top: 50%; + transform: translateY(-50%); + overflow-y: auto; + max-height: 100%; + max-width: 30%; + user-select: none; +} + +.thumbnail { + pointer-events: none; + display: block; + margin: auto; +} + +.thumb { + position: relative; + border: 1px dotted blue; + touch-action: none; + user-select: none; +} + +.thumb-active { + border: 5px dotted blue; +} + +.thumb span { + position: absolute; + right: 0; + color: lightblue; +} + +.cornerstone-element { + position: relative; + height: 100%; +} + +.dicom-button { + font-size: 40px; + z-index: 99; + opacity: 20%; + color: gray; + top: 0; +} + +.dicom-button:hover { + opacity: 100%; +} + +.dicom-button-highlight { + opacity: 50%; + color: lightblue; +} + +#dicom-toggle-mode-button { + position: absolute; + left: 0; +} + +#dicom-settings-button { + position: absolute; + right: 0; +} + +#dicom-fullscreen-button { + position: absolute; + transform: translate(50%, -20%); + right: 50%; +} + +#dicom-collapse-button { + position: absolute; + transform: translate(0, -50%); + top: 50%; + right: 0; +} + +#dicom-info-button { + position: absolute; + transform: translate(0, -75%); + right: 0; + top: 100%; +} + +#dicom-settings-panel { + display: none; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + padding-left: 30px; + padding-right: 30px; + padding-bottom: 20px; + background-color: rgba(0, 0, 0, 0.8); /* Black w/ opacity */ + max-width: 100%; + max-height: 100%; + z-index: 999; +} + +#dicom-log-panel { + display: none; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + padding-left: 30px; + padding-right: 30px; + padding-bottom: 20px; + background-color: rgba(0, 0, 0, 0.8); /* Black w/ opacity */ + width: 90%; + height: 90%; + z-index: 999; +} + +#dicom-window-panel { + position: absolute; + top: 100%; + transform: translate(0, -100%); + opacity: 0.0; +} +#dicom-window-panel:hover { + opacity: 1; +} + +#dicom-summary-panel { + position: relative; + z-index: 999; + max-width: 100%; + height: 100%; + color: red; + top: -100%; + overflow: scroll; + display: none; +} \ No newline at end of file diff --git a/atlas/forms.py b/atlas/forms.py index 63aa1926..ea46d480 100755 --- a/atlas/forms.py +++ b/atlas/forms.py @@ -16,6 +16,7 @@ from django.shortcuts import get_object_or_404 from atlas.models import ( Case, CaseCollection, + CaseDetail, CidReportAnswer, Differential, Finding, @@ -39,6 +40,7 @@ from tinymce.widgets import TinyMCE from dal import autocomplete +import logging class ConditionForm(ModelForm): class Meta: @@ -240,9 +242,20 @@ class CaseForm(ModelForm): js = ["jsi18n.js", "tesseract.min.js"] def __init__(self, *args, **kwargs): + logging.info("LOG") + logging.debug(kwargs) self.user = kwargs.pop( "user" ) # To get request.user. Do not use kwargs.pop('user', None) due to potential security hole + + # This is populated if we create the case from an exam/collection + self.collection = None + if "exams" in kwargs["initial"]: + collections = kwargs["initial"].pop("exams") + logging.debug(collections) + + self.collection = get_object_or_404(CaseCollection, pk=collections[0]) + if kwargs.get("instance"): # We get the 'initial' keyword argument or initialize it # as a dict if it didn't exist. @@ -251,6 +264,7 @@ class CaseForm(ModelForm): # a list of primary key for the selected data. # initial["exams"] = [t.pk for t in kwargs["instance"].exams.all()] + ModelForm.__init__(self, *args, **kwargs) super(CaseForm, self).__init__(*args, **kwargs) @@ -307,6 +321,9 @@ class CaseForm(ModelForm): # Get the unsaved Case instance instance = ModelForm.save(self, False) + + + # Prepare a 'save_m2m' method for the form, old_save_m2m = self.save_m2m @@ -324,6 +341,18 @@ class CaseForm(ModelForm): instance.save() self.save_m2m() + logging.debug(f"{self.collection=}") + + if self.collection is not None: + logging.debug(f"{self.collection=}") + case_no = self.collection.cases.count() + 1 + logging.debug(f"{case_no=}") + logging.debug(f"{instance=}") + + # Create through model + #cd = CaseDetail(case=instance, collection=exam, sort_order=case_no) + self.collection.cases.add(instance, through_defaults={"sort_order": case_no}) + return instance diff --git a/atlas/templates/atlas/case_display_block.html b/atlas/templates/atlas/case_display_block.html index 5e86d483..d3ed1c3b 100755 --- a/atlas/templates/atlas/case_display_block.html +++ b/atlas/templates/atlas/case_display_block.html @@ -117,8 +117,8 @@ Add collection

diff --git a/atlas/templates/atlas/case_form.html b/atlas/templates/atlas/case_form.html index 74032194..0e43584b 100755 --- a/atlas/templates/atlas/case_form.html +++ b/atlas/templates/atlas/case_form.html @@ -51,6 +51,10 @@ {% block content %}

Submit Case

Use this form to create a atlas case. Existing associated image sets can be added using this form. + + {% if form.collection %} + + {% endif %}
{% csrf_token %} diff --git a/atlas/templates/atlas/collection_headers.html b/atlas/templates/atlas/collection_headers.html index 119c234d..d1784444 100644 --- a/atlas/templates/atlas/collection_headers.html +++ b/atlas/templates/atlas/collection_headers.html @@ -3,7 +3,8 @@
Collection: {{collection.name}}-> Overview / Mark / Scores / - Candidates + Candidates / + Add New Case
Edit \ Delete diff --git a/atlas/templates/atlas/series_form.html b/atlas/templates/atlas/series_form.html index e9f0c559..3cecd608 100755 --- a/atlas/templates/atlas/series_form.html +++ b/atlas/templates/atlas/series_form.html @@ -486,8 +486,8 @@

Images:

- -
Drop images here (or use the buttons below). Dropping images here will overwrite + +
Drop images here (or use the buttons below). Dropping images here will overwrite existing images in the series. When drag and dropping make sure to drag the first image or the order will not be maintained. Note: dicom images are often not exported in order (although their order can be automatically detected once uploaded) diff --git a/atlas/urls.py b/atlas/urls.py index 0050762c..3d07ca3c 100755 --- a/atlas/urls.py +++ b/atlas/urls.py @@ -127,6 +127,7 @@ urlpatterns = [ path("case//scrap", views.atlas_scrap, name="case_scrap"), path("case//delete", views.CaseDelete.as_view(), name="case_delete"), path("case/create/", views.AtlasCreate.as_view(), name="case_create"), + path("case/collection//create", views.AtlasCreate.as_view(), name="atlas_create_exam"), path("series/create", views.SeriesCreate.as_view(), name="series_create"), path( "series//create", diff --git a/atlas/views.py b/atlas/views.py index 09980821..70d52ce2 100755 --- a/atlas/views.py +++ b/atlas/views.py @@ -645,13 +645,20 @@ class AtlasCreateBase(RevisionMixin, LoginRequiredMixin): # @login_required class AtlasCreate(AtlasCreateBase, CreateView): def get_initial(self): - # There has to be a better way... - try: - s = (i.pk for i in self.request.user.atlas_default.site.all()) - self.initial.update({"site": s}) - except AttributeError: - pass - return self.initial + if "pk" in self.kwargs: + initial = super().get_initial() + exam = get_object_or_404(CaseCollection, pk=self.kwargs["pk"]) + + initial["exams"] = [exam.id] + + return initial + ## There has to be a better way... + #try: + # s = (i.pk for i in self.request.user.atlas_default.site.all()) + # self.initial.update({"site": s}) + #except AttributeError: + # pass + #return self.initial class AtlasUpdate( diff --git a/rapids/templates/rapids/rapid_form.html b/rapids/templates/rapids/rapid_form.html index b9d2ee5f..4da158e0 100755 --- a/rapids/templates/rapids/rapid_form.html +++ b/rapids/templates/rapids/rapid_form.html @@ -6,38 +6,38 @@