Skip to content

Commit

Permalink
Merge pull request #244 from AllenNeuralDynamics/feat-236-IP-injections
Browse files Browse the repository at this point in the history
Maps IP Injections
  • Loading branch information
saskiad authored Jul 5, 2024
2 parents 92d3fe9 + dc22272 commit 2706dab
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 75 deletions.
7 changes: 4 additions & 3 deletions src/aind_metadata_service/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
LabTracksClient,
LabTracksSettings,
)

from aind_metadata_service.response_handler import EtlResponse
from aind_metadata_service.sharepoint.client import (
SharePointClient,
Expand Down Expand Up @@ -300,9 +301,9 @@ async def retrieve_procedures(subject_id, pickle: bool = False):
# smartsheet_response = await retrieve_protocols(
# protocol_name=protocol_name
# )
protocols_mapping[protocol_name] = (
model_response.map_to_json_response()
)
protocols_mapping[
protocol_name
] = model_response.map_to_json_response()
integrated_response = protocols_integrator.integrate_protocols(
response=integrated_response, protocols_mapping=protocols_mapping
)
Expand Down
6 changes: 3 additions & 3 deletions src/aind_metadata_service/sharepoint/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,9 +268,9 @@ def _merge_two_responses(
)
else:
left_procedures: List[Procedures] = left_model_response.aind_models
right_procedures: List[Procedures] = (
right_model_response.aind_models
)
right_procedures: List[
Procedures
] = right_model_response.aind_models
procedures = self._merge_procedures(
left_procedures=left_procedures,
right_procedures=right_procedures,
Expand Down
163 changes: 110 additions & 53 deletions src/aind_metadata_service/sharepoint/las2020/mapping.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
"""Module template was autogenerated from sharepoint list schema."""

from dataclasses import dataclass
from datetime import date, datetime
from decimal import Decimal, DecimalException
from enum import Enum
from typing import Any, Optional

from aind_data_schema.core.procedures import Surgery
from aind_data_schema.core.procedures import Surgery, IntraperitonealInjection

from aind_metadata_service.sharepoint.las2020.models import LASList
from aind_metadata_service.sharepoint.las2020.models import LASList, Doseroute


class IacucProtocol(Enum):
Expand Down Expand Up @@ -46,6 +46,28 @@ class IacucProtocol(Enum):
N_2405 = "2405"


@dataclass
class RequestedProcedureInfo:
"""Container for requested procedure information"""

procedure: Optional[str] = None
preferred_age: Optional[Decimal] = None
preferred_date: Optional[date] = None


class LASProcedure(Enum):
"""Enum class of requested procedure types"""

BLOOD_COLLECTION = "Blood Collection"
CEREBROSPINAL_FLUID = "Cerebrospinal Fluid Collection (CSF)"
DOSING = "Dosing"
INDUCTION = "Induction"
IC_INJECTION = "Intracerebroventricular Injection"
OTHER = "Other"
RETRO_ORBITAL_INJECTION = "Retro-Orbital Injection"
TISSUE_COLLECTION = "Tissue Collection"


class MappedLASList:
"""Mapped Fields in Sharepoint list"""

Expand Down Expand Up @@ -209,19 +231,9 @@ def aind_custpresent(self) -> Optional[bool]:
return self._las.custpresent

@property
def aind_dose_route(self) -> Optional[Any]:
def aind_dose_route(self) -> Optional[Doseroute]:
"""Maps dose_route to aind model"""
return (
None
if self._las.dose_route is None
else {
self._las.dose_route.INTRAMUSCULAR_IM: None,
self._las.dose_route.INTRAPERITONEAL_IP: None,
self._las.dose_route.INTRAVENOUS_IV: None,
self._las.dose_route.ORAL_GAVAGE_PO: None,
self._las.dose_route.SUBCUTANEOUS_SC: None,
}.get(self._las.dose_route, None)
)
return self._las.dose_route

@property
def aind_dose_sub(self) -> Optional[str]:
Expand All @@ -234,14 +246,14 @@ def aind_dose_where(self) -> Optional[str]:
return self._las.dose_where

@property
def aind_doseduration(self) -> Optional[str]:
def aind_doseduration(self) -> Optional[Decimal]:
"""Maps doseduration to aind model"""
return self._las.doseduration
return self._parse_basic_decimal_str(self._las.doseduration)

@property
def aind_dosevolume(self) -> Optional[str]:
def aind_dosevolume(self) -> Optional[Decimal]:
"""Maps dosevolume to aind model"""
return self._las.dosevolume
return self._parse_basic_decimal_str(self._las.dosevolume)

@property
def aind_doxycycline(self) -> Optional[bool]:
Expand Down Expand Up @@ -1003,73 +1015,103 @@ def aind_req_age3(self) -> Optional[str]:
return self._las.req_age3

@property
def aind_req_pro1(self) -> Optional[Any]:
def aind_req_pro1(self) -> Optional[LASProcedure]:
"""Maps req_pro1 to aind model"""
return (
None
if self._las.req_pro1 is None
else {
self._las.req_pro1.TISSUE_COLLECTION: None,
self._las.req_pro1.INDUCTION: None,
self._las.req_pro1.DOSING: None,
self._las.req_pro1.RETRO_ORBITAL_INJECTION: None,
self._las.req_pro1.INTRACEREBROVENTRICULAR_I: None,
self._las.req_pro1.BLOOD_COLLECTION: None,
self._las.req_pro1.CEREBROSPINAL_FLUID_COLLE: None,
self._las.req_pro1.OTHER: None,
self._las.req_pro1.TISSUE_COLLECTION: (
LASProcedure.TISSUE_COLLECTION
),
self._las.req_pro1.INDUCTION: LASProcedure.INDUCTION,
self._las.req_pro1.DOSING: LASProcedure.DOSING,
self._las.req_pro1.RETRO_ORBITAL_INJECTION: (
LASProcedure.RETRO_ORBITAL_INJECTION
),
self._las.req_pro1.INTRACEREBROVENTRICULAR_I: (
LASProcedure.IC_INJECTION
),
self._las.req_pro1.BLOOD_COLLECTION: (
LASProcedure.BLOOD_COLLECTION
),
self._las.req_pro1.CEREBROSPINAL_FLUID_COLLE: (
LASProcedure.CEREBROSPINAL_FLUID
),
self._las.req_pro1.OTHER: LASProcedure.OTHER,
}.get(self._las.req_pro1, None)
)

@property
def aind_req_pro2(self) -> Optional[Any]:
def aind_req_pro2(self) -> Optional[LASProcedure]:
"""Maps req_pro2 to aind model"""
return (
None
if self._las.req_pro2 is None
else {
self._las.req_pro2.TISSUE_COLLECTION: None,
self._las.req_pro2.INDUCTION: None,
self._las.req_pro2.DOSING: None,
self._las.req_pro2.RETRO_ORBITAL_INJECTION: None,
self._las.req_pro2.INTRACEREBROVENTRICULAR_I: None,
self._las.req_pro2.BLOOD_COLLECTION: None,
self._las.req_pro2.CEREBROSPINAL_FLUID_COLLE: None,
self._las.req_pro2.OTHER: None,
self._las.req_pro2.TISSUE_COLLECTION: (
LASProcedure.TISSUE_COLLECTION
),
self._las.req_pro2.INDUCTION: LASProcedure.INDUCTION,
self._las.req_pro2.DOSING: LASProcedure.DOSING,
self._las.req_pro2.RETRO_ORBITAL_INJECTION: (
LASProcedure.RETRO_ORBITAL_INJECTION
),
self._las.req_pro2.INTRACEREBROVENTRICULAR_I: (
LASProcedure.IC_INJECTION
),
self._las.req_pro2.BLOOD_COLLECTION: (
LASProcedure.BLOOD_COLLECTION
),
self._las.req_pro2.CEREBROSPINAL_FLUID_COLLE: (
LASProcedure.CEREBROSPINAL_FLUID
),
self._las.req_pro2.OTHER: LASProcedure.OTHER,
}.get(self._las.req_pro2, None)
)

@property
def aind_req_pro3(self) -> Optional[Any]:
def aind_req_pro3(self) -> Optional[LASProcedure]:
"""Maps req_pro3 to aind model"""
return (
None
if self._las.req_pro3 is None
else {
self._las.req_pro3.TISSUE_COLLECTION: None,
self._las.req_pro3.INDUCTION: None,
self._las.req_pro3.DOSING: None,
self._las.req_pro3.RETRO_ORBITAL_INJECTION: None,
self._las.req_pro3.INTRACEREBROVENTRICULAR_I: None,
self._las.req_pro3.BLOOD_COLLECTION: None,
self._las.req_pro3.CEREBROSPINAL_FLUID_COLLE: None,
self._las.req_pro3.OTHER: None,
self._las.req_pro3.TISSUE_COLLECTION: (
LASProcedure.TISSUE_COLLECTION
),
self._las.req_pro3.INDUCTION: (LASProcedure.INDUCTION),
self._las.req_pro3.DOSING: (LASProcedure.DOSING),
self._las.req_pro3.RETRO_ORBITAL_INJECTION: (
LASProcedure.RETRO_ORBITAL_INJECTION
),
self._las.req_pro3.INTRACEREBROVENTRICULAR_I: (
LASProcedure.IC_INJECTION
),
self._las.req_pro3.BLOOD_COLLECTION: (
LASProcedure.BLOOD_COLLECTION
),
self._las.req_pro3.CEREBROSPINAL_FLUID_COLLE: (
LASProcedure.CEREBROSPINAL_FLUID
),
self._las.req_pro3.OTHER: LASProcedure.OTHER,
}.get(self._las.req_pro3, None)
)

@property
def aind_reqdate1(self) -> Optional[datetime]:
def aind_reqdate1(self) -> Optional[date]:
"""Maps reqdate1 to aind model"""
return self._las.reqdate1
return self._parse_datetime_to_date(self._las.reqdate1)

@property
def aind_reqdate2(self) -> Optional[datetime]:
def aind_reqdate2(self) -> Optional[date]:
"""Maps reqdate2 to aind model"""
return self._las.reqdate2
return self._parse_datetime_to_date(self._las.reqdate2)

@property
def aind_reqdate3(self) -> Optional[datetime]:
def aind_reqdate3(self) -> Optional[date]:
"""Maps reqdate3 to aind model"""
return self._las.reqdate3
return self._parse_datetime_to_date(self._las.reqdate3)

@property
def aind_request_status2(self) -> Optional[Any]:
Expand Down Expand Up @@ -1873,10 +1915,25 @@ def aind_whereto_obtainsubstance_i(self) -> Optional[Any]:
}.get(self._las.whereto_obtainsubstance_i, None)
)

