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 cpheapm79 observations animalv2 #1168

Open
wants to merge 8 commits into
base: bioassay-v2
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
8 changes: 4 additions & 4 deletions frontend/animal/EndpointForm/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,14 @@ const termUrlLookupMap = {
endpoint_name_parent: "effect_subtype_term_id",
},
helpText: {
system_popup: `The health effect category/biological system an endpoint/outcome or group of related endpoints/outcomes within a health effect category. "Multi-system" is an option for widespread effects. If the Endpoint is measured in Blood, Urine, or biological media other than the affected system, extract the media term in the Effect Subtype field.`,
system_popup: `The health effect category/biological system an endpoint/outcome or group of related endpoints/outcomes within a health effect category. 'System' maps to 'endpoint_category' in ToxRefDB as the broadest descriptive term for an endpoint. Possible endpoint categories include: systemic, developmental, reproductive, and cholinesterase.`,
system: `The health effect category/biological system an endpoint/outcome or group of related endpoints/outcomes within a health effect category. Please use a controlled vocabulary term if possible.`,
effect_popup: `A group of related outcomes considered as a health effect category and/or unit of analysis typically considered together during evidence synthesis.`,
effect_popup: `A group of related outcomes considered as a health effect category and/or unit of analysis typically considered together during evidence synthesis. 'Effect' maps to 'endpoint_type' in ToxRefDB as a subcategory for endpoint_category, which is more descriptive for a particular endpoint (e.g. pathology gross, clinical chemistry, reproductive performance, etc.`,
effect: `Related outcomes (e.g., unit of analysis) considered together during evidence synthesis. Please use a controlled vocabulary term if possible.`,
effect_subtype_popup: `An outcome or measurement within an effect.`,
effect_subtype_popup: `An outcome or measurement within an effect. 'Effect_subtype' maps to 'Endpoint_target' in ToxRefDB indicating where or how the sample was collected to supply data for a particular endpoint. Typically describes an organ/tissue or metabolite/protein measured.`,
effect_subtype: `Please use a controlled vocabulary term if possible.`,
endpoint_name_popup: `An observable or measurable biological change used as an index of a potential health effect of an exposure. Endpoint may also be referred to as effect, outcome, or event. Search for the best match based on the author reported term. Use the field "Diagnostic (as reported)" to capture as reported by study authors. If no existing term matches, deselect use EHV for endpoint and enter the name as reported. Do not include units. If an endpoint is a repeated measure, indicate the time in parentheses, [e.g., running wheel activity (6 wk)], using the abbreviated format: seconds = sec, minutes = min, hours = h, days = d, weeks = wk, months = mon, years = y.`,
endpoint_name: `An observable or measurable biological change used as an index of a potential health effect of an exposure. Endpoint may also be referred to as effect or outcome. For a searchable list of ToxRefDB Vocabulary terms, see the <a href="/vocab/toxrefdb/">ToxRefDB.</a> Enter the term ID and all relationships to this term will automatically populate. Please use a controlled vocabulary term if possible.`,
endpoint_name: `An observable or measurable biological change used as an index of a potential health effect of an exposure. 'Endpoint_name' maps to 'Effect_desc' in ToxRefDB, detailing a specific condition associated with an endpoint_target (e.g. dysplasia, atrophy, necrosis, etc.`,
},
},
defaultHelpText = {
Expand Down
8 changes: 4 additions & 4 deletions frontend/animal/VocabBrowser/ToxRefDBBrowser/Table.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,28 +44,28 @@ class Table extends Component {
<thead>
<tr>
<th>
Endpoint Category
System
<HelpTextPopup
title="Endpoint Category"
content={helpText.system_popup}
/>
</th>
<th>
Endpoint Type
Effect
<HelpTextPopup
title="Endpoint Type"
content={helpText.effect_popup}
/>
</th>
<th>
Endpoint Target
Effect Subtype
<HelpTextPopup
title="Endpoint Target"
content={helpText.effect_subtype_popup}
/>
</th>
<th>
Effect Description
Endpoint/Outcome
<HelpTextPopup
title="Effect Description"
content={helpText.endpoint_name_popup}
Expand Down
7 changes: 7 additions & 0 deletions hawc/apps/animalv2/autocomplete.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,10 @@
class ChemicalAutocomplete(BaseAutocomplete):
model = models.Chemical
search_fields = ["name"]


@register
class ExperimentAutocomplete(BaseAutocomplete):
model = models.Experiment
search_fields = ["name"]
filter_fields = ["study_id", "study__assessment_id"]
81 changes: 81 additions & 0 deletions hawc/apps/animalv2/filterset.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import django_filters as df
from django.db.models import Q

from ..common.autocomplete import AutocompleteTextWidget
from ..common.filterset import (
BaseFilterSet,
InlineFilterForm,
PaginationFilter,
)
from . import autocomplete, models


class ExperimentFilterSet(BaseFilterSet):
query = df.CharFilter(
method="filter_query",
label="Short Citation",
help_text="Filter citations (author, year, title, ID)",
)
name = df.CharFilter(
lookup_expr="icontains",
label="Experiment name",
widget=AutocompleteTextWidget(
autocomplete_class=autocomplete.ExperimentAutocomplete,
field="name",
attrs={
"data-placeholder": "Filter by name (ex: developmental)",
"class": "autocompletefilter",
},
),
)
design = df.CharFilter(
lookup_expr="icontains",
label="Experiment design",
widget=AutocompleteTextWidget(
autocomplete_class=autocomplete.ExperimentAutocomplete,
field="design",
attrs={"data-placeholder": "Filter by design (ex: B)", "class": "autocompletefilter"},
),
)
chemical = df.CharFilter(
field_name="v2_chemicals__name",
lookup_expr="icontains",
label="Chemical name",
widget=AutocompleteTextWidget(
autocomplete_class=autocomplete.ChemicalAutocomplete,
field="name",
attrs={"data-placeholder": "Chemical name", "class": "autocompletefilter"},
),
help_text="ex: sodium",
)
cas = df.CharFilter(
field_name="v2_chemicals__cas",
lookup_expr="icontains",
label="CAS",
widget=AutocompleteTextWidget(
autocomplete_class=autocomplete.ChemicalAutocomplete,
field="cas",
attrs={"data-placeholder": "CAS", "class": "autocompletefilter"},
),
help_text="ex: 107-02-8",
)
paginate_by = PaginationFilter()

class Meta:
model = models.Experiment
form = InlineFilterForm
fields = ["query", "name", "design", "paginate_by"]
main_field = "query"
appended_fields = ["name", "design", "chemical", "cas", "paginate_by"]

def __init__(self, *args, assessment, **kwargs):
super().__init__(*args, assessment=assessment, **kwargs)

def filter_queryset(self, queryset):
queryset = super().filter_queryset(queryset).prefetch_related("v2_chemicals")
queryset = queryset.filter(study__assessment=self.assessment, study__bioassay=True)
return queryset

def filter_query(self, queryset, name, value):
query = Q(study__short_citation__icontains=value)
return queryset.filter(query)
4 changes: 4 additions & 0 deletions hawc/apps/animalv2/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from ..assessment.autocomplete import DSSToxAutocomplete
from ..common.autocomplete import AutocompleteSelectWidget, AutocompleteTextWidget
from ..common.forms import BaseFormHelper
from ..vocab.models import GuidelineProfile
from . import autocomplete, constants, models


Expand Down Expand Up @@ -66,6 +67,9 @@ def __init__(self, *args, **kwargs):
self.fields["has_multiple_generations"].widget = forms.Select(
choices=((True, "Yes"), (False, "No"))
)
self.fields["guideline"].widget = forms.Select(
choices=GuidelineProfile.objects.get_guideline_choices()
)

@property
def helper(self):
Expand Down
17 changes: 17 additions & 0 deletions hawc/apps/animalv2/migrations/0003_experiment_guideline.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 5.1.6 on 2025-02-09 22:39

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("animalv2", "0002_studylevelvalue"),
]

operations = [
migrations.AddField(
model_name="experiment",
name="guideline",
field=models.CharField(blank=True, max_length=128, null=True),
),
]
1 change: 1 addition & 0 deletions hawc/apps/animalv2/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class Experiment(models.Model):
blank=True,
help_text="""Description of any compliance methods used (i.e. use of EPA OECD, NTP, or other guidelines; conducted under GLP guideline conditions, non-GLP but consistent with guideline study, etc.). This field response should match any description used in study evaluation in the reporting quality domain, e.g., GLP study (OECD guidelines 414 and 412, 1981 versions). If not reported, then use state \"not reported.\"""",
)
guideline = models.CharField(max_length=128, blank=True, null=True)
comments = models.TextField(
blank=True,
help_text="Additional comments (eg., description, animal husbandry, etc.)",
Expand Down
4 changes: 4 additions & 0 deletions hawc/apps/animalv2/templates/animalv2/experiment_detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ <h2>{{object.name}}</h2>
<span class="dropdown-header">Experiment editing</span>
<a class="dropdown-item" href="{{object.get_update_url}}">Update</a>
<a class="dropdown-item" href="{{object.get_delete_url}}">Delete</a>
<a class="dropdown-item" href="{% url 'vocab:api:guideline_profile-export' object.guideline %}?format=csv">View guideline profiles</a>
{% if assessment.enable_observations and object.guideline %}
<a class="dropdown-item" href="{% url 'vocab:observation-list' object.pk %}?order_by=system">View observations</a>
{% endif %}
{% endactions %}
{% endif %}
</div>
Expand Down
39 changes: 39 additions & 0 deletions hawc/apps/animalv2/templates/animalv2/experiment_list.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{% extends 'assessment-rooted.html' %}

{% block content %}
<div class="d-flex">
<h2>Available bioassay experiments ({{page_obj.paginator.count}} found)</h2>
</div>

{% include 'common/inline_filter_form.html' %}

<table id="mainTbl" class="table table-sm table-striped">
{% bs4_colgroup '25,25,50' %}
{% bs4_thead 'Short citation,Experiment name,Experiment design' %}
<tbody>
{% for object in object_list %}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ticket 79: "display a list of bioassay experiments with short citation, experiment name, experiment type, chemical name, and casrn. Search should be able to filter by each of these fields"

  • replaced 'experiment type' with 'design': not sure if there's an equivalent in animal_v2.
  • Filtering by chemical and casrn works, but I removed the fields from the table row (each experiment can have multiple chemicals/casrns in animalv2)

<tr>
<td>
<a href="{% url 'vocab:observation-list' object.pk %}?order_by=system">{{ object.study.short_citation }}</a>
{% debug_badge object.id %}
</td>
<td>
<a href="{% url 'study:detail' object.pk %}">{{ object.name }}</a>
</td>
<td>
<a href="{% url 'study:detail' object.pk %}">{{ object.design }}</a>
</td>
</tr>
{% empty %}
<tr>
<td colspan="7">
No experiments available
</td>
</tr>
{% endfor %}
</tbody>
</table>

{% include 'includes/paginator.html' with plural_object_name='experiments' %}

{% endblock %}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
{% optional_table_row "Design" object.get_design_display %}
{% optional_table_row "Multiple generations" object.get_has_multiple_generations_display %}
{% optional_table_row "Guideline compliance" object.guideline_compliance %}
{% optional_table_row "Guideline" object.guideline %}
{% optional_table_row "Comments" object.comments %}
</tbody>
</table>
1 change: 1 addition & 0 deletions hawc/apps/animalv2/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
views.ExperimentDetail.as_view(),
name="experiment_detail",
),
path("assessment/<int:pk>/", views.ExperimentFilterList.as_view(), name="experiment_list"),
path(
"experiment/<int:pk>/delete/",
views.ExperimentDelete.as_view(),
Expand Down
15 changes: 14 additions & 1 deletion hawc/apps/animalv2/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,22 @@
from django.http import HttpRequest
from django.shortcuts import render

from ..assessment.models import Assessment
from ..common.forms import FormsetGenericFormHelper
from ..common.htmx import HtmxViewSet, Item, action, can_edit, can_view
from ..common.views import (
BaseCreate,
BaseDelete,
BaseDetail,
BaseFilterList,
BaseList,
BaseUpdate,
FormsetConfiguration,
create_object_log,
)
from ..mgmt.views import EnsureExtractionStartedMixin
from ..study.models import Study
from . import forms, models
from . import filterset, forms, models

# TODO - make sure HTML views efficiently query database, HTMX views lower priority

Expand Down Expand Up @@ -258,6 +260,17 @@ def get_context_data(self, **kwargs):
return context


class ExperimentFilterList(BaseFilterList):
template_name = "animalv2/experiment_list.html"
parent_model = Assessment
model = models.Experiment
filterset_class = filterset.ExperimentFilterSet
paginate_by = 50

def get_queryset(self):
return super().get_queryset()


class ChemicalViewSet(ExperimentChildViewSet):
model = models.Chemical
form_class = forms.ChemicalForm
Expand Down
17 changes: 16 additions & 1 deletion hawc/apps/assessment/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ class Meta:
"enable_summary_tables",
"enable_visuals",
"enable_downloads",
"enable_observations",
"noel_name",
"rob_name",
"vocabulary",
Expand Down Expand Up @@ -354,13 +355,27 @@ def helper(self):
)
helper.add_row("enable_literature_review", 3, "col-lg-4")
helper.add_row("enable_risk_of_bias", 3, "col-lg-4")
helper.add_row("enable_visuals", 2, "col-lg-6")
helper.add_row("enable_visuals", 3, "col-lg-4")
helper.add_row("noel_name", 3, "col-lg-4")
helper.add_row(
"epi_version", 2 if settings.HAWC_FEATURES.ENABLE_BIOASSAY_V2 else 1, "col-lg-4"
)
return helper

