Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
b012dd0
initial animalv2 model declarations (#947)
DookTibs Feb 6, 2024
7495220
Merge branch 'main' into bioassay-v2
shapiromatron Jun 18, 2024
b0dbb12
Merge remote-tracking branch 'origin/main' into bioassay-v2
caseyhans Jan 15, 2025
97c8f1a
animal v2 UI (ICF) (#980)
DookTibs Jan 16, 2025
52c488a
ICF bioassay v2 add study level values (#1081)
BerkowitzMaxICF Jan 19, 2025
f878d51
Merge branch 'main' into bioassay-v2
shapiromatron Apr 22, 2025
fa3073b
Merge branch 'main' into bioassay-v2
shapiromatron Jun 2, 2025
1f28463
create animal v2 API (#1169)
tfeiler-icf Jun 3, 2025
6abbead
Merge branch 'main' into bioassay-v2
shapiromatron Jun 9, 2025
db81e7d
Icf cpheapm79 observations animalv2 (#1168)
ZindahFarhaICF Jun 10, 2025
f884ced
Merge branch 'main' into chore/HAWC-1169-merge-bioassay-branch
shanethacker Mar 6, 2026
bf7599d
Fixed linting error.
shanethacker Mar 6, 2026
315252f
Shut down pyright type checking for now and fixed an issue with djhtm…
shanethacker Mar 6, 2026
c4de34d
Updated instructions to create databases for testing
shanethacker Mar 6, 2026
164523f
Redundant migration
shanethacker Mar 6, 2026
b7a1b4e
Add IsTeamMemberOrHigher permission class for global resource APIs
shanethacker Mar 9, 2026
b8898d4
Add update/delete to Animal v1 Endpoint API + PATCH serializer support
shanethacker Mar 9, 2026
1eac2b8
Add Reference creation API for gray literature studies
shanethacker Mar 9, 2026
dfa6ea2
Add Species viewset and expand Strain viewset to full CRUD
shanethacker Mar 9, 2026
63b46be
Add Animal v2 Observation CRUD API
shanethacker Mar 9, 2026
68dfc2b
Add CREATE to DoseUnits viewset
shanethacker Mar 9, 2026
6217425
Add Guideline and GuidelineProfile CRUD APIs
shanethacker Mar 9, 2026
146f40a
Add hawc-client methods for all new API endpoints
shanethacker Mar 9, 2026
16e17e2
Add tests for all new API endpoints
shanethacker Mar 9, 2026
0d6c17e
Fix linting and formatting issues
shanethacker Mar 9, 2026
9d17cab
Fix test failures in new API endpoints
shanethacker Mar 9, 2026
399d8a1
Changed new endpoints to admin-only for now
shanethacker Mar 10, 2026
bc112f2
Added in some changes from a code review
shanethacker Mar 10, 2026
151f84e
Changed permission on endpoint caught by a test. Fixed a flaky integr…
shanethacker Mar 10, 2026
5795051
Fixing a test that's flaky on local runs.
shanethacker Mar 10, 2026
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
24 changes: 24 additions & 0 deletions client/hawc_client/animal.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,30 @@ def bmds_endpoints(self, assessment_id: int, unpublished: bool = False) -> pd.Da
response_json = self.session.get(url, params=params).json()
return pd.DataFrame(response_json)

def update_endpoint(self, endpoint_id: int, data: dict) -> dict:
"""
Update an existing endpoint via PATCH.

Args:
endpoint_id (int): Endpoint ID
data (dict): fields to update

Returns:
dict: The resulting object, if update was successful
"""
url = f"{self.session.root_url}/ani/api/endpoint/{endpoint_id}/"
return self.session.patch(url, data).json()

def delete_endpoint(self, endpoint_id: int) -> None:
"""
Delete an endpoint.

Args:
endpoint_id (int): Endpoint ID
"""
url = f"{self.session.root_url}/ani/api/endpoint/{endpoint_id}/"
self.session.delete(url)

def metadata(self) -> dict:
"""
Retrieves field choices for all animal models.
Expand Down
119 changes: 119 additions & 0 deletions client/hawc_client/assessment.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,125 @@ def delete(self, assessment_id: int) -> Response:
url = f"{self.session.root_url}/assessment/api/assessment/{assessment_id}/"
return self.session.delete(url)

def list_species(self) -> list[dict]:
"""
List all species.

Returns:
list[dict]: Species data
"""
url = f"{self.session.root_url}/assessment/api/species/"
return self.session.get(url).json()

def create_species(self, data: dict) -> dict:
"""
Create a new species.

Args:
data (dict): required metadata (name)

Returns:
dict: The created species
"""
url = f"{self.session.root_url}/assessment/api/species/"
return self.session.post(url, data).json()

def update_species(self, species_id: int, data: dict) -> dict:
"""
Update an existing species.

Args:
species_id (int): species ID
data (dict): fields to update

Returns:
dict: The updated species
"""
url = f"{self.session.root_url}/assessment/api/species/{species_id}/"
return self.session.patch(url, data).json()

def delete_species(self, species_id: int) -> Response:
"""
Delete a species.

Args:
species_id (int): species ID

Returns:
Response: The response object.
"""
url = f"{self.session.root_url}/assessment/api/species/{species_id}/"
return self.session.delete(url)

def list_strains(self, species_id: int | None = None) -> list[dict]:
"""
List all strains, optionally filtered by species.

Args:
species_id (int, optional): Filter by species ID

Returns:
list[dict]: Strain data
"""
url = f"{self.session.root_url}/assessment/api/strain/"
params = {}
if species_id is not None:
params["species"] = species_id
return self.session.get(url, params=params).json()

def create_strain(self, data: dict) -> dict:
"""
Create a new strain.

Args:
data (dict): required metadata (name, species)

Returns:
dict: The created strain
"""
url = f"{self.session.root_url}/assessment/api/strain/"
return self.session.post(url, data).json()

def update_strain(self, strain_id: int, data: dict) -> dict:
"""
Update an existing strain.

Args:
strain_id (int): strain ID
data (dict): fields to update

Returns:
dict: The updated strain
"""
url = f"{self.session.root_url}/assessment/api/strain/{strain_id}/"
return self.session.patch(url, data).json()

def delete_strain(self, strain_id: int) -> Response:
"""
Delete a strain.

Args:
strain_id (int): strain ID

Returns:
Response: The response object.
"""
url = f"{self.session.root_url}/assessment/api/strain/{strain_id}/"
return self.session.delete(url)

def create_dose_units(self, name: str) -> dict:
"""
Create a new dose unit.

Args:
name (str): Name of the dose unit

Returns:
dict: The created dose unit
"""
url = f"{self.session.root_url}/ani/api/dose-units/"
return self.session.post(url, {"name": name}).json()

def effect_tag_create(self, name: str, slug: str) -> dict:
"""Create an effect tag.

Expand Down
14 changes: 14 additions & 0 deletions client/hawc_client/literature.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,20 @@ def _import(
url = f"{self.session.root_url}/lit/api/search/"
return self.session.post(url, payload).json()

def create_reference(self, data: dict) -> dict:
"""
Create a manual reference (no external ID required).

Args:
data (dict): Reference data. Required: assessment, title.
Optional: authors_short, authors, year, journal, abstract, full_text_url.

Returns:
dict: The created reference
"""
url = f"{self.session.root_url}/lit/api/reference/"
return self.session.post(url, data).json()

def import_hero(self, assessment_id: int, title: str, description: str, ids: list[int]) -> dict:
"""
Imports a list of HERO IDs as literature references for the given assessment.
Expand Down
108 changes: 108 additions & 0 deletions client/hawc_client/vocab.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from requests import Response

from .client import BaseClient


Expand Down Expand Up @@ -32,6 +34,112 @@ def bulk_update(self, terms: list[dict]) -> list[dict]:
url = f"{self.session.root_url}/vocab/api/term/bulk-update/"
return self.session.patch(url, terms).json()

def list_guidelines(self) -> list[dict]:
"""
List all guidelines.

Returns:
list[dict]: Guideline data
"""
url = f"{self.session.root_url}/vocab/api/guideline/"
return self.session.get(url).json()

def create_guideline(self, data: dict) -> dict:
"""
Create a new guideline.

Args:
data (dict): required metadata

Returns:
dict: The created guideline
"""
url = f"{self.session.root_url}/vocab/api/guideline/"
return self.session.post(url, data).json()

def update_guideline(self, guideline_id: int, data: dict) -> dict:
"""
Update an existing guideline.

Args:
guideline_id (int): guideline ID
data (dict): fields to update

Returns:
dict: The updated guideline
"""
url = f"{self.session.root_url}/vocab/api/guideline/{guideline_id}/"
return self.session.patch(url, data).json()

def delete_guideline(self, guideline_id: int) -> Response:
"""
Delete a guideline.

Args:
guideline_id (int): guideline ID

Returns:
Response: The response object.
"""
url = f"{self.session.root_url}/vocab/api/guideline/{guideline_id}/"
return self.session.delete(url)

def list_guideline_profiles(self, guideline_id: int | None = None) -> list[dict]:
"""
List all guideline profiles, optionally filtered by guideline.

Args:
guideline_id (int, optional): Filter by guideline ID

Returns:
list[dict]: GuidelineProfile data
"""
url = f"{self.session.root_url}/vocab/api/guideline-profile/"
params = {}
if guideline_id is not None:
params["guideline"] = guideline_id
return self.session.get(url, params=params).json()

def create_guideline_profile(self, data: dict) -> dict:
"""
Create a new guideline profile.

Args:
data (dict): required metadata

Returns:
dict: The created guideline profile
"""
url = f"{self.session.root_url}/vocab/api/guideline-profile/"
return self.session.post(url, data).json()

def update_guideline_profile(self, profile_id: int, data: dict) -> dict:
"""
Update an existing guideline profile.

Args:
profile_id (int): guideline profile ID
data (dict): fields to update

Returns:
dict: The updated guideline profile
"""
url = f"{self.session.root_url}/vocab/api/guideline-profile/{profile_id}/"
return self.session.patch(url, data).json()

def delete_guideline_profile(self, profile_id: int) -> Response:
"""
Delete a guideline profile.

Args:
profile_id (int): guideline profile ID

Returns:
Response: The response object.
"""
url = f"{self.session.root_url}/vocab/api/guideline-profile/{profile_id}/"
return self.session.delete(url)

def uids(self) -> list[tuple[int, int]]:
"""
Get all term ids and uids.
Expand Down
6 changes: 6 additions & 0 deletions docs/docs/database.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ The HAWC database is a [PostgreSQL](https://www.postgresql.org/) database. See d

## Animal bioassay schema

<figure markdown>
![HAWC animal bioassay v2 data schema](./static/img/hawc-schema-animalv2.png)
<figcaption>Animal bioassay v2 schema. The image is very large; please save/or open in another tab.</figcaption>
</figure>

<figure markdown>
![HAWC animal bioassay data schema](./static/img/hawc-schema-animal.png)
<figcaption>Animal bioassay schema. The image is very large; please save/or open in another tab.</figcaption>
Expand Down Expand Up @@ -84,6 +89,7 @@ pip install -U pydot
manage graph_models -g --pydot -o ./docs/docs/static/img/hawc-schema-lit.png lit study
manage graph_models -g --pydot -o ./docs/docs/static/img/hawc-schema-study.png study riskofbias
manage graph_models -g --pydot -o ./docs/docs/static/img/hawc-schema-animal.png animal
manage graph_models -g --pydot -o ./docs/docs/static/img/hawc-schema-animalv2.png animalv2
manage graph_models -g --pydot -o ./docs/docs/static/img/hawc-schema-bmd.png bmd
manage graph_models -g --pydot -o ./docs/docs/static/img/hawc-schema-epi.png epi
manage graph_models -g --pydot -o ./docs/docs/static/img/hawc-schema-epiv2.png epiv2
Expand Down
9 changes: 8 additions & 1 deletion docs/docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ uv pip install -e client
# create a PostgreSQL database and superuser
createuser --superuser --no-password hawc
createdb -E UTF-8 -U hawc hawc

# create test databases (required for running unit tests)
createdb -T template0 -E UTF8 hawc-fixture
createdb -T template0 -E UTF8 hawc-test
```

For Windows, using Anaconda or Miniconda is preferred for additional dependencies:
Expand Down Expand Up @@ -76,9 +80,12 @@ pg_ctl -D pgdata initdb
mkdir pgdata\logs
pg_ctl -D pgdata -l pgdata\logs\logfile start

:: create our superuser and main/test databases
:: create a PostgreSQL database and superuser
createuser --superuser --no-password hawc
createdb -T template0 -E UTF8 hawc

:: create test databases (required for running unit tests)
createdb -T template0 -E UTF8 hawc-fixture
createdb -T template0 -E UTF8 hawc-test
```

Expand Down
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
Loading
Loading