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

500-errors #696

Merged
merged 20 commits into from
Sep 12, 2022
Merged
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
12 changes: 6 additions & 6 deletions hawc/apps/animal/exports.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,16 +115,16 @@ def _get_data_rows(self):
row.extend(Study.flat_complete_data_row(ser["animal_group"]["experiment"]["study"]))
row.extend(models.Experiment.flat_complete_data_row(ser["animal_group"]["experiment"]))
row.extend(models.AnimalGroup.flat_complete_data_row(ser["animal_group"]))
row.extend(
models.DosingRegime.flat_complete_data_row(ser["animal_group"]["dosing_regime"])
)
ser_dosing_regime = ser["animal_group"]["dosing_regime"]
row.extend(models.DosingRegime.flat_complete_data_row(ser_dosing_regime))
row.extend(models.Endpoint.flat_complete_data_row(ser))
for i, eg in enumerate(ser["groups"]):
row_copy = copy(row)
ser_doses = ser_dosing_regime["doses"] if ser_dosing_regime else None
row_copy.extend(
models.DoseGroup.flat_complete_data_row(
ser["animal_group"]["dosing_regime"]["doses"], self.doses, i
)
models.DoseGroup.flat_complete_data_row(ser_doses, self.doses, i)
if ser_doses
else [None for _ in self.doses]
)
row_copy.extend(models.EndpointGroup.flat_complete_data_row(eg, ser))
rows.append(row_copy)
Expand Down
37 changes: 26 additions & 11 deletions hawc/apps/animal/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -318,7 +318,8 @@ def sex_symbol(self):
def get_doses_json(self, json_encode=True):
if not hasattr(self, "doses"):
self.doses = [{"error": "no dosing regime"}]
self.doses = self.dosing_regime.get_doses_json(False)
if self.dosing_regime:
self.doses = self.dosing_regime.get_doses_json(False)
if json_encode:
return json.dumps(self.doses, cls=HAWCDjangoJSONEncoder)
return self.doses
Expand Down Expand Up @@ -382,6 +383,12 @@ def delete_caches(cls, ids):
Endpoint.objects.filter(animal_group__in=ids).values_list("id", flat=True)
)

def can_delete(self) -> bool:
# can only be deleted if dosing regime is not associated with other animal groups
if not self.dosing_regime or self.dosing_regime.dosed_animals_id != self.id:
return True
return self.dosing_regime.can_delete()

def copy_across_assessments(self, cw, skip_siblings: bool = False):
children = list(self.endpoints.all().order_by("id"))
old_id = self.id
Expand Down Expand Up @@ -544,18 +551,26 @@ def flat_complete_header_row():
@staticmethod
def flat_complete_data_row(ser):
return (
ser["id"],
AnimalGroup.get_relation_id(ser["dosed_animals"]),
ser["route_of_exposure"],
ser["duration_exposure"],
ser["duration_exposure_text"],
ser["duration_observation"],
ser["num_dose_groups"],
ser["positive_control"],
ser["negative_control"],
cleanHTML(ser["description"]),
(
ser["id"],
AnimalGroup.get_relation_id(ser["dosed_animals"]),
ser["route_of_exposure"],
ser["duration_exposure"],
ser["duration_exposure_text"],
ser["duration_observation"],
ser["num_dose_groups"],
ser["positive_control"],
ser["negative_control"],
cleanHTML(ser["description"]),
)
if ser
else (None for _ in range(10))
)

def can_delete(self) -> bool:
# can delete only if no animals others than those dosed are related
return self.animalgroup_set.exclude(id=self.dosed_animals_id).count() == 0

def get_doses_json(self, json_encode=True):
doses = []
dgs = self.dose_groups.order_by("dose_units_id", "dose_group_id")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,10 @@

{% block content %}
{{ block.super }}
{% include "hawc/_delete_block.html" with name="animal-group" notes="This will remove all data associated with the animal-group, including dose-regimes, endpoints, and BMD modeling runs." %}

{% if object.can_delete %}
{% include "hawc/_delete_block.html" with name="animal-group" notes="This will remove all data associated with the animal-group, including dose-regimes, endpoints, and BMD modeling runs." %}
{% else %}
<div class="alert alert-danger" role="alert">Cannot be deleted. The dosing regime is associated with at least one more animal group; please delete associated animal groups before continuing.</div>
{% endif %}
{% endblock content %}
12 changes: 6 additions & 6 deletions hawc/apps/animal/templates/animal/animalgroup_detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,12 @@ <h2 id="animal-group-header" class="d-inline-block">{{object}}</h2>
window.app.startup("animalStartup", function(animal){
const data = window.app.getConfig();
animal.AnimalGroup.get_object(data.id, function(d){
$('h2#animal-group-header')
.after(
d.build_details_table(),
$("<h3>Dosing regime</h3>"),
d.build_dr_details_table()
);
inserts = [d.build_details_table()];
if (data.has_dosing_regime){
inserts.push($("<h3>Dosing regime</h3>"));
inserts.push(d.build_dr_details_table());
}
$('h2#animal-group-header').after(inserts);
});
animal.startupAnimalGroupTable(document.getElementById("animalGroupTableMain"), data);
});
Expand Down
7 changes: 5 additions & 2 deletions hawc/apps/animal/templates/animal/endpoint_form.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,11 @@
{% load crispy_forms_tags %}

{% block content %}

