Skip to content

Commit

Permalink
Update injection parameters [skip CI]
Browse files Browse the repository at this point in the history
  • Loading branch information
bjhardcastle committed May 9, 2024
1 parent 8fb781f commit 2eb9114
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 50 deletions.
66 changes: 44 additions & 22 deletions src/npc_shields/injections.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,34 +21,33 @@ class Injection:
>>> i = Injection(
... shield=npc_shields.shields.DR2002,
... location="A1",
... location="D1",
... target_structure="VISp",
... hemisphere="left",
... depth_um=3000,
... depth_um=200,
... substance="Fluorogold",
... manufacturer="Sigma",
... identifier="12345",
... total_volume_ul=1.0,
... total_volume_nl=1.0,
... concentration_mg_ml=10.0,
... flow_rate_ul_s=0.1,
... flow_rate_nl_s=0.1,
... start_time=datetime.datetime(2023, 1, 1, 12, 0),
... fluorescence_nm=500,
... number_of_injections=3,
... notes="This was a test injection",
... is_control=False,
... is_anaesthetized=True,
... )
"""

shield: npc_shields.types.Shield | None
"""The shield through which the injection was made."""

location: str
"""The hole in the shield through which the injection was made (e.g. 'C3').
- alternatively, a string indicating location of a burr hole or other non-shield location.
"""
target_structure: str
"""The intended brain structure for the injection ('VISp' etc.)."""

hemisphere: Literal['left', 'right']
"""The hemisphere of the brain where the injection was made (e.g. 'left', 'right')."""
"""The hemisphere of the brain where the injection was made ('left' or 'right')."""

depth_um: float
"""Depth of the injection, in microns from brain surface."""
Expand All @@ -62,22 +61,39 @@ class Injection:
identifier: str | None
"""Identifier of the injected substance (e.g. manufacture serial number)."""

total_volume_ul: float
"""Total volume injected, in microliters."""
total_volume_nl: float
"""Total volume injected, in nanoliters."""

concentration_mg_ml: float | None
"""Concentration of the injected substance in milligrams per milliliter."""

flow_rate_ul_s: float
"""Flow rate of the injection in microliters per second."""
flow_rate_nl_s: float
"""Flow rate of the injection in nanoliters per second."""

start_time: datetime.datetime
"""Time of the first injection, as a datetime object."""

is_anaesthetized: bool
"""Whether the subject was anaesthetized during the injection."""

# args with defaults ----------------------------------------------- #

location: str | None = None
"""The hole in the shield through which the injection was made (e.g. 'C3').
- alternatively, a string indicating location of a burr hole or other non-shield location.
"""

location_ap: float | None = None
"""Distance in millimeters from bregma to injection site along
anterior-posterior axis (+ve is anterior)."""

location_ml: float | None = None
"""Distance in millimeters from brain midline to injection site along
medial-lateral axis."""

fluorescence_nm: float | None = None
"""Wavelength of fluorescence for the injection."""
"""Emission wavelength of the substance injected, if it fluoresces."""

number_of_injections: int = 1
"""Number of individual injections made at this site + depth."""
Expand All @@ -88,37 +104,43 @@ class Injection:
notes: str | None = None
"""Text notes for the injection."""


def to_json(self) -> dict[str, Any]:
data = dataclasses.asdict(self)
if self.shield is not None:
data['shield'] = self.shield.to_json()
return data

@dataclasses.dataclass
class InjectionRecord:
"""A record of a set of injections.
>>> i = Injection(
... shield=npc_shields.shields.DR2002,
... location="A1",
... target_structure="VISp",
... hemisphere="left",
... depth_um=3000,
... substance="Fluorogold",
... manufacturer="Sigma",
... identifier="12345",
... total_volume_ul=1.0,
... total_volume_nl=1.0,
... concentration_mg_ml=10.0,
... flow_rate_ul_s=0.1,
... flow_rate_nl_s=0.1,
... start_time=datetime.datetime(2023, 1, 1, 12, 0),
... fluorescence_nm=500,
... number_of_injections=3,
... notes="This was a test injection",
... is_control=False,
... is_anaesthetized=False,
... )
>>> r = InjectionRecord(
... injections=[i],
... session="366122_20240101",
... experiment_day=1,
... )
>>> r.to_json()
{'injections': [{'shield': {'name': '2002', 'drawing_id': '0283-200-002', 'labels': ('A1', 'A2', 'A3', 'B1', 'B2', 'B3', 'B4', 'C1', 'C2', 'C3', 'C4', 'D1', 'D2', 'D3', 'E1', 'E2', 'E3', 'E4', 'F1', 'F2', 'F3'), 'svg': WindowsPath('C:/Users/ben.hardcastle/github/np_probe_targets/src/npc_shields/drawings/2002.svg')}, 'location': 'A1', 'hemisphere': 'left', 'depth_um': 3000, 'substance': 'Fluorogold', 'manufacturer': 'Sigma', 'identifier': '12345', 'total_volume_ul': 1.0, 'concentration_mg_ml': 10.0, 'flow_rate_ul_s': 0.1, 'start_time': datetime.datetime(2023, 1, 1, 12, 0), 'fluorescence_nm': 500, 'number_of_injections': 3, 'is_control': False, 'notes': 'This was a test injection'}], 'session': '366122_20240101', 'experiment_day': 1}
{'injections': [{'shield': {'name': '2002', 'drawing_id': '0283-200-002'}, 'target_structure': 'VISp', 'hemisphere': 'left', 'depth_um': 3000, 'substance': 'Fluorogold', 'manufacturer': 'Sigma', 'identifier': '12345', 'total_volume_nl': 1.0, 'concentration_mg_ml': 10.0, 'flow_rate_nl_s': 0.1, 'start_time': datetime.datetime(2023, 1, 1, 12, 0), 'is_anaesthetized': False, 'location': None, 'location_ap': None, 'location_ml': None, 'fluorescence_nm': 500, 'number_of_injections': 3, 'is_control': False, 'notes': 'This was a test injection'}], 'session': '366122_20240101', 'experiment_day': 1}
"""

injections: Sequence[Injection]
"""A record of each injection made."""

Expand All @@ -132,7 +154,7 @@ class InjectionRecord:
def to_json(self)-> dict[str, Any]:
"""Get a JSON-serializable representation of the injections."""
return {
'injections': [dataclasses.asdict(injection) for injection in self.injections],
'injections': [injection.to_json() for injection in self.injections],
'session': self.session,
'experiment_day': self.experiment_day,
}
Expand Down
83 changes: 55 additions & 28 deletions src/npc_shields/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@

from __future__ import annotations

import dataclasses
import datetime
import pathlib
import typing
from collections.abc import Iterable, MutableMapping
from typing import Any, Literal, Protocol, Sequence, Union
from collections.abc import Iterable, MutableMapping, Sequence
from typing import Any, Literal, Protocol, Union

import npc_session
from typing_extensions import TypeAlias
Expand Down Expand Up @@ -47,6 +46,9 @@ def svg(self) -> pathlib.Path:
def __hash__(self) -> int:
...

def to_json(self) -> dict[str, Any]:
"""Get a JSON-serializable representation of the shield."""
...

class Insertion(Protocol):
"""A set of probes inserted (or planned to be inserted) into a shield."""
Expand Down Expand Up @@ -90,7 +92,7 @@ def experiment_day(self) -> int:

class Injection(Protocol):
"""An injection through a hole in a shield at a particular brain location (site + depth).
- should allow for no shield (e.g. burr hole)
- should record hemisphere
- may consist of multiple individual injections
Expand All @@ -104,75 +106,101 @@ def shield(self) -> Shield | None:
@property
def location(self) -> str:
"""The hole in the shield through which the injection was made (e.g. 'C3').
- alternatively, a string indicating location of a burr hole or other non-shield location.
"""
...

