.
This commit is contained in:
+11
-1
@@ -8,7 +8,7 @@ from django.forms import (
|
|||||||
)
|
)
|
||||||
from django.forms import inlineformset_factory
|
from django.forms import inlineformset_factory
|
||||||
|
|
||||||
from atlas.models import Case, Series, SeriesImage, SeriesFinding
|
from atlas.models import Case, Differential, Series, SeriesImage, SeriesFinding
|
||||||
|
|
||||||
from anatomy.models import Modality
|
from anatomy.models import Modality
|
||||||
|
|
||||||
@@ -21,6 +21,7 @@ from django.forms.widgets import RadioSelect, TextInput, Textarea
|
|||||||
|
|
||||||
from tinymce.widgets import TinyMCE
|
from tinymce.widgets import TinyMCE
|
||||||
|
|
||||||
|
from dal import autocomplete
|
||||||
|
|
||||||
class ExaminationForm(ModelForm):
|
class ExaminationForm(ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
@@ -173,6 +174,7 @@ class CaseForm(ModelForm):
|
|||||||
# "mark_scheme": TinyMCE(attrs={"cols": 80, "rows": 30}),
|
# "mark_scheme": TinyMCE(attrs={"cols": 80, "rows": 30}),
|
||||||
"description": Textarea(attrs={"cols": 80, "rows": 5}),
|
"description": Textarea(attrs={"cols": 80, "rows": 5}),
|
||||||
"history": Textarea(attrs={"cols": 80, "rows": 5}),
|
"history": Textarea(attrs={"cols": 80, "rows": 5}),
|
||||||
|
"condition" : autocomplete.ModelSelect2(url='atlas:condition-autocomplete')
|
||||||
}
|
}
|
||||||
|
|
||||||
def save(self, commit=True):
|
def save(self, commit=True):
|
||||||
@@ -198,6 +200,14 @@ class CaseForm(ModelForm):
|
|||||||
|
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
CaseDifferentialFormSet = inlineformset_factory(
|
||||||
|
Case,
|
||||||
|
Differential,
|
||||||
|
exclude=[],
|
||||||
|
can_delete=True,
|
||||||
|
extra=0,
|
||||||
|
max_num=10,
|
||||||
|
)
|
||||||
|
|
||||||
SeriesFormSet = inlineformset_factory(
|
SeriesFormSet = inlineformset_factory(
|
||||||
Case,
|
Case,
|
||||||
|
|||||||
+16
-6
@@ -90,14 +90,14 @@ class SynMixin(object):
|
|||||||
|
|
||||||
|
|
||||||
class Finding(SynMixin, models.Model):
|
class Finding(SynMixin, models.Model):
|
||||||
name = models.CharField(max_length=255)
|
name = models.CharField(max_length=255, unique=True)
|
||||||
synonym = models.ManyToManyField("self", blank=True)
|
synonym = models.ManyToManyField("self", blank=True)
|
||||||
|
|
||||||
primary_name = models.BooleanField(default="True")
|
primary_name = models.BooleanField(default="True")
|
||||||
|
|
||||||
|
|
||||||
class Condition(SynMixin, models.Model):
|
class Condition(SynMixin, models.Model):
|
||||||
name = models.CharField(max_length=255)
|
name = models.CharField(max_length=255, unique=True)
|
||||||
|
|
||||||
synonym = models.ManyToManyField("self", blank=True)
|
synonym = models.ManyToManyField("self", blank=True)
|
||||||
|
|
||||||
@@ -114,15 +114,22 @@ class Subspecialty(models.Model):
|
|||||||
|
|
||||||
|
|
||||||
class Differential(models.Model):
|
class Differential(models.Model):
|
||||||
condition = models.ForeignKey(
|
condition = models.ForeignKey(Condition, on_delete=models.CASCADE)
|
||||||
Condition, on_delete=models.CASCADE
|
|
||||||
)
|
|
||||||
case = models.ForeignKey(
|
case = models.ForeignKey(
|
||||||
"Case", on_delete=models.CASCADE, related_name="differentialcase"
|
"Case", on_delete=models.CASCADE, related_name="differentialcase"
|
||||||
)
|
)
|
||||||
text = models.TextField(null=True, blank=True)
|
text = models.TextField(null=True, blank=True)
|
||||||
|
|
||||||
|
|
||||||
|
class Structure(SynMixin, models.Model):
|
||||||
|
structure = models.CharField(max_length=255, unique=True)
|
||||||
|
|
||||||
|
synonym = models.ManyToManyField("self", blank=True)
|
||||||
|
|
||||||
|
primary_name = models.BooleanField(default="True")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@reversion.register
|
@reversion.register
|
||||||
class Case(models.Model):
|
class Case(models.Model):
|
||||||
# class SubspecialtyChoices(models.TextChoices):
|
# class SubspecialtyChoices(models.TextChoices):
|
||||||
@@ -156,7 +163,9 @@ class Case(models.Model):
|
|||||||
|
|
||||||
condition = models.ManyToManyField(Condition, blank=True)
|
condition = models.ManyToManyField(Condition, blank=True)
|
||||||
|
|
||||||
differential = models.ManyToManyField(Condition, through=Differential, related_name="casedifferential")
|
differential = models.ManyToManyField(
|
||||||
|
Condition, through=Differential, related_name="casedifferential"
|
||||||
|
)
|
||||||
|
|
||||||
sign = tagulous.models.TagField(
|
sign = tagulous.models.TagField(
|
||||||
to=Sign, blank=True, help_text="Radiological signs in the question"
|
to=Sign, blank=True, help_text="Radiological signs in the question"
|
||||||
@@ -232,6 +241,7 @@ class SeriesFinding(models.Model):
|
|||||||
null=True, blank=True, help_text="Findings on the series / stack"
|
null=True, blank=True, help_text="Findings on the series / stack"
|
||||||
)
|
)
|
||||||
findings = models.ManyToManyField(Finding, blank=True)
|
findings = models.ManyToManyField(Finding, blank=True)
|
||||||
|
structures = models.ManyToManyField(Structure, blank=True)
|
||||||
annotation_json = models.TextField(null=True, blank=True)
|
annotation_json = models.TextField(null=True, blank=True)
|
||||||
viewport_json = models.TextField(null=True, blank=True)
|
viewport_json = models.TextField(null=True, blank=True)
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@
|
|||||||
<!-- {{ form.media }} -->
|
<!-- {{ form.media }} -->
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h2>Submit Long Case</h2>
|
<h2>Submit Case</h2>
|
||||||
Use this form to create a atlas case. Existing associated image sets can be added using this form.
|
Use this form to create a atlas case. Existing associated image sets can be added using this form.
|
||||||
<form action="" method="post" enctype="multipart/form-data" id="atlas-form">
|
<form action="" method="post" enctype="multipart/form-data" id="atlas-form">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
|||||||
+24
-3
@@ -1,4 +1,5 @@
|
|||||||
import json
|
import json
|
||||||
|
from dal import autocomplete
|
||||||
from django.shortcuts import render, get_object_or_404, redirect
|
from django.shortcuts import render, get_object_or_404, redirect
|
||||||
from django import forms
|
from django import forms
|
||||||
|
|
||||||
@@ -6,6 +7,7 @@ from django import forms
|
|||||||
from django.contrib.auth.decorators import login_required, user_passes_test
|
from django.contrib.auth.decorators import login_required, user_passes_test
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.core.exceptions import (
|
from django.core.exceptions import (
|
||||||
|
FieldError,
|
||||||
ObjectDoesNotExist,
|
ObjectDoesNotExist,
|
||||||
PermissionDenied,
|
PermissionDenied,
|
||||||
ViewDoesNotExist,
|
ViewDoesNotExist,
|
||||||
@@ -34,6 +36,7 @@ from .forms import (
|
|||||||
)
|
)
|
||||||
from .models import (
|
from .models import (
|
||||||
Case,
|
Case,
|
||||||
|
Condition,
|
||||||
Series,
|
Series,
|
||||||
Examination,
|
Examination,
|
||||||
Finding,
|
Finding,
|
||||||
@@ -152,17 +155,17 @@ class AtlasDelete(RevisionMixin, AuthorOrCheckerRequiredMixin, DeleteView):
|
|||||||
model = Case
|
model = Case
|
||||||
success_url = reverse_lazy("atlas:case_view")
|
success_url = reverse_lazy("atlas:case_view")
|
||||||
|
|
||||||
|
|
||||||
class SeriesFindingDelete(RevisionMixin, PermissionRequiredMixin, DeleteView):
|
class SeriesFindingDelete(RevisionMixin, PermissionRequiredMixin, DeleteView):
|
||||||
permission_required = 'atlas.delete_seriesfinding'
|
permission_required = "atlas.delete_seriesfinding"
|
||||||
model = SeriesFinding
|
model = SeriesFinding
|
||||||
template_name = "confirm_delete.html"
|
template_name = "confirm_delete.html"
|
||||||
#success_url = reverse_lazy("atlas:case_view")
|
# success_url = reverse_lazy("atlas:case_view")
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
pk = self.kwargs["pk"]
|
pk = self.kwargs["pk"]
|
||||||
series_id = get_object_or_404(SeriesFinding, pk=pk).series.id
|
series_id = get_object_or_404(SeriesFinding, pk=pk).series.id
|
||||||
return reverse("atlas:series_detail", kwargs={"pk": series_id})
|
return reverse("atlas:series_detail", kwargs={"pk": series_id})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class SeriesDelete(RevisionMixin, AuthorOrCheckerRequiredMixin, DeleteView):
|
class SeriesDelete(RevisionMixin, AuthorOrCheckerRequiredMixin, DeleteView):
|
||||||
@@ -558,3 +561,21 @@ def create_series_findings(request):
|
|||||||
|
|
||||||
return JsonResponse({"success": False})
|
return JsonResponse({"success": False})
|
||||||
return render(request, "create_post.html", {"posts": posts})
|
return render(request, "create_post.html", {"posts": posts})
|
||||||
|
|
||||||
|
|
||||||
|
class ConditionAutocomplete(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 Condition.objects.none()
|
||||||
|
|
||||||
|
qs = Condition.objects.all()
|
||||||
|
|
||||||
|
if self.q:
|
||||||
|
# This raises a fielderror which breaks creating a new item if not caught
|
||||||
|
try:
|
||||||
|
qs = qs.filter(name__istartswith=self.q)
|
||||||
|
except FieldError:
|
||||||
|
return Condition.objects.none()
|
||||||
|
|
||||||
|
return qs
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
from django.urls import path, include
|
from django.urls import path, include
|
||||||
|
|
||||||
|
from atlas.models import Condition
|
||||||
from . import views
|
from . import views
|
||||||
from django.views.decorators.cache import cache_page
|
from django.views.decorators.cache import cache_page
|
||||||
|
|
||||||
@@ -42,5 +44,10 @@ urlpatterns = [
|
|||||||
views.GenericViews.user_answer_delete_multiple,
|
views.GenericViews.user_answer_delete_multiple,
|
||||||
name="user_answer_delete_multiple",
|
name="user_answer_delete_multiple",
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"condition-autocomplete",
|
||||||
|
views.ConditionAutocomplete.as_view(model=Condition, create_field='structure'),
|
||||||
|
name="condition-autocomplete",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user