def has_ip_injection(self) -> bool:
"""Is there a IP injection procedure?"""
return (
LASProcedure.DOSING
in [self.aind_req_pro1, self.aind_req_pro2, self.aind_req_pro3]
and self.aind_dose_route == Doseroute.INTRAPERITONEAL_IP
)

def get_procedure(self) -> Surgery:
"""Return Surgery as best as possible from a record."""
# TODO: add RO Injection and IP Injections to procedures
# TODO: add RO Injection to procedures
subject_procedures = []
if self.has_ip_injection():
# TODO: map injection_materials, protocol_id
ip_injection = IntraperitonealInjection.model_construct(
injection_volume=self.aind_dosevolume,
injection_duration=self.aind_doseduration,
)
subject_procedures.append(ip_injection)
return Surgery.model_construct(
experimenter_full_name=self.aind_author_id,
iacuc_protocol=self.aind_protocol,
Expand Down
8 changes: 4 additions & 4 deletions src/aind_metadata_service/smartsheet/protocols/mapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,10 @@ def _get_protocol_list(
"""
rows_associated_with_protocol_name: List[ProtocolInformation] = []
for row in self.model.rows:
opt_protocol: Optional[ProtocolInformation] = (
self._map_row_to_protocol(
row=row, input_protocol_name=protocol_name
)
opt_protocol: Optional[
ProtocolInformation
] = self._map_row_to_protocol(
row=row, input_protocol_name=protocol_name
)
if opt_protocol is not None:
rows_associated_with_protocol_name.append(opt_protocol)
Expand Down
14 changes: 8 additions & 6 deletions src/aind_metadata_service/tars/mapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,10 +221,12 @@ def get_virus_strains(response: ModelResponse) -> List:
and hasattr(procedure, "injection_materials")
and isinstance(procedure.injection_materials, list)
and procedure.injection_materials
and hasattr(procedure.injection_materials[0], "name")
):
virus_strain = procedure.injection_materials[0].name
viruses.append(virus_strain)
virus_strains = [
getattr(material, "name")
for material in procedure.injection_materials
]
viruses.extend(virus_strains)
return viruses

@staticmethod
Expand Down Expand Up @@ -268,9 +270,9 @@ def integrate_injection_materials(
new_material.titer = (
injection_material.titer
)
procedure.injection_materials[idx] = (
new_material
)
procedure.injection_materials[
idx
] = new_material
elif (
tars_response.status_code
== StatusCodes.NO_DATA_FOUND.value
Expand Down
15 changes: 13 additions & 2 deletions tests/resources/sharepoint/las2020/mapped/mapped_list_item1.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,17 @@
"weight_unit": "gram",
"anaesthesia": null,
"workstation_id": null,
"procedures": [],
"procedures": [
{
"injection_duration": "30",
"injection_duration_unit": "minute",
"injection_volume": "70.4",
"injection_volume_unit": "microliter",
"instrument_id": null,
"procedure_type": "Intraperitoneal injection",
"recovery_time": null,
"recovery_time_unit": "minute"
}
],
"notes": null
}
}
8 changes: 4 additions & 4 deletions tests/resources/sharepoint/las2020/raw/list_item1.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@
"CustomCom1": "Recover headpost, if possible.",
"CustomCom2": null,
"custpresent": false,
"doseduration": null,
"doseRoute": null,
"doseduration": "30",
"doseRoute": "Intraperitoneal (IP)",
"doseSub": null,
"dosevolume": null,
"dosevolume": "70.4",
"doseWhere": null,
"Doxycycline": false,
"LTaID1": null,
Expand Down Expand Up @@ -62,7 +62,7 @@
"Reqdate2": null,
"Reqdate3": null,
"ReqPro1": "Tissue Collection",
"ReqPro2": null,
"ReqPro2": "Dosing",
"ReqPro3": null,
"roEye1": null,
"roEye2": null,
Expand Down

0 comments on commit 2706dab

Please sign in to comment.