migrate examinations to generic model (and start adding mangament)

This commit is contained in:
Ross
2023-06-19 12:37:55 +01:00
parent 154494e4b0
commit 6c1fd3ae2f
21 changed files with 429 additions and 64 deletions
+1 -2
View File
@@ -6,7 +6,7 @@ from .models import (
Answer, Answer,
UserAnswer, UserAnswer,
Exam, Exam,
Modality, #Modality,
Region, Region,
BodyPart, BodyPart,
Structure, Structure,
@@ -56,7 +56,6 @@ admin.site.register(AnatomyQuestion, AnatomyAdmin)
admin.site.register(QuestionType) admin.site.register(QuestionType)
admin.site.register(UserAnswer) admin.site.register(UserAnswer)
admin.site.register(Exam, ExamAdmin) admin.site.register(Exam, ExamAdmin)
admin.site.register(Modality)
admin.site.register(Region) admin.site.register(Region)
admin.site.register(BodyPart) admin.site.register(BodyPart)
admin.site.register(Structure) admin.site.register(Structure)
@@ -0,0 +1,47 @@
# Generated by Django 4.1.4 on 2023-06-19 10:51
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("longs", "0002_alter_longseries_modality"),
("atlas", "0003_alter_series_modality"),
("generic", "0002_modality_examination_modality"),
("anatomy", "0002_initial"),
]
operations = [
# migrations.DeleteModel(
# name='Modality',
# ),
migrations.SeparateDatabaseAndState(
state_operations=[
migrations.DeleteModel(
name="Modality",
),
],
database_operations=[
migrations.AlterModelTable(
name="Modality",
table="generic_modality",
),
],
),
migrations.SeparateDatabaseAndState(
state_operations=[
migrations.AlterField(
model_name="anatomyquestion",
name="modality",
field=models.ForeignKey(
help_text="Modality of the image",
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="generic.modality",
),
),
],
database_operations=[],
),
]
+7 -6
View File
@@ -16,7 +16,7 @@ from sortedm2m.fields import SortedManyToManyField
import string import string
from generic.models import CidUser, CidUserGroup, ExamUserStatus, Examination, ExamBase, QuestionBase, QuestionNote, UserAnswerBase, UserUserGroup from generic.models import CidUser, CidUserGroup, ExamUserStatus, Examination, ExamBase, QuestionBase, QuestionNote, UserAnswerBase, UserUserGroup, Modality
from collections import defaultdict from collections import defaultdict
from helpers.images import image_as_base64 from helpers.images import image_as_base64
@@ -57,11 +57,12 @@ class Region(models.Model):
return self.region return self.region
class Modality(models.Model): ## TODO: ??? Move to generic app
modality = models.CharField(max_length=200) #class Modality(models.Model):
# modality = models.CharField(max_length=200)
def __str__(self): #
return self.modality # def __str__(self):
# return self.modality
class QuestionType(models.Model): class QuestionType(models.Model):
@@ -0,0 +1,29 @@
# Generated by Django 4.1.4 on 2023-06-19 10:51
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("generic", "0002_modality_examination_modality"),
("atlas", "0002_initial"),
]
operations = [
migrations.SeparateDatabaseAndState(
state_operations=[
migrations.AlterField(
model_name="series",
name="modality",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="atlas_series_modality",
to="generic.modality",
),
),
],
database_operations=[],
)
]
+1 -1
View File
@@ -29,7 +29,6 @@ import string
from collections import defaultdict from collections import defaultdict
from helpers.images import image_as_base64, pretty_print_dicom from helpers.images import image_as_base64, pretty_print_dicom
from anatomy.models import Modality
from generic.models import ( from generic.models import (
CidUser, CidUser,
@@ -44,6 +43,7 @@ from generic.models import (
QuestionNote, QuestionNote,
SeriesBase, SeriesBase,
SeriesImageBase, SeriesImageBase,
Modality,
) )
# from generic.models import Examination, Site, Condition, Sign # from generic.models import Examination, Site, Condition, Sign
+49 -47
View File
@@ -34,8 +34,8 @@
</a> </a>
<br> <br>
<span class="series-block-popup-link"> <span class="series-block-popup-link">
<a href="#" <a href="#"
onclick="return window.create_popup_window('/atlas/series/{{series.pk}}', 'Series')">Popup</a> onclick="return window.create_popup_window('/atlas/series/{{series.pk}}', 'Series')">Popup</a>
</span> </span>
</span> </span>
</span> </span>
@@ -53,51 +53,53 @@
<li>{{con.get_link}}</li> <li>{{con.get_link}}</li>
{% endfor %} {% endfor %}
</ul> </ul>
<details> <div>
<summary><b>Findings</b></summary> <details>
{% if case.series.all %} <summary><b>Findings</b></summary>
See individual series for more details.<br/> {% if case.series.all %}
{% for series in case.series.all %} {% for series in case.series.all %}
{% for finding in series.findings.all %} {% for finding in series.findings.all %}
<div class="finding-box"> <div class="finding-box">
<span>
<a href="{{series.get_absolute_url}}">Series {{series.pk}}</a>: {{series.get_examination_full}} |&nbsp;
</span>
{% if finding.findings.all %}
<span> <span>
Findings: {% for f in finding.findings.all %} <a href="{{series.get_absolute_url}}">Series {{series.pk}}</a>: {{series.get_examination_full}} |&nbsp;
{{f.get_link}},&nbsp;
{% endfor %}
</span> </span>
{% endif %} {% if finding.findings.all %}
{% if finding.structures.all %} <span>
<span> Findings: {% for f in finding.findings.all %}
Structure: {% for s in finding.structures.all %} {{f.get_link}},&nbsp;
{{s.get_link}},&nbsp; {% endfor %}
{% endfor %} </span>
</span> {% endif %}
{% endif %} {% if finding.structures.all %}
{% if finding.description %} <span>
<span> Structure: {% for s in finding.structures.all %}
Description: {{finding.description}} {{s.get_link}},&nbsp;
</span> {% endfor %}
{% endif %} </span>
</div> {% endif %}
{% if finding.description %}
<span>
Description: {{finding.description}}
</span>
{% endif %}
</div>
{% endfor %}
{% endfor %} {% endfor %}
{% endfor %} {% else %}
{% else %} No series associated with case.
No findings associated with case. {% endif %}
{% endif %} </details>
</details> </div>
<details open> <div>
<summary><b>Differentials</b></summary> <details open>
<ul> <summary><b>Differentials</b></summary>
{% for diff in case.differentialcase.all %} <ul>
<li>{{diff.condition.get_link}} - {{diff.text}}</li> {% for diff in case.differentialcase.all %}
{% endfor %} <li>{{diff.condition.get_link}} - {{diff.text}}</li>
</ul> {% endfor %}
</details> </ul>
</details>
</div>
</p> </p>
</div> </div>
Subspecialty: Subspecialty:
@@ -114,9 +116,9 @@
</ul> </ul>
<b>Collections:</b> <b>Collections:</b>
<button <button
hx-get="{% url 'atlas:case_collection_form' case.pk %}" hx-get="{% url 'atlas:case_collection_form' case.pk %}"
hx-target="#collection-form"> hx-target="#collection-form">
Add collection</button> Add collection</button>
<div id="collection-form"></div> <div id="collection-form"></div>
<ul> <ul>
{% for collection in case.casecollection_set.all %} {% for collection in case.casecollection_set.all %}
+2 -1
View File
@@ -6,7 +6,7 @@ from .models import (
CidUserGroup, CidUserGroup,
Examination, Examination,
#Sign, #Sign,
#Condition, Modality,
Plane, Plane,
Contrast, Contrast,
QuestionNote, QuestionNote,
@@ -35,3 +35,4 @@ admin.site.register(CidUserGroup)
admin.site.register(UserProfile) admin.site.register(UserProfile)
admin.site.register(UserGrades) admin.site.register(UserGrades)
admin.site.register(Supervisor) admin.site.register(Supervisor)
admin.site.register(Modality)
+20 -1
View File
@@ -3,7 +3,7 @@ import django_filters
from django.contrib.auth.models import User from django.contrib.auth.models import User
from generic.models import CidUser, Supervisor, UserGrades from generic.models import CidUser, Examination, Supervisor, UserGrades
class CidUserFilter(django_filters.FilterSet): class CidUserFilter(django_filters.FilterSet):
@@ -61,3 +61,22 @@ class UserUserFilter(django_filters.FilterSet):
# return parent.filter(active=True) # return parent.filter(active=True)
return parent return parent
class ExaminationFilter(django_filters.FilterSet):
class Meta:
model = Examination
fields = {"examination": ["icontains"], "modality": ["exact"]}
def __init__(
self,
data=None,
queryset=None,
prefix=None,
strict=None,
user=None,
request=None,
):
super(ExaminationFilter, self).__init__(
data=data, queryset=queryset, prefix=prefix, request=request
)
pass
+9 -1
View File
@@ -115,9 +115,17 @@ class ExamAuthorFormMixin(ModelForm):
class ExaminationForm(ModelForm): class ExaminationForm(ModelForm):
def __init__(self, *args, **kwargs):
# Where is this coming from?
if "user" in kwargs:
user = kwargs.pop("user")
ModelForm.__init__(self, *args, **kwargs)
super(ExaminationForm, self).__init__(*args, **kwargs)
class Meta: class Meta:
model = Examination model = Examination
fields = ["examination"] fields = ["examination", "modality"]
class QuestionNoteForm(ModelForm): class QuestionNoteForm(ModelForm):
@@ -0,0 +1,47 @@
# Generated by Django 4.1.4 on 2023-06-19 10:51
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("generic", "0001_initial"),
]
operations = [
migrations.SeparateDatabaseAndState(
state_operations=[
migrations.CreateModel(
name="Modality",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("modality", models.CharField(max_length=200)),
],
),
],
database_operations=[],
),
migrations.SeparateDatabaseAndState(
state_operations=[
migrations.AddField(
model_name="examination",
name="modality",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="generic.modality",
),
),
],
database_operations=[],
),
]
@@ -0,0 +1,21 @@
# Generated by Django 4.1.4 on 2023-06-19 11:13
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("generic", "0002_modality_examination_modality"),
]
operations = [
migrations.SeparateDatabaseAndState(
state_operations=[
migrations.RemoveField(
model_name="examination",
name="modality",
),
],
database_operations=[],
),
]
@@ -0,0 +1,19 @@
# Generated by Django 4.1.4 on 2023-06-19 11:13
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('generic', '0003_remove_examination_modality'),
]
operations = [
migrations.AddField(
model_name='examination',
name='modality',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='generic.modality'),
),
]
+8
View File
@@ -1,5 +1,6 @@
from collections import defaultdict from collections import defaultdict
import json import json
import os
from typing import Tuple from typing import Tuple
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.db import models from django.db import models
@@ -42,6 +43,11 @@ def findMiddle(input_list):
else: else:
return input_list[int(middle)] return input_list[int(middle)]
class Modality(models.Model):
modality = models.CharField(max_length=200)
def __str__(self):
return self.modality
class Plane(models.Model): class Plane(models.Model):
plane = models.CharField(max_length=200, unique=True) plane = models.CharField(max_length=200, unique=True)
@@ -66,6 +72,8 @@ class Contrast(models.Model):
class Examination(models.Model): class Examination(models.Model):
examination = models.CharField(max_length=200, unique=True) examination = models.CharField(max_length=200, unique=True)
modality = models.ForeignKey(Modality, on_delete=models.SET_NULL, null=True)
def __str__(self): def __str__(self):
return self.examination return self.examination
+25 -1
View File
@@ -7,7 +7,7 @@ from easy_thumbnails.files import get_thumbnailer
from easy_thumbnails.exceptions import InvalidImageFormatError from easy_thumbnails.exceptions import InvalidImageFormatError
from generic.models import CidUser from generic.models import CidUser, Examination
from django.contrib.auth.models import User from django.contrib.auth.models import User
@@ -259,3 +259,27 @@ class UserUserTable(tables.Table):
record.username, record.username,
record.email, record.email,
) )
class ExaminationTable(tables.Table):
examination = tables.Column(
linkify=("generic:examination_detail", {"pk": tables.A("pk")}),
verbose_name="Condition",
)
#edit = tables.LinkColumn(
# "generic:examination_update", text="Edit", args=[A("pk")], orderable=False
#)
delete = tables.LinkColumn(
"generic:examination_delete", text="Delete", args=[A("pk")], orderable=False
)
selection = tables.CheckBoxColumn(accessor="pk", orderable=False)
# synonym = tables.ManyToManyColumn(verbose_name="Synonyms")
#synonym = tables.Column(empty_values=())
#parent = tables.ManyToManyColumn(verbose_name="Parents")
class Meta:
model = Examination
template_name = "django_tables2/bootstrap4.html"
#fields = ("primary", "subspecialty")
sequence = ("examination",)
+44
View File
@@ -0,0 +1,44 @@
{% extends 'generic/base.html' %}
{% block content %}
<div class="floating-header">
<a href="{% url 'generic:examination_update' pk=examination.pk %}" title="Edit the Examination">Edit</a>
<a href="{% url 'generic:examination_delete' pk=examination.pk %}" title="Delete the Examination">Delete</a>
{% if request.user.is_superuser %}
<a href="{% url 'admin:generic_examination_change' examination.id %}"
title="Edit the Examination using the admin interface">Admin Edit</a>
{% endif %}
</div>
<div>
<h3>Name: {{examination.examination}}</h3>
Modality: {{examination.modality}}
</div>
{% if examination.atlas_series_examination_set.all %}
<h4>Associated Atlas Cases</h4>
<ul>
{% for case in examination.atlas_series_examination_set.all %}
<li>{{case}}</li>
{% endfor %}
</ul>
{% endif %}
{% if examination.longs_series_examination_set.all %}
<h4>Associated Longs Cases</h4>
<ul>
{% for case in examination.longs_series_examination_set.all %}
<li>{{case}}</li>
{% endfor %}
</ul>
{% endif %}
{% endblock %}
{% block js %}
{% endblock %}
+26
View File
@@ -0,0 +1,26 @@
{% extends "atlas/base.html" %}
<!-- {% load static from static %} -->
{% block css %}
{% endblock %}
{% block js %}
<!--<script type="text/javascript" src="/admin/jsi18n/"></script>-->
{{form.media}}
<script type="text/javascript">
</script>
<!-- {{ form.media }} -->
{% endblock %}
{% block content %}
<h2>Add/Edit Examination</h2>
Use this form to create or edit a examination.
<form action="" method="post" enctype="multipart/form-data" id="examination-form">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<input type="submit" class="submit-button" value="Submit" name="submit">
</form>
{% endblock %}
+4
View File
@@ -9,6 +9,10 @@ app_name = "generic"
urlpatterns = [ urlpatterns = [
# path('', views.question_list, name='question_list'), # path('', views.question_list, name='question_list'),
path("examination/", views.ExaminationView.as_view(), name="examination_view"),
path("examination/<int:pk>", views.examination_detail, name="examination_detail"),
path("examination/<int:pk>/delete", views.ExaminationDelete.as_view(), name="examination_delete"),
path("examination/<int:pk>/update", views.ExaminationUpdate.as_view(), name="examination_update"),
path("examination/create/", views.create_examination, name="create_examination"), path("examination/create/", views.create_examination, name="create_examination"),
path( path(
"examination/ajax/get_examination_id", "examination/ajax/get_examination_id",
+36 -2
View File
@@ -44,9 +44,10 @@ from django_tables2.views import SingleTableMixin
from reversion.views import RevisionMixin from reversion.views import RevisionMixin
from atlas.models import CaseCollection from atlas.models import CaseCollection
from generic.decorators import user_is_cid_user_manager from generic.decorators import user_is_cid_user_manager
from generic.filters import CidUserFilter from generic.filters import CidUserFilter, ExaminationFilter
from generic.tables import CidUserExamTable, CidUserTable from generic.tables import CidUserExamTable, CidUserTable, ExaminationTable
from generic.mixins import SuperuserRequiredMixin
from .forms import ( from .forms import (
CidUserForm, CidUserForm,
@@ -177,6 +178,21 @@ def create_examination(request):
request, "rapids/create_simple.html", {"form": form, "name": "Examination"} request, "rapids/create_simple.html", {"form": form, "name": "Examination"}
) )
@login_required
@user_passes_test(lambda u: u.is_superuser)
def examination_detail(request, pk):
examination = get_object_or_404(Examination, pk=pk)
# logging.debug(atlas.subspecialty.first().name.all())
return render(
request,
"generic/examination_detail.html",
{
"examination": examination,
},
)
@csrf_exempt @csrf_exempt
def get_examination_id(request): def get_examination_id(request):
@@ -2954,3 +2970,21 @@ class SupervisorCreate(CidManagerRequiredMixin, CreateView):
class SupervisorList(CidManagerRequiredMixin, ListView): class SupervisorList(CidManagerRequiredMixin, ListView):
model = Supervisor model = Supervisor
class ExaminationView(SuperuserRequiredMixin, SingleTableMixin, FilterView):
model = Examination
table_class = ExaminationTable
template_name = "atlas/view.html"
filterset_class = ExaminationFilter
class ExaminationDelete(RevisionMixin, SuperuserRequiredMixin, DeleteView):
model = Examination
template_name = "confirm_delete.html"
success_url = reverse_lazy("generic:examination_view")
class ExaminationUpdate(UpdateView, SuperuserRequiredMixin):
model = Examination
form_class = ExaminationForm
@@ -0,0 +1,29 @@
# Generated by Django 4.1.4 on 2023-06-19 10:51
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
("generic", "0002_modality_examination_modality"),
("longs", "0001_initial"),
]
operations = [
migrations.SeparateDatabaseAndState(
state_operations=[
migrations.AlterField(
model_name="longseries",
name="modality",
field=models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="series_modality",
to="generic.modality",
),
),
],
database_operations=[],
)
]
+1 -2
View File
@@ -25,8 +25,6 @@ import string
from collections import defaultdict from collections import defaultdict
from helpers.images import image_as_base64, pretty_print_dicom from helpers.images import image_as_base64, pretty_print_dicom
from anatomy.models import Modality
from generic.models import ( from generic.models import (
CidUser, CidUser,
CidUserGroup, CidUserGroup,
@@ -41,6 +39,7 @@ from generic.models import (
SeriesImageBase, SeriesImageBase,
UserAnswerBase, UserAnswerBase,
UserUserGroup, UserUserGroup,
Modality
) )
# from generic.models import Examination, Site, Condition, Sign # from generic.models import Examination, Site, Condition, Sign
+4
View File
@@ -31,6 +31,10 @@
<div> <div>
{% if request.user.is_staff %} {% if request.user.is_staff %}
Manage users <a href="{% url 'accounts_list'%}">here</a> and candidates <a href="{% url 'generic:manage_cids'%}">here</a> Manage users <a href="{% url 'accounts_list'%}">here</a> and candidates <a href="{% url 'generic:manage_cids'%}">here</a>
<p>Manage <a href="{% url 'generic:examination_view' %}">Examinations</a>
</p>
{% endif %} {% endif %}
</div> </div>
{% endblock %} {% endblock %}