@property

@property
def location_ap(self) -> float | None:
"""Distance in millimeters from bregma to injection site along
anterior-posterior axis (+ve is anterior)."""
...

@property
def location_ml(self) -> float | None:
"""Distance in millimeters from brain midline to injection site along
medial-lateral axis."""
...

@property
def target_structure(self) -> str:
"""The intended brain structure for the injection ('VISp' etc.)."""
...

@property
def hemisphere(self) -> Literal['left', 'right']:
"""The hemisphere of the brain where the injection was made (e.g. 'left', 'right')."""
...

@property
def depth_um(self) -> float:
"""Depth of the injection, in microns from brain surface."""
...

@property
def fluorescence_nm(self) -> float | None:
"""Wavelength of fluorescence for the injection."""
...

@property
def manufacturer(self) -> str | None:
"""Manufacturer of the injected substance."""
...
@property

@property
def substance(self) -> str:
"""Name of the injected substance."""
...
@property

@property
def identifier(self) -> str | None:
"""Identifier of the injected substance (e.g. manufacture serial number)."""
...

@property
def concentration_mg_ml(self) -> float | None:
"""Concentration of the injected substance in milligrams per milliliter."""
...

@property
def flow_rate_ul_s(self) -> float:
"""Flow rate of the injection in microliters per second."""
def flow_rate_nl_s(self) -> float:
"""Flow rate of the injection in nanoliters per second."""
...

@property
def number_of_injections(self) -> int:
"""Number of individual injections made at this site + depth."""
...

@property
def total_volume_ul(self) -> float:
"""Total volume injected, in microliters."""
def total_volume_nl(self) -> float:
"""Total volume injected, in nanoliters."""
...

@property
def start_time(self) -> datetime.datetime:
"""Time of the first injection, as a datetime object."""
...


@property
def is_anaesthetized(self) -> bool:
"""Whether the subject was anaesthetized during the injection."""
...

@property
def is_control(self) -> bool:
"""Whether the purpose of the injection was a control."""
...

@property
def notes(self) -> dict[str | npc_session.ProbeRecord, str | None]:
def notes(self) -> str | None:
"""Text notes for the injection."""
...

def to_json(self) -> dict[str, Any]:
"""Get a JSON-serializable representation of the injection."""
...


class InjectionRecord(Protocol):
Expand All @@ -194,6 +222,5 @@ def experiment_day(self) -> int:
...

def to_json(self) -> dict[str, Any]:
"""Get a JSON-serializable representation of the injections."""
"""Get a JSON-serializable representation of the injections in a session."""
...

0 comments on commit 2eb9114

Please sign in to comment.