Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Icf cpheapm71 72 add guidelines #1118

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions hawc/apps/animal/exports.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ def get_value_map(self):
"purity": "purity",
"vehicle": "vehicle",
"guideline_compliance": "guideline_compliance",
"guideline_profile": "guideline_profile",
"description": "description",
}

Expand Down
17 changes: 17 additions & 0 deletions hawc/apps/animal/migrations/0032_experiment_guideline_profile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 5.1.1 on 2024-10-11 13:26

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("animal", "0031_tre"),
]

operations = [
migrations.AddField(
model_name="experiment",
name="guideline_profile",
field=models.CharField(blank=True, max_length=128, null=True),
),
]
1 change: 1 addition & 0 deletions hawc/apps/animal/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ class Experiment(models.Model):
414 and 412, 1981 versions). If not reported, then use state "not reported."
""",
)
guideline_profile = models.CharField(max_length=128, blank=True, null=True)
description = models.TextField(
blank=True,
verbose_name="Comments",
Expand Down
10 changes: 10 additions & 0 deletions hawc/apps/animal/serializers.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import itertools
import json

from django.core.exceptions import ValidationError
from django.db import transaction
from rest_framework import serializers

Expand All @@ -13,6 +14,7 @@
from ..study.models import Study
from ..study.serializers import StudySerializer
from ..vocab.constants import VocabularyTermType
from ..vocab.serializers import GuidelineProfileSerializer
from . import forms, models


Expand Down Expand Up @@ -47,6 +49,14 @@ def validate(self, data):
dict(dtxsid=f"DSSTox {dtxsid} does not exist")
) from exc

# validate guideline profile
guideline = self.initial_data.get("guideline_profile")
if guideline:
valid_guidelines = GuidelineProfileSerializer._load_guideline_data()
names = [guideline["guideline_name"] for guideline in valid_guidelines]
if guideline not in names:
raise ValidationError(f"{guideline} is not a valid guideline")

return data

def create(self, validated_data):
Expand Down
14 changes: 13 additions & 1 deletion hawc/apps/vocab/constants.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from django.db.models import IntegerChoices
from django.db.models import IntegerChoices, TextChoices
from django.urls import reverse


Expand Down Expand Up @@ -70,3 +70,15 @@ class Ontology(IntegerChoices):
"""

umls = 1, "UMLS"


class ObservationStatus(TextChoices):
"""
Guideline profile observation status
"""

NM = "NM", "NM"
NR = "not required", "not required"
REC = "recommended", "recommended"
REQ = "required", "required"
TR = "triggered", "triggered"
Copy link
Collaborator Author

@ZindahFarhaICF ZindahFarhaICF Oct 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added type checking for GuidelineProfile observation status, can be changed/removed as needed

7,020 changes: 7,020 additions & 0 deletions hawc/apps/vocab/fixtures/guideline_profile.jsonl

Large diffs are not rendered by default.

128 changes: 128 additions & 0 deletions hawc/apps/vocab/fixtures/guidelines.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
[
{
"guideline_id": "1",
"guideline_number": "870.31",
"guideline_name": "90-day Oral Toxicity in Rodents",
"profile_name": "SUB_oral_rode",
"description": ""
},
{
"guideline_id": "2",
"guideline_number": "870.315",
"guideline_name": "90-day Oral Toxicity in Nonrodents",
"profile_name": "SUB_oral_nonr",
"description": ""
},
{
"guideline_id": "3",
"guideline_number": "870.325",
"guideline_name": "90-day Dermal Toxicity",
"profile_name": "SUB_sub_derm",
"description": ""
},
{
"guideline_id": "4",
"guideline_number": "870.3465",
"guideline_name": "90-Day Inhalation Toxicity",
"profile_name": "SUB_sub_inha",
"description": ""
},
{
"guideline_id": "5",
"guideline_number": "870.355",
"guideline_name": "Reproduction/Development Toxicity Screening Test",
"profile_name": "REP_rep_dev",
"description": ""
},
{
"guideline_id": "6",
"guideline_number": "870.37",
"guideline_name": "Prenatal Developmental Toxicity Study",
"profile_name": "DEV_pren_dev",
"description": ""
},
{
"guideline_id": "7",
"guideline_number": "870.38",
"guideline_name": "Reproduction and Fertility Effects post 1998",
"profile_name": "MGR_rep_fert",
"description": "MGR studies post 1998 (study_year 1996)"
},
{
"guideline_id": "8",
"guideline_number": "870.41",
"guideline_name": "Chronic Toxicity",
"profile_name": "CHR_chr_tox",
"description": ""
},
{
"guideline_id": "9",
"guideline_number": "870.42",
"guideline_name": "Carcinogenicty",
"profile_name": "CHR_carc",
"description": ""
},
{
"guideline_id": "10",
"guideline_number": "870.43",
"guideline_name": "Combined Chronic Toxicity/Carcinogenicity",
"profile_name": "CHR_chr_canc",
"description": ""
},
{
"guideline_id": "11",
"guideline_number": "870.62",
"guideline_name": "Neurotoxicity Screening Battery",
"profile_name": "NEU_neur_bat",
"description": ""
},
{
"guideline_id": "12",
"guideline_number": "870.63",
"guideline_name": "Developmental Neurotoxicity Study",
"profile_name": "DNT_dev_neur",
"description": ""
},
{
"guideline_id": "13",
"guideline_number": "870.38",
"guideline_name": "Reproduction and Fertility Effects pre 1998",
"profile_name": "MGR_rep_fert_pre98",
"description": "MGR studies pre 1998 (study year 1996)"
},
{
"guideline_id": "14",
"guideline_number": "870.305",
"guideline_name": "28-day Oral Toxicity in Rodents",
"profile_name": "SAC_oral_rode_28",
"description": ""
},
{
"guideline_id": "15",
"guideline_number": "NULL",
"guideline_name": "14-day Toxicity in Rodents",
"profile_name": "SAC_ntp",
"description": ""
},
{
"guideline_id": "16",
"guideline_number": "NULL",
"guideline_name": "13-Week Toxicity in Rodents",
"profile_name": "SUB_ntp",
"description": ""
},
{
"guideline_id": "17",
"guideline_number": "NULL",
"guideline_name": "2 - Year Toxicity",
"profile_name": "CHR_ntp",
"description": ""
},
{
"guideline_id": "18",
"guideline_number": "NULL",
"guideline_name": "Non-guideline",
"profile_name": "NON-GUIDE",
"description": ""
}
]
24 changes: 24 additions & 0 deletions hawc/apps/vocab/migrations/00010_load_guideline_profile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from pathlib import Path