def clean(self):
cleaned_data = super().clean()

obs_enabled = cleaned_data.get("enable_observations")
vocabulary = cleaned_data.get("vocabulary")

if obs_enabled and vocabulary != VocabularyNamespace.ToxRefDB:
self.add_error(
"enable_observations",
"Toxicity Reference Database Vocabulary must be selected to enable observations.",
)

return cleaned_data


class AttachmentForm(forms.ModelForm):
class Meta:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Generated by Django 5.1.6 on 2025-02-09 23:39

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("assessment", "0048_assessment_animal_version"),
]

operations = [
migrations.AddField(
model_name="assessment",
name="enable_observations",
field=models.BooleanField(
default=True,
help_text="Observations can be used to identify negative effects in animal bioassay studies. The project must use the Toxicity Reference Database Vocabulary to use Observations.",
),
),
]
4 changes: 4 additions & 0 deletions hawc/apps/assessment/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,10 @@ def get_rob_name_default():
default=True,
help_text="Show the downloads link on the assessment sidebar.",
)
enable_observations = models.BooleanField(
default=True,
help_text="Observations can be used to identify negative effects in animal bioassay studies. The project must use the Toxicity Reference Database Vocabulary to use Observations.",
)
conflicts_of_interest = models.TextField(
blank=True,
help_text="Describe any conflicts of interest by the assessment-team.",
Expand Down
13 changes: 13 additions & 0 deletions hawc/apps/vocab/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,16 @@ def get_queryset(self, request):

def has_delete_permission(self, request, obj=None):
return False


@admin.register(models.GuidelineProfile)
class GuidelineProfileAdmin(admin.ModelAdmin):
list_display = (
"id",
"guideline_id",
"endpoint",
"obs_status",
"description",
)
list_filter = ("guideline_id", "obs_status")
search_fields = ("guideline id", "obs_status")
Loading