diff --git a/anatomy/static/css/anatomy.css b/anatomy/static/css/anatomy.css index 512012f6..3f0bca3b 100644 --- a/anatomy/static/css/anatomy.css +++ b/anatomy/static/css/anatomy.css @@ -810,4 +810,8 @@ input { .finding-box { display: flex; border: 1px dashed gray; +} + +.delete-finding-link { + float: right; } \ No newline at end of file diff --git a/atlas/forms.py b/atlas/forms.py index bc9c1570..3dd5e45c 100755 --- a/atlas/forms.py +++ b/atlas/forms.py @@ -25,12 +25,6 @@ from tinymce.widgets import TinyMCE from dal import autocomplete -class ExaminationForm(ModelForm): - class Meta: - model = Examination - fields = ["examination"] - - class SeriesFindingForm(ModelForm): class Meta: model = SeriesFinding @@ -129,6 +123,13 @@ class SeriesForm(ModelForm): exclude = ["author"] + widgets = { + "examination": autocomplete.ModelSelect2Multiple( + url="generic:examination-autocomplete" + ), + } + + class CaseForm(ModelForm): # exams = ModelMultipleChoiceField(required=False, queryset=Exam.objects.all(),widget=FilteredSelectMultiple(verbose_name="Exams", is_stacked=False)) diff --git a/atlas/migrations/0014_remove_case_sign.py b/atlas/migrations/0014_remove_case_sign.py new file mode 100644 index 00000000..920dd994 --- /dev/null +++ b/atlas/migrations/0014_remove_case_sign.py @@ -0,0 +1,17 @@ +# Generated by Django 3.2.8 on 2021-12-01 19:32 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('atlas', '0013_rename_structure_structure_name'), + ] + + operations = [ + migrations.RemoveField( + model_name='case', + name='sign', + ), + ] diff --git a/atlas/templates/atlas/series_form.html b/atlas/templates/atlas/series_form.html index 61ad33cf..2e02bd57 100755 --- a/atlas/templates/atlas/series_form.html +++ b/atlas/templates/atlas/series_form.html @@ -304,6 +304,43 @@ } } + + function extractDicomStudyDescription(byteArray) { + // We need to setup a try/catch block because parseDicom will throw an exception + // if you attempt to parse a non dicom part 10 file (or one that is corrupted) + try { + // parse byteArray into a DataSet object using the parseDicom library + var dataSet = dicomParser.parseDicom(byteArray); + + // dataSet contains the parsed elements. Each element is available via a property + // in the dataSet.elements object. The property name is based on the elements group + // and element in the following format: xggggeeee where gggg is the group number + // and eeee is the element number with lowercase hex characters. + + // To access the data for an element, we need to know its type and its tag. + // We will get the sopInstanceUid from the file which is a string and + // has the tag (0020,000D) + var study_description = dataSet.string('x00081030').toLowerCase(); + console.log("STUDY", study_description); + + // try to match with a study description (if not already selected) + if ($(`#id_examination_to option`).length < 1) { + option = $(`#id_examination_from option[title*='${study_description}' i]`); + + if (option.length) { + option.appendTo($("#id_examination_to")); + toastr.success(`Examination set to ${option.attr('title')} from dicom data`); + } else { + toastr.warning( + `Unable to set examination ${study_description} from dicom data (it needs creating)`); + + } + } + + } catch (err) { + console.log(err); + } + } {{ form.media }} diff --git a/atlas/templates/atlas/series_viewer.html b/atlas/templates/atlas/series_viewer.html index 0dba8a2b..2b366040 100755 --- a/atlas/templates/atlas/series_viewer.html +++ b/atlas/templates/atlas/series_viewer.html @@ -23,10 +23,10 @@ {% for finding in series.findings.all %}
- Findings: {{finding.findings.all|join:", "}}
- Structure: {{finding.structure.all|join:", "}}
+ Finding(s): {{finding.findings.all|join:", "}}
+ Structure(s): {{finding.structure.all|join:", "}}
Description: {{finding.description}}
- Delete finding + Delete finding
{% endfor %} diff --git a/generic/urls.py b/generic/urls.py index ffb845e3..0241b4c6 100755 --- a/generic/urls.py +++ b/generic/urls.py @@ -1,4 +1,6 @@ from django.urls import path, include + +from generic.models import Examination from . import views @@ -6,14 +8,22 @@ app_name = "generic" urlpatterns = [ # path('', views.question_list, name='question_list'), - path("examination/create/", - views.create_examination, - name="create_examination"), - path("examination/ajax/get_examination_id", - views.get_examination_id, - name="get_examination_id"), - path("generic_exam_json_edit", - views.generic_exam_json_edit, - name="generic_exam_json_edit"), - + path("examination/create/", views.create_examination, name="create_examination"), + path( + "examination/ajax/get_examination_id", + views.get_examination_id, + name="get_examination_id", + ), + path( + "generic_exam_json_edit", + views.generic_exam_json_edit, + name="generic_exam_json_edit", + ), + path( + "examination-autocomplete", + views.ExaminationAutocomplete.as_view( + model=Examination, create_field="examination" + ), + name="examination-autocomplete", + ), ] diff --git a/generic/views.py b/generic/views.py index f851efb8..4583c411 100644 --- a/generic/views.py +++ b/generic/views.py @@ -1,3 +1,4 @@ +from dal import autocomplete from django.contrib.auth.models import User from django.contrib.contenttypes.models import ContentType from django.forms.models import model_to_dict @@ -5,7 +6,7 @@ from django.shortcuts import render, get_object_or_404, redirect from django.contrib.auth.decorators import login_required, user_passes_test from django.views.decorators.csrf import csrf_exempt -from django.core.exceptions import PermissionDenied +from django.core.exceptions import PermissionDenied, FieldError from django.http import Http404, JsonResponse from django.http import HttpResponseRedirect, HttpResponse @@ -806,4 +807,22 @@ class ExamCloneMixin(): object = form.save() object.exam_questions.set(self.exam_questions) object.save() - return HttpResponseRedirect(object.get_absolute_url()) \ No newline at end of file + return HttpResponseRedirect(object.get_absolute_url()) + + +class ExaminationAutocomplete(autocomplete.Select2QuerySetView): + def get_queryset(self): + # Don't forget to filter out results depending on the visitor ! + if not self.request.user.is_authenticated: + return Examination.objects.none() + + qs = Examination.objects.all() + + if self.q: + # This raises a fielderror which breaks creating a new item if not caught + try: + qs = qs.filter(name__icontains=self.q) + except FieldError: + return Examination.objects.none() + + return qs \ No newline at end of file diff --git a/static/css/anatomy.css b/static/css/anatomy.css index 512012f6..3f0bca3b 100644 --- a/static/css/anatomy.css +++ b/static/css/anatomy.css @@ -810,4 +810,8 @@ input { .finding-box { display: flex; border: 1px dashed gray; +} + +.delete-finding-link { + float: right; } \ No newline at end of file