update conditions to use through for self links

This commit is contained in:
Ross
2023-11-06 11:16:59 +00:00
parent bbcfe7d80c
commit 369b1970a3
12 changed files with 232 additions and 6 deletions
+2
View File
@@ -7,6 +7,7 @@ from .models import (
Series,
SeriesImage,
Condition,
ConditionRelationship,
Subspecialty,
Finding,
SeriesFinding,
@@ -31,6 +32,7 @@ from generic.models import Examination
# admin.site.register(Site)
admin.site.register(SeriesImage)
admin.site.register(Condition)
admin.site.register(ConditionRelationship)
admin.site.register(Subspecialty)
admin.site.register(Finding)
admin.site.register(Structure)
+1 -1
View File
@@ -163,7 +163,7 @@ class ConditionFilter(django_filters.FilterSet):
"name": ["icontains"],
"primary": ["exact"],
"synonym": ["exact"],
"parent": ["exact"],
#"parent": ["exact"],
"subspecialty": ["exact"],
"rcr_curriculum": ["exact"],
}
@@ -0,0 +1,18 @@
# Generated by Django 4.1.4 on 2023-11-06 09:21
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('atlas', '0025_case_diagnostic_certainty'),
]
operations = [
migrations.AlterField(
model_name='condition',
name='parent',
field=models.ManyToManyField(blank=True, related_name='child', to='atlas.condition'),
),
]
@@ -0,0 +1,32 @@
# Generated by Django 4.1.4 on 2023-11-06 10:09
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('atlas', '0026_alter_condition_parent'),
]
operations = [
migrations.RemoveField(
model_name='condition',
name='parent',
),
migrations.CreateModel(
name='ConditionRelationship',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('relationship_type', models.CharField(default='', max_length=255)),
('child', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='atlas.condition')),
('parent', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='atlas.condition')),
],
),
migrations.AddField(
model_name='condition',
name='parents',
field=models.ManyToManyField(blank=True, related_name='children', through='atlas.ConditionRelationship', to='atlas.condition'),
),
]
@@ -0,0 +1,22 @@
# Generated by Django 4.1.4 on 2023-11-06 10:09
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('atlas', '0027_remove_condition_parent_conditionrelationship_and_more'),
]
operations = [
migrations.RemoveField(
model_name='condition',
name='parents',
),
migrations.AddField(
model_name='condition',
name='parent',
field=models.ManyToManyField(blank=True, related_name='child', through='atlas.ConditionRelationship', to='atlas.condition'),
),
]
@@ -0,0 +1,29 @@
# Generated by Django 4.1.4 on 2023-11-06 10:14
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('atlas', '0028_remove_condition_parents_condition_parent'),
]
operations = [
migrations.AlterField(
model_name='conditionrelationship',
name='child',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='atlas.condition'),
),
migrations.AlterField(
model_name='conditionrelationship',
name='parent',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='atlas.condition'),
),
migrations.AlterField(
model_name='conditionrelationship',
name='relationship_type',
field=models.CharField(blank=True, max_length=255, null=True),
),
]
+20 -1
View File
@@ -144,7 +144,7 @@ class Condition(SynMixin, models.Model):
name = models.CharField(max_length=255, unique=True)
synonym = models.ManyToManyField("self", blank=True)
parent = models.ManyToManyField("self", blank=True)
parent = models.ManyToManyField("self", blank=True, related_name="child", symmetrical=False, through="ConditionRelationship")
primary = models.BooleanField(default="True")
@@ -157,6 +157,25 @@ class Condition(SynMixin, models.Model):
def get_absolute_url(self):
return reverse("atlas:condition_detail", kwargs={"pk": self.pk})
def get_children(self):
return self.child.all()
def get_descendents(self):
"""TODO"""
def get_parents(self):
return self.parent.all()
class ConditionRelationship(models.Model):
child = models.ForeignKey(Condition, on_delete=models.CASCADE)
parent = models.ForeignKey(Condition, on_delete=models.CASCADE, related_name="+")
relationship_type = models.CharField(max_length=255, blank=True, null=True)
def __str__(self):
return f"{self.parent} -> {self.child}"
class Presentation(models.Model):
name = models.CharField(max_length=255)
@@ -26,6 +26,10 @@
{% for parent in condition.parent.all %}
<a href="{{parent.get_absolute_url}}">{{parent}}</a>
{% endfor %}<br />
Children:
{% for child in condition.child.all %}
<a href="{{child.get_absolute_url}}">{{child}}</a>
{% endfor %}<br />
RCR condition: {{condition.rcr_curriculum}}<br/>
{% if not condition.rcr_curriculum %}
+75
View File
@@ -0,0 +1,75 @@
from django.urls import reverse
import pytest
from atlas.models import Finding, Condition
#from atlas.views import GenericExamViews
#@pytest.mark.django_db
def test_create_condition(db):
abnormality = Condition.objects.create(name="Abnorm")
assert abnormality.name == "Abnorm"
def test_condition_tree_structure(db):
parent = Condition.objects.create(name="parent")
child1 = Condition.objects.create(name="child1")
child2 = Condition.objects.create(name="child2")
parent.child.set([child1, child2])
child1_1 = Condition.objects.create(name="child1_1")
child1_2 = Condition.objects.create(name="child1_2")
child1.child.set([child1_1, child1_2])
assert len(parent.get_children()) == 2
assert len(child1.get_children()) == 2
assert len(child1.get_parents()) == 1
assert parent in child1.get_parents()
assert len(child2.get_parents()) == 1
assert parent in child2.get_parents()
print(parent.get_descendents())
#@pytest.fixture
#def create_basic_user(db, django_user_model):
# return django_user_model.objects.create_superuser("basicuser", "ross@xkjq.uk", "password")
#
#@pytest.fixture
#def create_exam(db):
# return Exam.objects.create(name="test exam", exam_mode=True)
#
#def test_exam_creation(create_exam):
# exams = Exam.objects.filter(name="test exam")
# assert exams.exists()
#
# exam = exams.first()
#
# assert exam.time_limit == 35 * 60
#
#def test_exam_list(rf, admin_user, create_exam):
# request = rf.get('/rapids/exam/')
# # Remember that when using RequestFactory, the request does not pass
# # through middleware. If your view expects fields such as request.user
# # to be set, you need to set them explicitly.
# # The following line sets request.user to an admin user.
# request.user = admin_user
# response = GenericExamViews.exam_list(request)
# #print(response)
# assert response.status_code == 200
#
#def test_exam_active_admin(rf, admin_user, create_exam, client):
# request = rf.get(reverse('rapids:active_exams'))
# request.user = admin_user
# response = GenericExamViews.active_exams(request)
# print(response.content)
# assert response.status_code == 200
#
#def test_exam_active_basic_user(create_basic_user, create_exam, client):
# client.login(username="basicuser", password="password")
# response = client.get(reverse('rapids:active_exams'))
# print(response.content)
# assert response.status_code == 200
+2 -1
View File
@@ -1,5 +1,6 @@
{% extends 'generic/base.html' %}
{% load crispy_forms_tags %}
{% load render_table from django_tables2 %}
{% block css %}
{% endblock %}
@@ -12,7 +13,7 @@
<h3>Filter Users </h3>
</summary>
<form action="" method="get">
{{ filter.form }}
{{ filter.form| crispy }}
<input class="btn btn-primary btn-sm mt-1 mb-1" type="submit" />
</form>
</details>
@@ -0,0 +1,24 @@
# Generated by Django 4.1.4 on 2023-11-06 09:21
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('rcr', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='item',
name='category',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='rcr.category'),
),
migrations.AlterField(
model_name='item',
name='level',
field=models.ManyToManyField(blank=True, to='rcr.level'),
),
]
+3 -3
View File
@@ -12,12 +12,12 @@
{% endblock %}
</title>
{% comment %} <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css" /> {% endcomment %}
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet">
<link href="{% static 'css/widgets.css' %}" type="text/css" media="all" rel="stylesheet">
<link rel="stylesheet" href="{% static 'css/dicomViewer.css' %}">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/2.1.4/toastr.min.css" integrity="sha512-6S2HWzVFxruDlZxI3sXOZZ4/eJ8AcxkQH1+JjSe/ONCEqR9L4Ysq5JdT5ipqtzU7WHalNwzwBv+iE51gNHJNqQ==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css">
<link rel="stylesheet" href="{% static 'css/toastr.min.css' %}">
<link rel="stylesheet" href="{% static 'css/bootstrap-icons.css' %}">
<link rel="apple-touch-icon" sizes="180x180" href="{% static 'favicon/apple-touch-icon.png' %}">
<link rel="icon" type="image/png" sizes="32x32" href="{% static 'favicon/favicon-32x32.png' %}">