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

Enh/attachments #20

Merged
merged 32 commits into from
Jul 18, 2024
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
2b69f6f
add support for attachments
mochic Jul 11, 2024
154b39c
add support for attachments
mochic Jul 11, 2024
5e96ecd
docs, whitespace
mochic Jul 11, 2024
5a3d60e
push for sharing
mochic Jul 12, 2024
eca7767
add conditional slim_api for reserialization testing? or maybe unnece…
mochic Jul 12, 2024
54cf4c8
revert change
mochic Jul 12, 2024
a5fd7ac
add model alias fetching
mochic Jul 12, 2024
3a918c1
only return validated data
mochic Jul 12, 2024
59edb41
use alias for sort key
mochic Jul 12, 2024
760ca39
whitespace
mochic Jul 12, 2024
ae4aec7
new attachments iteration
mochic Jul 15, 2024
a5eff9d
new attachments implementation
mochic Jul 16, 2024
680ff25
doctest example for model
mochic Jul 16, 2024
ac0cd43
new core
mochic Jul 16, 2024
51ce1d2
use doctests, remove fetches
mochic Jul 16, 2024
21183bd
coverage
mochic Jul 17, 2024
d48a21a
documentation
mochic Jul 17, 2024
32492de
update docs
mochic Jul 17, 2024
8a76f0a
formatting
mochic Jul 17, 2024
7ef9343
add usage example
mochic Jul 17, 2024
081908b
rework models
mochic Jul 17, 2024
5b8d277
update example usage
mochic Jul 17, 2024
c353219
add static methods
mochic Jul 18, 2024
256eb98
update class prop type
mochic Jul 18, 2024
d5e6d27
remove doctests
mochic Jul 18, 2024
d621742
raise exception if fetch_model finds no records
mochic Jul 18, 2024
af8b626
change slims tables type name
mochic Jul 18, 2024
3a9b844
formatting
mochic Jul 18, 2024
75c8223
tests: mocked log info and fixed type warning
jtyoung84 Jul 18, 2024
e592f7f
Merge pull request #1 from AllenNeuralDynamics/enh/general-improvemen…
mochic Jul 18, 2024
c52e481
fix example
mochic Jul 18, 2024
cfc8d8a
Merge remote-tracking branch 'mochic/enh/general-improvements' into e…
mochic Jul 18, 2024
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
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,13 @@ To develop the code, run
pip install -e .[dev]
```

## Usage

### VScode
![](example-usage.gif)

Requires the pylance extension to be installed.

## Contributing

### Linters and testing
Expand Down
Binary file added example-usage.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
25 changes: 25 additions & 0 deletions src/aind_slims_api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,28 @@
config = AindSlimsApiSettings()

from aind_slims_api.core import SlimsClient # noqa


def testmod(**testmod_kwargs):
jtyoung84 marked this conversation as resolved.
Show resolved Hide resolved
"""
Run doctests for the module, configured to ignore exception details and
normalize whitespace. Also sets logging level to DEBUG.

Accepts kwargs to pass to doctest.testmod().

Add to modules to run doctests when run as a script:
.. code-block:: text
if __name__ == "__main__":
from npc_io import testmod
testmod()

"""
import doctest
import logging

logging.basicConfig(level=logging.DEBUG)

_ = testmod_kwargs.setdefault(
"optionflags", doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS
)
doctest.testmod(**testmod_kwargs)
105 changes: 38 additions & 67 deletions src/aind_slims_api/behavior_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,39 @@
"""

import logging
from typing import Any
from datetime import datetime
from typing import ClassVar

from pydantic import Field

from aind_slims_api.core import SlimsBaseModel, SlimsClient, SLIMSTABLES
from aind_slims_api.core import SlimsBaseModel, SlimsClient
from aind_slims_api.instrument import SlimsInstrument
from aind_slims_api.mouse import SlimsMouseContent
from aind_slims_api.user import SlimsUser

logger = logging.getLogger()


class SlimsBehaviorSessionContentEvent(SlimsBaseModel):
"""Model for an instance of the Behavior Session ContentEvent"""
"""Model for an instance of the Behavior Session ContentEvent

Examples
--------
>>> from aind_slims_api.core import SlimsClient
>>> from aind_slims_api.mouse import SlimsMouseContent
>>> client = SlimsClient()
>>> mouse = client.fetch_model(SlimsMouseContent, barcode="00000000")
>>> behavior_sessions = client.fetch_models(SlimsBehaviorSessionContentEvent,
... mouse_pk=mouse.pk, sort=["date"])
"""

pk: int | None = Field(default=None, alias="cnvn_pk")
mouse_pk: int | None = Field(
default=None, alias="cnvn_fk_content"
default=None,
alias="cnvn_fk_content",
description=(
"The primary key of the mouse associated with this behavior session."
),
) # used as reference to mouse
notes: str | None = Field(default=None, alias="cnvn_cf_notes")
task_stage: str | None = Field(default=None, alias="cnvn_cf_taskStage")
Expand All @@ -32,67 +49,19 @@ class SlimsBehaviorSessionContentEvent(SlimsBaseModel):
default=None, alias="cnvn_cf_taskSchemaVersion"
)
software_version: str | None = Field(default=None, alias="cnvn_cf_softwareVersion")
date: datetime | None = Field(..., alias="cnvn_cf_scheduledDate")

date: datetime | None = Field(default=None, alias="cnvn_cf_scheduledDate")
cnvn_fk_contentEventType: int = 10 # pk of Behavior Session ContentEvent

_slims_table: SLIMSTABLES = "ContentEvent"


SlimsSingletonFetchReturn = SlimsBaseModel | dict[str, Any] | None


def _resolve_pk(
model: SlimsSingletonFetchReturn,
primary_key_name: str = "pk",
) -> int:
"""Utility function shared across read/write

Notes
-----
- TODO: Change return type of fetch_mouse_content to match pattern in
fetch_behavior_session_content_events, or the other way around?
- TODO: Move to core to have better centralized control of when references
are resolved
"""
if isinstance(model, dict):
logger.warning("Extracting primary key from unvalidated dict.")
return model[primary_key_name]
elif isinstance(model, SlimsBaseModel):
return getattr(model, primary_key_name)
elif model is None:
raise ValueError(f"Cannot resolve primary key from {model}")
else:
raise ValueError("Unexpected type for model: %s" % type(model))


def fetch_behavior_session_content_events(
client: SlimsClient,
mouse: SlimsSingletonFetchReturn,
) -> tuple[list[SlimsBehaviorSessionContentEvent], list[dict[str, Any]]]:
"""Fetches behavior sessions for a mouse with labtracks id {mouse_name}

Returns
-------
tuple:
list:
Validated SlimsBehaviorSessionContentEvent objects
list:
Dictionaries representations of objects that failed validation
"""
return client.fetch_models(
SlimsBehaviorSessionContentEvent,
cnvn_fk_content=_resolve_pk(mouse),
cnvt_name="Behavior Session",
sort=["cnvn_cf_scheduledDate"],
)
_slims_table = "ContentEvent"
_base_fetch_filters: ClassVar[dict[str, str]] = {
"cnvt_name": "Behavior Session",
}


def write_behavior_session_content_events(
client: SlimsClient,
mouse: SlimsSingletonFetchReturn,
instrument: SlimsSingletonFetchReturn,
trainers: list[SlimsSingletonFetchReturn],
mouse: SlimsMouseContent,
instrument: SlimsInstrument,
trainers: list[SlimsUser],
*behavior_sessions: SlimsBehaviorSessionContentEvent,
) -> list[SlimsBehaviorSessionContentEvent]:
"""Writes behavior sessions for a mouse with labtracks id {mouse_name}
Expand All @@ -102,22 +71,24 @@ def write_behavior_session_content_events(
- All supplied `behavior_sessions` will have their `mouse_name` field set
to the value supplied as `mouse_name` to this function
"""
mouse_pk = _resolve_pk(mouse)
logger.debug(f"Mouse pk: {mouse_pk}")
instrument_pk = _resolve_pk(instrument)
logger.debug(f"Instrument pk: {instrument_pk}")
trainer_pks = [_resolve_pk(trainer) for trainer in trainers]
trainer_pks = [trainer.pk for trainer in trainers]
logger.debug(f"Trainer pks: {trainer_pks}")
added = []
for behavior_session in behavior_sessions:
updated = behavior_session.model_copy(
update={
"mouse_pk": mouse_pk,
"instrument": instrument_pk,
"mouse_pk": mouse.pk,
"instrument": instrument.pk,
"trainers": trainer_pks,
},
)
logger.debug(f"Resolved behavior session: {updated}")
added.append(client.add_model(updated))

return added


if __name__ == "__main__":
from aind_slims_api import testmod

testmod()
2 changes: 1 addition & 1 deletion src/aind_slims_api/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ class AindSlimsApiSettings(BaseSettings):

slims_url: str = "https://aind-test.us.slims.agilent.com/slimsrest/"
slims_username: str = ""
slims_password: SecretStr = ""
slims_password: SecretStr = SecretStr("")
Loading
Loading