Add Procedure model integration: create views, forms, and templates; update URLs and search functionality
This commit is contained in:
@@ -15,6 +15,7 @@ from .models import (
|
|||||||
Structure,
|
Structure,
|
||||||
PathologicalProcess,
|
PathologicalProcess,
|
||||||
Presentation,
|
Presentation,
|
||||||
|
Procedure,
|
||||||
UncategorisedDicom,
|
UncategorisedDicom,
|
||||||
SeriesDetail,
|
SeriesDetail,
|
||||||
Resource,
|
Resource,
|
||||||
@@ -51,6 +52,7 @@ admin.site.register(UncategorisedDicom)
|
|||||||
admin.site.register(SeriesDetail)
|
admin.site.register(SeriesDetail)
|
||||||
admin.site.register(Resource)
|
admin.site.register(Resource)
|
||||||
admin.site.register(CaseDisplaySet)
|
admin.site.register(CaseDisplaySet)
|
||||||
|
admin.site.register(Procedure)
|
||||||
admin.site.register(UserReportAnswer)
|
admin.site.register(UserReportAnswer)
|
||||||
admin.site.register(CidReportAnswer)
|
admin.site.register(CidReportAnswer)
|
||||||
|
|
||||||
|
|||||||
+7
-5
@@ -42,6 +42,7 @@ from atlas.models import (
|
|||||||
Subspecialty,
|
Subspecialty,
|
||||||
Presentation,
|
Presentation,
|
||||||
PathologicalProcess,
|
PathologicalProcess,
|
||||||
|
Procedure,
|
||||||
UncategorisedDicom,
|
UncategorisedDicom,
|
||||||
UserReportAnswer,
|
UserReportAnswer,
|
||||||
CaseDisplaySet,
|
CaseDisplaySet,
|
||||||
@@ -395,6 +396,12 @@ class PathologicalProcessForm(ModelForm):
|
|||||||
exclude = []
|
exclude = []
|
||||||
|
|
||||||
|
|
||||||
|
class ProcedureForm(ModelForm):
|
||||||
|
class Meta:
|
||||||
|
model = Procedure
|
||||||
|
exclude = []
|
||||||
|
|
||||||
|
|
||||||
class SeriesFindingForm(ModelForm):
|
class SeriesFindingForm(ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = SeriesFinding
|
model = SeriesFinding
|
||||||
@@ -617,7 +624,6 @@ class CaseForm(ModelForm):
|
|||||||
"presentation",
|
"presentation",
|
||||||
"discussion",
|
"discussion",
|
||||||
"report",
|
"report",
|
||||||
"procedures",
|
|
||||||
"condition",
|
"condition",
|
||||||
"pathological_process",
|
"pathological_process",
|
||||||
"open_access",
|
"open_access",
|
||||||
@@ -650,7 +656,6 @@ class CaseForm(ModelForm):
|
|||||||
"presentation",
|
"presentation",
|
||||||
"discussion",
|
"discussion",
|
||||||
"condition",
|
"condition",
|
||||||
"procedures",
|
|
||||||
"pathological_process",
|
"pathological_process",
|
||||||
"report",
|
"report",
|
||||||
"open_access",
|
"open_access",
|
||||||
@@ -679,9 +684,6 @@ class CaseForm(ModelForm):
|
|||||||
"subspecialty": CheckboxSelectMultiple(),
|
"subspecialty": CheckboxSelectMultiple(),
|
||||||
"pathological_process": CheckboxSelectMultiple(),
|
"pathological_process": CheckboxSelectMultiple(),
|
||||||
"previous_case": CaseSelect(),
|
"previous_case": CaseSelect(),
|
||||||
"procedures": autocomplete.ModelSelect2Multiple(
|
|
||||||
url="atlas:procedure-autocomplete"
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def clean_cimar_uuid(self):
|
def clean_cimar_uuid(self):
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ from .models import (
|
|||||||
Finding,
|
Finding,
|
||||||
Subspecialty,
|
Subspecialty,
|
||||||
Resource,
|
Resource,
|
||||||
|
Procedure,
|
||||||
)
|
)
|
||||||
|
|
||||||
from django.utils.html import format_html
|
from django.utils.html import format_html
|
||||||
@@ -395,6 +396,26 @@ class PathologicalProcessTable(SelectionTable):
|
|||||||
sequence = ("name",)
|
sequence = ("name",)
|
||||||
|
|
||||||
|
|
||||||
|
class ProcedureTable(SelectionTable):
|
||||||
|
name = tables.Column(
|
||||||
|
linkify=("atlas:procedure_detail", {"pk": tables.A("pk")}),
|
||||||
|
verbose_name="Procedure",
|
||||||
|
)
|
||||||
|
|
||||||
|
edit = tables.LinkColumn(
|
||||||
|
"atlas:procedure_update", text="Edit", args=[A("pk")], orderable=False
|
||||||
|
)
|
||||||
|
delete = tables.LinkColumn(
|
||||||
|
"atlas:procedure_delete", text="Delete", args=[A("pk")], orderable=False
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta(SelectionTable.Meta):
|
||||||
|
model = Procedure
|
||||||
|
template_name = "django_tables2/bootstrap4.html"
|
||||||
|
fields = ("name",)
|
||||||
|
sequence = ("name",)
|
||||||
|
|
||||||
|
|
||||||
class SubspecialtyTable(SelectionTable):
|
class SubspecialtyTable(SelectionTable):
|
||||||
name = tables.Column(
|
name = tables.Column(
|
||||||
linkify=("atlas:subspecialty_detail", {"pk": tables.A("pk")}),
|
linkify=("atlas:subspecialty_detail", {"pk": tables.A("pk")}),
|
||||||
|
|||||||
@@ -18,6 +18,8 @@
|
|||||||
<a href="{% url 'atlas:subspecialty_detail' item.pk %}">{{ item.name }}</a>
|
<a href="{% url 'atlas:subspecialty_detail' item.pk %}">{{ item.name }}</a>
|
||||||
{% elif group_name == 'presentations' %}
|
{% elif group_name == 'presentations' %}
|
||||||
<a href="{% url 'atlas:presentation_detail' item.pk %}">{{ item.name }}</a>
|
<a href="{% url 'atlas:presentation_detail' item.pk %}">{{ item.name }}</a>
|
||||||
|
{% elif group_name == 'procedures' %}
|
||||||
|
<a href="{% url 'atlas:procedure_detail' item.pk %}">{{ item.name }}</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ item.name }}
|
{{ item.name }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@@ -27,7 +29,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
{% if not results.conditions.exists and not results.findings.exists and not results.structures.exists and not results.subspecialties.exists and not results.presentations.exists %}
|
{% if not results.conditions.exists and not results.findings.exists and not results.structures.exists and not results.subspecialties.exists and not results.presentations.exists and not results.procedures.exists %}
|
||||||
<p class="text-muted">No matches found.</p>
|
<p class="text-muted">No matches found.</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -90,6 +90,7 @@
|
|||||||
<li><a class="dropdown-item" href="{% url 'atlas:subspecialty_view' %}">Subspecialties</a></li>
|
<li><a class="dropdown-item" href="{% url 'atlas:subspecialty_view' %}">Subspecialties</a></li>
|
||||||
<li><a class="dropdown-item" href="{% url 'atlas:presentation_view' %}">Presentations</a></li>
|
<li><a class="dropdown-item" href="{% url 'atlas:presentation_view' %}">Presentations</a></li>
|
||||||
<li><a class="dropdown-item" href="{% url 'atlas:pathological_process_view' %}">Pathological process</a></li>
|
<li><a class="dropdown-item" href="{% url 'atlas:pathological_process_view' %}">Pathological process</a></li>
|
||||||
|
<li><a class="dropdown-item" href="{% url 'atlas:procedure_view' %}">Procedures</a></li>
|
||||||
{% if request.user.is_staff %}
|
{% if request.user.is_staff %}
|
||||||
<li><hr class="dropdown-divider"></li>
|
<li><hr class="dropdown-divider"></li>
|
||||||
<li class="dropdown-item-text small text-muted px-3">Create</li>
|
<li class="dropdown-item-text small text-muted px-3">Create</li>
|
||||||
@@ -99,6 +100,7 @@
|
|||||||
<li><a class="dropdown-item" href="{% url 'atlas:subspecialty_create' %}">New subspecialty</a></li>
|
<li><a class="dropdown-item" href="{% url 'atlas:subspecialty_create' %}">New subspecialty</a></li>
|
||||||
<li><a class="dropdown-item" href="{% url 'atlas:presentation_create' %}">New presentation</a></li>
|
<li><a class="dropdown-item" href="{% url 'atlas:presentation_create' %}">New presentation</a></li>
|
||||||
<li><a class="dropdown-item" href="{% url 'atlas:pathological_process_create' %}">New pathological process</a></li>
|
<li><a class="dropdown-item" href="{% url 'atlas:pathological_process_create' %}">New pathological process</a></li>
|
||||||
|
<li><a class="dropdown-item" href="{% url 'atlas:procedure_create' %}">New procedure</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
<li><a href="{% url 'atlas:subspecialty_view' %}">Subspecialties</li></a>
|
<li><a href="{% url 'atlas:subspecialty_view' %}">Subspecialties</li></a>
|
||||||
<li><a href="{% url 'atlas:presentation_view' %}">Presentations</li></a>
|
<li><a href="{% url 'atlas:presentation_view' %}">Presentations</li></a>
|
||||||
<li><a href="{% url 'atlas:pathological_process_view' %}">Pathological Process</li></a>
|
<li><a href="{% url 'atlas:pathological_process_view' %}">Pathological Process</li></a>
|
||||||
|
<li><a href="{% url 'atlas:procedure_view' %}">Procedures</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div class="mt-4 mb-3">
|
<div class="mt-4 mb-3">
|
||||||
@@ -94,6 +95,15 @@
|
|||||||
hx-indicator="#category-search-loading" />
|
hx-indicator="#category-search-loading" />
|
||||||
<label class="form-check-label" for="type-presentations">Presentations</label>
|
<label class="form-check-label" for="type-presentations">Presentations</label>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input" type="checkbox" id="type-procedures" name="types" value="procedures" checked
|
||||||
|
hx-get="{% url 'atlas:categories_search_partial' %}"
|
||||||
|
hx-target="#category-search-results"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
hx-include="#category-search-form"
|
||||||
|
hx-indicator="#category-search-loading" />
|
||||||
|
<label class="form-check-label" for="type-procedures">Procedures</label>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
{% extends "atlas/base.html" %}
|
||||||
|
{% load crispy_forms_tags %}
|
||||||
|
|
||||||
|
{% block js %}
|
||||||
|
{{ form.media }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h2>Add / Edit Procedure</h2>
|
||||||
|
<p>Use this form to create or edit a procedure.</p>
|
||||||
|
<p>Please check if it already <a href='{% url "atlas:procedure_view" %}'>exists</a> before doing so!</p>
|
||||||
|
|
||||||
|
<form action="" method="post" enctype="multipart/form-data" id="procedure-form">
|
||||||
|
{% csrf_token %}
|
||||||
|
{% crispy form %}
|
||||||
|
|
||||||
|
<div class="mt-3">
|
||||||
|
<button type="submit" class="btn btn-primary">Submit</button>
|
||||||
|
<a class="btn btn-secondary ms-2" href="{% url 'atlas:procedure_view' %}">Cancel</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
@@ -642,7 +642,12 @@ urlpatterns = [
|
|||||||
path(
|
path(
|
||||||
"presentation/<int:pk>", views.presentation_detail, name="presentation_detail"
|
"presentation/<int:pk>", views.presentation_detail, name="presentation_detail"
|
||||||
),
|
),
|
||||||
|
path("procedure/", views.ProcedureView.as_view(), name="procedure_view"),
|
||||||
path("procedure/<int:pk>", views.procedure_detail, name="procedure_detail"),
|
path("procedure/<int:pk>", views.procedure_detail, name="procedure_detail"),
|
||||||
|
path("procedure/create", views.ProcedureCreate.as_view(), name="procedure_create"),
|
||||||
|
path("procedure/<int:pk>/update", views.ProcedureUpdate.as_view(), name="procedure_update"),
|
||||||
|
path("procedure/<int:pk>/delete", views.ProcedureDelete.as_view(), name="procedure_delete"),
|
||||||
|
|
||||||
path("presentation/create", views.PresentationCreate.as_view(), name="presentation_create"),
|
path("presentation/create", views.PresentationCreate.as_view(), name="presentation_create"),
|
||||||
path(
|
path(
|
||||||
"process/",
|
"process/",
|
||||||
|
|||||||
+65
-3
@@ -88,6 +88,7 @@ from .forms import (
|
|||||||
SubspecialtyForm,
|
SubspecialtyForm,
|
||||||
PresentationForm,
|
PresentationForm,
|
||||||
PathologicalProcessForm,
|
PathologicalProcessForm,
|
||||||
|
ProcedureForm,
|
||||||
UserQuestionAnswerForm,
|
UserQuestionAnswerForm,
|
||||||
UserReportAnswerForm,
|
UserReportAnswerForm,
|
||||||
)
|
)
|
||||||
@@ -2748,6 +2749,65 @@ class PresentationView(LoginRequiredMixin, UserConfigurablePaginationMixin, Sing
|
|||||||
return ctx
|
return ctx
|
||||||
|
|
||||||
|
|
||||||
|
class ProcedureView(LoginRequiredMixin, UserConfigurablePaginationMixin, SingleTableMixin, ListView):
|
||||||
|
model = Procedure
|
||||||
|
table_class = __import__('atlas.tables', fromlist=['ProcedureTable']).ProcedureTable
|
||||||
|
template_name = "atlas/view.html"
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
ctx = super().get_context_data(**kwargs)
|
||||||
|
try:
|
||||||
|
ctx["create_url"] = reverse(f"atlas:{self.model._meta.model_name}_create")
|
||||||
|
except NoReverseMatch:
|
||||||
|
ctx["create_url"] = None
|
||||||
|
ctx["model_verbose_name"] = getattr(self.model._meta, "verbose_name", None)
|
||||||
|
ctx["model_verbose_name_plural"] = getattr(self.model._meta, "verbose_name_plural", None)
|
||||||
|
return ctx
|
||||||
|
|
||||||
|
|
||||||
|
class ProcedureCreate(RevisionMixin, LoginRequiredMixin, CreateView):
|
||||||
|
model = Procedure
|
||||||
|
form_class = ProcedureForm
|
||||||
|
template_name = "atlas/procedure_form.html"
|
||||||
|
def form_valid(self, form):
|
||||||
|
self.object = form.save()
|
||||||
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
def form_invalid(self, form):
|
||||||
|
logger.warning("ProcedureCreate form invalid: %s", form.errors)
|
||||||
|
return super().form_invalid(form)
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
try:
|
||||||
|
return reverse("atlas:procedure_detail", kwargs={"pk": self.object.pk})
|
||||||
|
except Exception:
|
||||||
|
return reverse("atlas:procedure_view")
|
||||||
|
|
||||||
|
|
||||||
|
class ProcedureUpdate(RevisionMixin, LoginRequiredMixin, UpdateView):
|
||||||
|
model = Procedure
|
||||||
|
form_class = ProcedureForm
|
||||||
|
template_name = "atlas/procedure_form.html"
|
||||||
|
def form_valid(self, form):
|
||||||
|
self.object = form.save()
|
||||||
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
def form_invalid(self, form):
|
||||||
|
logger.warning("ProcedureUpdate form invalid: %s", form.errors)
|
||||||
|
return super().form_invalid(form)
|
||||||
|
|
||||||
|
def get_success_url(self):
|
||||||
|
try:
|
||||||
|
return reverse("atlas:procedure_detail", kwargs={"pk": self.object.pk})
|
||||||
|
except Exception:
|
||||||
|
return reverse("atlas:procedure_view")
|
||||||
|
|
||||||
|
|
||||||
|
class ProcedureDelete(RevisionMixin, LoginRequiredMixin, DeleteView):
|
||||||
|
model = Procedure
|
||||||
|
template_name = "confirm_delete.html"
|
||||||
|
|
||||||
|
|
||||||
class PathologicalProcessView(LoginRequiredMixin, UserConfigurablePaginationMixin, SingleTableMixin, FilterView):
|
class PathologicalProcessView(LoginRequiredMixin, UserConfigurablePaginationMixin, SingleTableMixin, FilterView):
|
||||||
model = PathologicalProcess
|
model = PathologicalProcess
|
||||||
table_class = PathologicalProcessTable
|
table_class = PathologicalProcessTable
|
||||||
@@ -3137,7 +3197,7 @@ def categories_list(request):
|
|||||||
|
|
||||||
results = None
|
results = None
|
||||||
if query:
|
if query:
|
||||||
from .models import Condition, Finding, Structure, Subspecialty, Presentation
|
from .models import Condition, Finding, Structure, Subspecialty, Presentation, Procedure
|
||||||
|
|
||||||
# Helper to conditionally populate each result list only when the
|
# Helper to conditionally populate each result list only when the
|
||||||
# corresponding type is requested (or when no types filter provided).
|
# corresponding type is requested (or when no types filter provided).
|
||||||
@@ -3150,6 +3210,7 @@ def categories_list(request):
|
|||||||
"structures": Structure.objects.filter(name__icontains=query).order_by("name") if include("structures") else (),
|
"structures": Structure.objects.filter(name__icontains=query).order_by("name") if include("structures") else (),
|
||||||
"subspecialties": Subspecialty.objects.filter(name__icontains=query).order_by("name") if include("subspecialties") else (),
|
"subspecialties": Subspecialty.objects.filter(name__icontains=query).order_by("name") if include("subspecialties") else (),
|
||||||
"presentations": Presentation.objects.filter(name__icontains=query).order_by("name") if include("presentations") else (),
|
"presentations": Presentation.objects.filter(name__icontains=query).order_by("name") if include("presentations") else (),
|
||||||
|
"procedures": Procedure.objects.filter(name__icontains=query).order_by("name") if include("procedures") else (),
|
||||||
}
|
}
|
||||||
|
|
||||||
return render(request, "atlas/categories_list.html", {"query": query, "results": results})
|
return render(request, "atlas/categories_list.html", {"query": query, "results": results})
|
||||||
@@ -3163,9 +3224,9 @@ def categories_search_partial(request):
|
|||||||
"""
|
"""
|
||||||
query = request.GET.get("q", "").strip()
|
query = request.GET.get("q", "").strip()
|
||||||
types = request.GET.getlist("types")
|
types = request.GET.getlist("types")
|
||||||
results = {"conditions": (), "findings": (), "structures": (), "subspecialties": (), "presentations": ()}
|
results = {"conditions": (), "findings": (), "structures": (), "subspecialties": (), "presentations": (), "procedures": ()}
|
||||||
if query:
|
if query:
|
||||||
from .models import Condition, Finding, Structure, Subspecialty, Presentation
|
from .models import Condition, Finding, Structure, Subspecialty, Presentation, Procedure
|
||||||
|
|
||||||
def include(name: str) -> bool:
|
def include(name: str) -> bool:
|
||||||
return not types or name in types
|
return not types or name in types
|
||||||
@@ -3176,6 +3237,7 @@ def categories_search_partial(request):
|
|||||||
"structures": Structure.objects.filter(name__icontains=query).order_by("name") if include("structures") else (),
|
"structures": Structure.objects.filter(name__icontains=query).order_by("name") if include("structures") else (),
|
||||||
"subspecialties": Subspecialty.objects.filter(name__icontains=query).order_by("name") if include("subspecialties") else (),
|
"subspecialties": Subspecialty.objects.filter(name__icontains=query).order_by("name") if include("subspecialties") else (),
|
||||||
"presentations": Presentation.objects.filter(name__icontains=query).order_by("name") if include("presentations") else (),
|
"presentations": Presentation.objects.filter(name__icontains=query).order_by("name") if include("presentations") else (),
|
||||||
|
"procedures": Procedure.objects.filter(name__icontains=query).order_by("name") if include("procedures") else (),
|
||||||
}
|
}
|
||||||
|
|
||||||
return render(request, "atlas/_categories_search_results.html", {"query": query, "results": results})
|
return render(request, "atlas/_categories_search_results.html", {"query": query, "results": results})
|
||||||
|
|||||||
Reference in New Issue
Block a user