Add htmx autocomplete and allow merging of conditions
This commit is contained in:
+19
-1
@@ -47,6 +47,8 @@ from tinymce.widgets import TinyMCE
|
|||||||
|
|
||||||
from dal import autocomplete
|
from dal import autocomplete
|
||||||
|
|
||||||
|
from autocomplete import widgets as htmx_widgets
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
class ConditionForm(ModelForm):
|
class ConditionForm(ModelForm):
|
||||||
@@ -603,4 +605,20 @@ class UncategorisedDicomForm(ModelForm):
|
|||||||
fields = ["image"]#, "user"]
|
fields = ["image"]#, "user"]
|
||||||
|
|
||||||
|
|
||||||
#UncategorisedDicomFormset = formset_factory(UncategorisedDicomForm)
|
#UncategorisedDicomFormset = formset_factory(UncategorisedDicomForm)
|
||||||
|
|
||||||
|
#class ConditionAutocompleteFormModel(forms.ModelForm):
|
||||||
|
# class Meta:
|
||||||
|
# model = Condition
|
||||||
|
# fields = ["name"]
|
||||||
|
# widgets = {
|
||||||
|
# 'members': widgets.Autocomplete(
|
||||||
|
# name='members',
|
||||||
|
# options=dict(multiselect=True, model=Condition)
|
||||||
|
# )
|
||||||
|
# }
|
||||||
|
|
||||||
|
class ConditionAutocompleteForm(Form):
|
||||||
|
condition = ModelChoiceField(queryset=Condition.objects.all(), widget=htmx_widgets.Autocomplete(
|
||||||
|
name="conditions", options=dict(model=Condition)
|
||||||
|
))
|
||||||
|
|||||||
@@ -56,12 +56,34 @@
|
|||||||
<ul>
|
<ul>
|
||||||
|
|
||||||
{% for diff in condition.differential_set.all %}
|
{% for diff in condition.differential_set.all %}
|
||||||
<li>{{diff.case.get_link}}</li>
|
<li>{{diff.case.get_link}} [{{diff.text}}]</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
{% if not condition.rcr_curriculum %}
|
||||||
|
|
||||||
|
|
||||||
|
<details><summary>Merge</summary>
|
||||||
|
Merge this condition into another. This will transfer all associated cases (and differential) but will not transfer synonymns / parents or children.
|
||||||
|
<form method="POST">
|
||||||
|
{% csrf_token %}
|
||||||
|
<table>
|
||||||
|
{{ form.as_table }}
|
||||||
|
</table>
|
||||||
|
<button hx-post='{% url "atlas:condition_merge" condition.pk %}'
|
||||||
|
hx-target="#merge-options" hx-swap="innerHTML"
|
||||||
|
hx-confirm="This will merge {{condition.name}} into the selecetd condition, this cannot be undone. Continue?"
|
||||||
|
>
|
||||||
|
Merge
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<div id="merge-options"></div>
|
||||||
|
</details>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block js %}
|
{% block js %}
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
{% for condition in conditions %}
|
||||||
|
|
||||||
|
<button id={{condition.pk}} class="option-button">{{condition.name}}</button>
|
||||||
|
|
||||||
|
{% endfor %}
|
||||||
@@ -159,6 +159,11 @@ urlpatterns = [
|
|||||||
views.ConditionUpdate.as_view(),
|
views.ConditionUpdate.as_view(),
|
||||||
name="condition_update",
|
name="condition_update",
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"condition/<int:pk_to_merge>/merge/",
|
||||||
|
views.condition_merge,
|
||||||
|
name="condition_merge",
|
||||||
|
),
|
||||||
path("subspecialty/", views.SubspecialtyView.as_view(), name="subspecialty_view"),
|
path("subspecialty/", views.SubspecialtyView.as_view(), name="subspecialty_view"),
|
||||||
path(
|
path(
|
||||||
"subspecialty/<int:pk>", views.subspecialty_detail, name="subspecialty_detail"
|
"subspecialty/<int:pk>", views.subspecialty_detail, name="subspecialty_detail"
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ from .forms import (
|
|||||||
CaseForm,
|
CaseForm,
|
||||||
CidReportAnswerForm,
|
CidReportAnswerForm,
|
||||||
CidReportAnswerMarkForm,
|
CidReportAnswerMarkForm,
|
||||||
|
ConditionAutocompleteForm,
|
||||||
ConditionForm,
|
ConditionForm,
|
||||||
FindingForm,
|
FindingForm,
|
||||||
SelfReviewForm,
|
SelfReviewForm,
|
||||||
@@ -53,6 +54,7 @@ from .models import (
|
|||||||
CaseDetail,
|
CaseDetail,
|
||||||
CidReportAnswer,
|
CidReportAnswer,
|
||||||
Condition,
|
Condition,
|
||||||
|
Differential,
|
||||||
PathologicalProcess,
|
PathologicalProcess,
|
||||||
Presentation,
|
Presentation,
|
||||||
SelfReview,
|
SelfReview,
|
||||||
@@ -92,6 +94,9 @@ from .filters import (
|
|||||||
from django_tables2 import SingleTableView, SingleTableMixin
|
from django_tables2 import SingleTableView, SingleTableMixin
|
||||||
from django_filters.views import FilterView
|
from django_filters.views import FilterView
|
||||||
|
|
||||||
|
from autocomplete import HTMXAutoComplete
|
||||||
|
|
||||||
|
|
||||||
from .decorators import (
|
from .decorators import (
|
||||||
user_is_author_or_atlas_editor,
|
user_is_author_or_atlas_editor,
|
||||||
user_is_author_or_atlas_editor_or_atlas_marker,
|
user_is_author_or_atlas_editor_or_atlas_marker,
|
||||||
@@ -186,15 +191,65 @@ def series_detail(request, pk, finding_pk=None):
|
|||||||
def condition_detail(request, pk):
|
def condition_detail(request, pk):
|
||||||
condition = get_object_or_404(Condition, pk=pk)
|
condition = get_object_or_404(Condition, pk=pk)
|
||||||
|
|
||||||
|
form = ConditionAutocompleteForm()
|
||||||
|
|
||||||
# logging.debug(atlas.subspecialty.first().name.all())
|
# logging.debug(atlas.subspecialty.first().name.all())
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
"atlas/condition_detail.html",
|
"atlas/condition_detail.html",
|
||||||
{
|
{
|
||||||
"condition": condition,
|
"condition": condition,
|
||||||
|
"form": form
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
#class GetConditionAutocomplete(HTMXAutoComplete):
|
||||||
|
# name = "conditions"
|
||||||
|
# multiselect = False
|
||||||
|
#
|
||||||
|
# class Meta:
|
||||||
|
# model = Condition
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
@user_is_atlas_editor
|
||||||
|
def condition_merge(request, pk_to_merge):
|
||||||
|
if not request.htmx:
|
||||||
|
raise Http404
|
||||||
|
|
||||||
|
condition_to_merge = get_object_or_404(Condition, pk=pk_to_merge)
|
||||||
|
#condition_to_merge_into = get_object_or_404(Condition, pk=pk_to_merge_into)
|
||||||
|
|
||||||
|
print(condition_to_merge.case_set)
|
||||||
|
if condition_to_merge.rcr_curriculum:
|
||||||
|
return HttpResponse("You cannot merge rcr curriculum items")
|
||||||
|
|
||||||
|
condition_to_merge_into = request.POST.get("conditions")
|
||||||
|
|
||||||
|
if not condition_to_merge_into:
|
||||||
|
return HttpResponse("You need to select a condition to merge into.")
|
||||||
|
|
||||||
|
condition_to_merge_into = get_object_or_404(Condition, pk=condition_to_merge_into)
|
||||||
|
|
||||||
|
if condition_to_merge == condition_to_merge_into:
|
||||||
|
return HttpResponse("You cannot merge into the same condition.")
|
||||||
|
|
||||||
|
|
||||||
|
# move all relevant relationships
|
||||||
|
cases = condition_to_merge.case_set.all().values_list("id", flat=True)
|
||||||
|
condition_to_merge_into.case_set.add(cases)
|
||||||
|
#cases = Case.objects.filter(condition=condition_to_merge)
|
||||||
|
#cases.update(condition=condition_to_merge_into)
|
||||||
|
|
||||||
|
differentials = Differential.objects.filter(condition=condition_to_merge)
|
||||||
|
differentials.update(condition=condition_to_merge_into)
|
||||||
|
|
||||||
|
return HttpResponse("Merged")
|
||||||
|
|
||||||
|
#conditions = Condition.objects.all()
|
||||||
|
|
||||||
|
#return render(request, "atlas/condition_options.html", {"conditions": conditions})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
@user_is_atlas_editor
|
@user_is_atlas_editor
|
||||||
|
|||||||
@@ -75,6 +75,7 @@ INSTALLED_APPS = [
|
|||||||
"django_htmx",
|
"django_htmx",
|
||||||
"crispy_forms",
|
"crispy_forms",
|
||||||
"crispy_bootstrap4",
|
"crispy_bootstrap4",
|
||||||
|
"autocomplete"
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ from rest_framework import routers
|
|||||||
from django.conf.urls import handler400, handler403, handler404, handler500
|
from django.conf.urls import handler400, handler403, handler404, handler500
|
||||||
|
|
||||||
from .api import api
|
from .api import api
|
||||||
|
from autocomplete import HTMXAutoComplete
|
||||||
|
|
||||||
|
|
||||||
router_drf = routers.DefaultRouter()
|
router_drf = routers.DefaultRouter()
|
||||||
@@ -73,6 +74,12 @@ urlpatterns = [
|
|||||||
path(
|
path(
|
||||||
"accounts/create/", generic_views.create_user, name="create_user"
|
"accounts/create/", generic_views.create_user, name="create_user"
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"accounts/trainees/", generic_views.trainees, name="trainees"
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"accounts/trainees/<str:grade>/", generic_views.trainees, name="trainees_grade"
|
||||||
|
),
|
||||||
path(
|
path(
|
||||||
"accounts/update/<str:slug>/",
|
"accounts/update/<str:slug>/",
|
||||||
views.UpdateUserView.as_view(),
|
views.UpdateUserView.as_view(),
|
||||||
@@ -152,6 +159,7 @@ urlpatterns = [
|
|||||||
#path("cookies/", include("cookie_consent.urls")),
|
#path("cookies/", include("cookie_consent.urls")),
|
||||||
path("privacy/", views.privacy_view, name="privacy"),
|
path("privacy/", views.privacy_view, name="privacy"),
|
||||||
# path('select2/', include('select2.urls')),
|
# path('select2/', include('select2.urls')),
|
||||||
|
*HTMXAutoComplete.url_dispatcher('ac'),
|
||||||
]
|
]
|
||||||
|
|
||||||
# handler400 = 'my_app.views.bad_request'
|
# handler400 = 'my_app.views.bad_request'
|
||||||
|
|||||||
+2
-1
@@ -37,4 +37,5 @@ django-crispy-forms
|
|||||||
django-ninja
|
django-ninja
|
||||||
crispy-bootstrap4
|
crispy-bootstrap4
|
||||||
django-rich
|
django-rich
|
||||||
ipython
|
ipython
|
||||||
|
django-htmx-autocomplete
|
||||||
Reference in New Issue
Block a user