{% crispy form %}
{% if animal_group.dosing_regime %}
{% crispy form %}
{% else %}
<div class="alert alert-danger" role="alert">Cannot create or modify an endpoint. The animal group has no dose regime. Please update the animal group to add a dosing regime before continuing.</div>
{% endif %}

{# extra stuff that JS will move to correct location #}
<div id="extra" class="hidden">
Expand Down
7 changes: 6 additions & 1 deletion hawc/apps/animal/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ def get_context_data(self, **kwargs):
)
context["config"] = dict(
id=self.object.id,
has_dosing_regime=self.object.dosing_regime is not None,
endpoints=[endpoint.get_json(json_encode=False) for endpoint in endpoints],
)
return context
Expand Down Expand Up @@ -239,6 +240,8 @@ class AnimalGroupDelete(BaseDelete):
model = models.AnimalGroup

def get_success_url(self):
if not self.object.can_delete():
raise ValueError("Cannot be deleted")
return self.object.experiment.get_absolute_url()


Expand Down Expand Up @@ -332,11 +335,12 @@ def get_form_kwargs(self):
return kwargs

def build_initial_formset_factory(self):
num_groups = self.parent.dosing_regime.num_dose_groups if self.parent.dosing_regime else 1
Formset = modelformset_factory(
models.EndpointGroup,
form=forms.EndpointGroupForm,
formset=forms.BaseEndpointGroupFormSet,
extra=self.parent.dosing_regime.num_dose_groups,
extra=num_groups,
)
return Formset(queryset=models.EndpointGroup.objects.none())

Expand Down Expand Up @@ -365,6 +369,7 @@ def post_object_save(self, form, formset):

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["animal_group"] = self.parent
context["vocabulary"] = self.model.get_vocabulary_settings(self.assessment, context["form"])
return context

Expand Down
6 changes: 4 additions & 2 deletions hawc/apps/lit/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,9 +299,11 @@ def reference_search(self, request, pk):
def excel_to_json(self, request, pk):
self.get_object() # permissions check

file_ = request.data["file"]
file_ = request.data.get("file")

if not file_.name.endswith(".xlsx"):
if file_ is None:
raise ValidationError({"file": "A file is required"})
elif not file_.name.endswith(".xlsx"):
raise ValidationError({"file": "File extension must be .xlsx"})

try:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
25 changes: 25 additions & 0 deletions tests/hawc/apps/animal/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from rest_framework.test import APIClient

from hawc.apps.animal import forms, models
from hawc.apps.assessment.models import Species, Strain

from ..test_utils import check_details_of_last_log_entry

Expand Down Expand Up @@ -56,6 +57,30 @@ def test_full_export(self, rewrite_data_files: bool, db_keys):
)
self._test_flat_export(rewrite_data_files, fn, url)

def test_missing_dosing_regime(self, rewrite_data_files: bool, db_keys):
# create an animal group/endpoint with no dosing regime and make sure the export doesn't cause a 500 error
fn = "api-animal-assessment-full-export-missing-dr.json"
experiment = models.Experiment.objects.get(pk=1)
species = Species.objects.get(pk=1)
strain = Strain.objects.get(pk=1)
animal_group = models.AnimalGroup(
experiment=experiment,
species=species,
strain=strain,
sex="C",
)
animal_group.save()
endpoint = models.Endpoint(
assessment_id=db_keys.assessment_working,
animal_group=animal_group,
)
endpoint.save()
url = (
reverse("animal:api:assessment-full-export", args=(db_keys.assessment_working,))
+ "?format=json"
)
self._test_flat_export(rewrite_data_files, fn, url)

def test_endpoint_export(self, rewrite_data_files: bool, db_keys):
fn = "api-animal-assessment-endpoint-export.json"
url = (
Expand Down
46 changes: 46 additions & 0 deletions tests/hawc/apps/animal/test_models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,52 @@
import pytest

from hawc.apps.animal import models
from hawc.apps.assessment.models import Species, Strain
from hawc.apps.study.models import Study


@pytest.mark.django_db
class TestAnimalGroup:
def test_can_delete(self):
study = Study.objects.get(pk=1)
experiment = models.Experiment(
study=study,
name="test",
type="Rp",
)
experiment.save()
species = Species.objects.get(pk=1)
strain = Strain.objects.get(pk=1)
dr = models.DosingRegime(route_of_exposure="OR")
dr.save()
parent = models.AnimalGroup(
experiment=experiment,
name="parent",
sex="C",
dosing_regime=dr,
species=species,
strain=strain,
)
dr.dosed_animals = parent
parent.save()
dr.save()
child = models.AnimalGroup(
experiment=experiment,
name="child",
sex="C",
dosing_regime=dr,
species=species,
strain=strain,
)
child.save()
assert parent.can_delete() is False
assert child.can_delete() is True
child.delete()
assert parent.can_delete() is True

# animal group with no dosing regime can be deleted
parent.dosing_regime = None
assert parent.can_delete() is True


@pytest.mark.django_db
Expand Down
4 changes: 4 additions & 0 deletions tests/hawc/apps/lit/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,10 @@ def test_excel_to_json(self, db_keys):
"detail": "Missing filename. Request should include a Content-Disposition header with a filename parameter."
}

# invalid; no file returns an error
resp = c.post(url)
assert resp.status_code == 400 and resp.json() == {"file": "A file is required"}

# invalid; non excel files return an error
resp = c.post(url, {"file": csv}, HTTP_CONTENT_DISPOSITION="attachment; filename=test.csv")
assert resp.status_code == 400 and resp.json() == {"file": "File extension must be .xlsx"}
Expand Down