update conditions to use through for self links
This commit is contained in:
@@ -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
@@ -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
@@ -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 %}
|
||||
|
||||
@@ -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
|
||||
@@ -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
@@ -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' %}">
|
||||
|
||||
Reference in New Issue
Block a user