from django.core.management import call_command
from django.db import migrations


def load_fixture(apps, schema_editor):
here = Path(__file__).parent
fixtures = (here / "../fixtures").resolve()
call_command("loaddata", str(fixtures / "guideline_profile.jsonl"), app_label="vocab")


def unload_fixture(apps, schema_editor):
apps.get_model("vocab", "GuidelineProfile").objects.all().delete()


class Migration(migrations.Migration):
dependencies = [
("vocab", "0009_guideline_profile"),
]

operations = [
migrations.RunPython(load_fixture, reverse_code=unload_fixture),
]
54 changes: 54 additions & 0 deletions hawc/apps/vocab/migrations/0009_guideline_profile.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# Generated by Django 5.1.1 on 2024-10-11 14:36

import django.db.models.deletion
from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("vocab", "0008_alter_term_namespace"),
]

operations = [
migrations.CreateModel(
name="GuidelineProfile",
fields=[
(
"guideline_profile_id",
models.PositiveIntegerField(
primary_key=True, serialize=False, unique=True, verbose_name="ID"
),
),
("guideline_id", models.PositiveIntegerField()),
(
"endpoint",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
to="vocab.term",
),
),
("endpoint_category", models.CharField(max_length=256)),
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Guideline_profile_id is the primary key, and I kept the 3 vocab name fields that make up the endpoint. If they were mainly for my benefit to load in the vocab ids, I can remove them!

("endpoint_type", models.CharField(max_length=256)),
("endpoint_target", models.CharField(max_length=256)),
(
"obs_status",
models.CharField(
choices=[
("NM", "NM"),
("not required", "not required"),
("recommended", "recommended"),
("required", "required"),
("triggered", "triggered"),
],
null=True,
),
),
("description", models.TextField(blank=True)),
],
options={
"ordering": ("guideline_profile_id",),
},
),
]
20 changes: 20 additions & 0 deletions hawc/apps/vocab/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,26 @@ def __str__(self) -> str:
return f"{self.term} -> {self.entity}"


class GuidelineProfile(models.Model):
guideline_profile_id = models.PositiveIntegerField(
unique=True, primary_key=True, verbose_name="ID"
)
guideline_id = models.PositiveIntegerField()
endpoint = models.ForeignKey("Term", on_delete=models.CASCADE, blank=True, null=True)
endpoint_category = models.CharField(max_length=256)
endpoint_type = models.CharField(max_length=256)
endpoint_target = models.CharField(max_length=256)
obs_status = models.CharField(choices=constants.ObservationStatus, null=True)
description = models.TextField(blank=True)

class Meta:
ordering = ("guideline_profile_id",)

def __str__(self) -> str:
return f"{self.guideline_id}::{self.profile_id}::{self.endpoint_id}"


reversion.register(Term)
reversion.register(Entity)
reversion.register(EntityTermRelation)
reversion.register(GuidelineProfile)
26 changes: 26 additions & 0 deletions hawc/apps/vocab/serializers.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
import json
from pathlib import Path

from django.conf import settings
from django.core.exceptions import ValidationError
from django.utils import timezone
from rest_framework import serializers

Expand Down Expand Up @@ -43,3 +48,24 @@ def get_unique_together_validators(self):
class Meta:
model = models.Entity
fields = ("id", "ontology", "uid")


class GuidelineProfileSerializer(serializers.ModelSerializer):
class Meta:
model = models.GuidelineProfile
fields = "_all_"

def validate_guideline_profile_id(self, guideline_id):
# validate guideline profile
if guideline_id:
valid_guidelines = self._load_guideline_data()
ids = [guideline["guideline_id"] for guideline in valid_guidelines]
if guideline_id not in ids:
raise ValidationError(f"{guideline_id} is not a valid guideline")

return guideline_id

def _load_guideline_data() -> dict:
"""Return guideline fixture data."""
p = Path(settings.PROJECT_PATH) / "apps/vocab/fixtures/guidelines.json"
return json.loads(p.read_text())
Loading