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,
UserAnswer,
Exam,
Modality,
#Modality,
Region,
BodyPart,
Structure,
@@ -56,7 +56,6 @@ admin.site.register(AnatomyQuestion, AnatomyAdmin)
admin.site.register(QuestionType)
admin.site.register(UserAnswer)
admin.site.register(Exam, ExamAdmin)
admin.site.register(Modality)
admin.site.register(Region)
admin.site.register(BodyPart)
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
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 helpers.images import image_as_base64
@@ -57,11 +57,12 @@ class Region(models.Model):
return self.region
class Modality(models.Model):
modality = models.CharField(max_length=200)
def __str__(self):
return self.modality
## TODO: ??? Move to generic app
#class Modality(models.Model):
# modality = models.CharField(max_length=200)
#
# def __str__(self):
# return self.modality
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 helpers.images import image_as_base64, pretty_print_dicom
from anatomy.models import Modality
from generic.models import (
CidUser,
@@ -44,6 +43,7 @@ from generic.models import (
QuestionNote,
SeriesBase,
SeriesImageBase,
Modality,
)
# from generic.models import Examination, Site, Condition, Sign
+49 -47
View File
@@ -34,8 +34,8 @@
</a>
<br>
<span class="series-block-popup-link">
<a href="#"
onclick="return window.create_popup_window('/atlas/series/{{series.pk}}', 'Series')">Popup</a>
<a href="#"
onclick="return window.create_popup_window('/atlas/series/{{series.pk}}', 'Series')">Popup</a>
</span>
</span>
</span>
@@ -53,51 +53,53 @@
<li>{{con.get_link}}</li>
{% endfor %}
</ul>
<details>
<summary><b>Findings</b></summary>
{% if case.series.all %}
See individual series for more details.<br/>
{% for series in case.series.all %}
{% for finding in series.findings.all %}
<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 %}
<div>
<details>
<summary><b>Findings</b></summary>
{% if case.series.all %}
{% for series in case.series.all %}
{% for finding in series.findings.all %}
<div class="finding-box">
<span>
Findings: {% for f in finding.findings.all %}
{{f.get_link}},&nbsp;
{% endfor %}
<a href="{{series.get_absolute_url}}">Series {{series.pk}}</a>: {{series.get_examination_full}} |&nbsp;
</span>
{% endif %}
{% if finding.structures.all %}
<span>
Structure: {% for s in finding.structures.all %}
{{s.get_link}},&nbsp;
{% endfor %}
</span>
{% endif %}
{% if finding.description %}
<span>
Description: {{finding.description}}
</span>
{% endif %}
</div>
{% if finding.findings.all %}
<span>
Findings: {% for f in finding.findings.all %}
{{f.get_link}},&nbsp;
{% endfor %}
</span>
{% endif %}
{% if finding.structures.all %}
<span>
Structure: {% for s in finding.structures.all %}
{{s.get_link}},&nbsp;
{% endfor %}
</span>
{% endif %}
{% if finding.description %}
<span>
Description: {{finding.description}}
</span>
{% endif %}
</div>
{% endfor %}
{% endfor %}
{% endfor %}
{% else %}
No findings associated with case.
{% endif %}
</details>
<details open>
<summary><b>Differentials</b></summary>
<ul>
{% for diff in case.differentialcase.all %}
<li>{{diff.condition.get_link}} - {{diff.text}}</li>
{% endfor %}
</ul>
</details>
{% else %}
No series associated with case.
{% endif %}
</details>
</div>
<div>
<details open>
<summary><b>Differentials</b></summary>
<ul>
{% for diff in case.differentialcase.all %}
<li>{{diff.condition.get_link}} - {{diff.text}}</li>
{% endfor %}
</ul>
</details>
</div>
</p>
</div>
Subspecialty:
@@ -114,9 +116,9 @@
</ul>
<b>Collections:</b>
<button
hx-get="{% url 'atlas:case_collection_form' case.pk %}"
hx-target="#collection-form">
Add collection</button>
hx-get="{% url 'atlas:case_collection_form' case.pk %}"
hx-target="#collection-form">
Add collection</button>
<div id="collection-form"></div>
<ul>
{% for collection in case.casecollection_set.all %}
+2 -1
View File
@@ -6,7 +6,7 @@ from .models import (
CidUserGroup,
Examination,
#Sign,
#Condition,
Modality,
Plane,
Contrast,
QuestionNote,
@@ -35,3 +35,4 @@ admin.site.register(CidUserGroup)
admin.site.register(UserProfile)
admin.site.register(UserGrades)
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 generic.models import CidUser, Supervisor, UserGrades
from generic.models import CidUser, Examination, Supervisor, UserGrades
class CidUserFilter(django_filters.FilterSet):
@@ -61,3 +61,22 @@ class UserUserFilter(django_filters.FilterSet):
# return parent.filter(active=True)
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):
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:
model = Examination
fields = ["examination"]
fields = ["examination", "modality"]
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
import json
import os
from typing import Tuple
from django.contrib.auth.mixins import LoginRequiredMixin
from django.db import models
@@ -42,6 +43,11 @@ def findMiddle(input_list):
else:
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):
plane = models.CharField(max_length=200, unique=True)
@@ -66,6 +72,8 @@ class Contrast(models.Model):
class Examination(models.Model):
examination = models.CharField(max_length=200, unique=True)
modality = models.ForeignKey(Modality, on_delete=models.SET_NULL, null=True)
def __str__(self):
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 generic.models import CidUser
from generic.models import CidUser, Examination
from django.contrib.auth.models import User
@@ -259,3 +259,27 @@ class UserUserTable(tables.Table):
record.username,
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 = [
# 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/ajax/get_examination_id",
+36 -2
View File
@@ -44,9 +44,10 @@ from django_tables2.views import SingleTableMixin
from reversion.views import RevisionMixin
from atlas.models import CaseCollection
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 (
CidUserForm,
@@ -177,6 +178,21 @@ def create_examination(request):
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
def get_examination_id(request):
@@ -2954,3 +2970,21 @@ class SupervisorCreate(CidManagerRequiredMixin, CreateView):
class SupervisorList(CidManagerRequiredMixin, ListView):
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 helpers.images import image_as_base64, pretty_print_dicom
from anatomy.models import Modality
from generic.models import (
CidUser,
CidUserGroup,
@@ -41,6 +39,7 @@ from generic.models import (
SeriesImageBase,
UserAnswerBase,
UserUserGroup,
Modality
)
# from generic.models import Examination, Site, Condition, Sign
+4
View File
@@ -31,6 +31,10 @@
<div>
{% if request.user.is_staff %}
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 %}
</div>
{% endblock %}