Skip to content

Commit 29155d0

Browse files
authored
Merge pull request #405 from AllenNeuralDynamics/release-v0.23.0
Release v0.23.0
2 parents 736524f + 85fb9d4 commit 29155d0

File tree

25 files changed

+2036
-25
lines changed

25 files changed

+2036
-25
lines changed

.pre-commit-config.yaml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
repos:
2+
- repo: https://github.com/pre-commit/pre-commit-hooks
3+
rev: v4.5.0
4+
hooks:
5+
- id: trailing-whitespace
6+
- id: end-of-file-fixer
7+
- id: check-yaml
8+
- id: check-added-large-files
9+
- repo: https://github.com/pycqa/flake8
10+
rev: 7.0.0
11+
hooks:
12+
- id: flake8
13+
- repo: https://github.com/psf/black
14+
rev: 24.2.0
15+
hooks:
16+
- id: black
17+
- repo: https://github.com/pycqa/isort
18+
rev: 5.13.2
19+
hooks:
20+
- id: isort

CODE_OF_CONDUCT.md

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
2+
# Contributor Covenant Code of Conduct
3+
4+
## Our Pledge
5+
6+
We as members, contributors, and leaders pledge to make participation in our
7+
community a harassment-free experience for everyone, regardless of age, body
8+
size, visible or invisible disability, ethnicity, sex characteristics, gender
9+
identity and expression, level of experience, education, socio-economic status,
10+
nationality, personal appearance, race, religion, or sexual identity
11+
and orientation.
12+
13+
We pledge to act and interact in ways that contribute to an open, welcoming,
14+
diverse, inclusive, and healthy community.
15+
16+
## Our Standards
17+
18+
Examples of behavior that contributes to a positive environment for our
19+
community include:
20+
21+
* Demonstrating empathy and kindness toward other people
22+
* Being respectful of differing opinions, viewpoints, and experiences
23+
* Giving and gracefully accepting constructive feedback
24+
* Accepting responsibility and apologizing to those affected by our mistakes,
25+
and learning from the experience
26+
* Focusing on what is best not just for us as individuals, but for the
27+
overall community
28+
29+
Examples of unacceptable behavior include:
30+
31+
* The use of sexualized language or imagery, and sexual attention or
32+
advances of any kind
33+
* Trolling, insulting or derogatory comments, and personal or political attacks
34+
* Public or private harassment
35+
* Publishing others' private information, such as a physical or email
36+
address, without their explicit permission
37+
* Other conduct which could reasonably be considered inappropriate in a
38+
professional setting
39+
40+
## Enforcement Responsibilities
41+
42+
Community leaders are responsible for clarifying and enforcing our standards of
43+
acceptable behavior and will take appropriate and fair corrective action in
44+
response to any behavior that they deem inappropriate, threatening, offensive,
45+
or harmful.
46+
47+
Community leaders have the right and responsibility to remove, edit, or reject
48+
comments, commits, code, wiki edits, issues, and other contributions that are
49+
not aligned to this Code of Conduct, and will communicate reasons for moderation
50+
decisions when appropriate.
51+
52+
## Scope
53+
54+
This Code of Conduct applies within all community spaces, and also applies when
55+
an individual is officially representing the community in public spaces.
56+
Examples of representing our community include using an official email address,
57+
posting via an official social media account, or acting as an appointed
58+
representative at an online or offline event.
59+
60+
## Enforcement
61+
62+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
63+
reported to the community leaders responsible for enforcement at
64+
ScientificComputing@AllenInstitute.onmicrosoft.com.
65+
All complaints will be reviewed and investigated promptly and fairly.
66+
67+
All community leaders are obligated to respect the privacy and security of the
68+
reporter of any incident.
69+
70+
## Enforcement Guidelines
71+
72+
Community leaders will follow these Community Impact Guidelines in determining
73+
the consequences for any action they deem in violation of this Code of Conduct:
74+
75+
### 1. Correction
76+
77+
**Community Impact**: Use of inappropriate language or other behavior deemed
78+
unprofessional or unwelcome in the community.
79+
80+
**Consequence**: A private, written warning from community leaders, providing
81+
clarity around the nature of the violation and an explanation of why the
82+
behavior was inappropriate. A public apology may be requested.
83+
84+
### 2. Warning
85+
86+
**Community Impact**: A violation through a single incident or series
87+
of actions.
88+
89+
**Consequence**: A warning with consequences for continued behavior. No
90+
interaction with the people involved, including unsolicited interaction with
91+
those enforcing the Code of Conduct, for a specified period of time. This
92+
includes avoiding interactions in community spaces as well as external channels
93+
like social media. Violating these terms may lead to a temporary or
94+
permanent ban.
95+
96+
### 3. Temporary Ban
97+
98+
**Community Impact**: A serious violation of community standards, including
99+
sustained inappropriate behavior.
100+
101+
**Consequence**: A temporary ban from any sort of interaction or public
102+
communication with the community for a specified period of time. No public or
103+
private interaction with the people involved, including unsolicited interaction
104+
with those enforcing the Code of Conduct, is allowed during this period.
105+
Violating these terms may lead to a permanent ban.
106+
107+
### 4. Permanent Ban
108+
109+
**Community Impact**: Demonstrating a pattern of violation of community
110+
standards, including sustained inappropriate behavior, harassment of an
111+
individual, or aggression toward or disparagement of classes of individuals.
112+
113+
**Consequence**: A permanent ban from any sort of public interaction within
114+
the community.
115+
116+
## Attribution
117+
118+
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
119+
version 2.0, available at
120+
[https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0].
121+
122+
Community Impact Guidelines were inspired by
123+
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
124+
125+
For answers to common questions about this code of conduct, see the FAQ at
126+
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available
127+
at [https://www.contributor-covenant.org/translations][translations].
128+
129+
[homepage]: https://www.contributor-covenant.org
130+
[v2.0]: https://www.contributor-covenant.org/version/2/0/code_of_conduct.html
131+
[Mozilla CoC]: https://github.com/mozilla/diversity
132+
[FAQ]: https://www.contributor-covenant.org/faq
133+
[translations]: https://www.contributor-covenant.org/translations

README.md

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -130,33 +130,56 @@ docker run -it -p 58350:58350 -p 5000:5000 --env-file=.env aind-metadata-service
130130

131131
There are several libraries used to run linters, check documentation, and run tests.
132132

133-
- Please test your changes using the **coverage** library, which will run the tests and log a coverage report:
133+
The following checks are enforced through GitHub Actions CI:
134+
- **flake8** to check that code is up to standards (no unused imports, etc.)
135+
- **interrogate** to check documentation coverage
136+
- **coverage** for 100% test coverage requirement
134137

135-
```
138+
Checks should ideally be run locally before pushing to github:
139+
140+
Test your changes using the **coverage** library, which will run the tests and log a coverage report:
141+
```bash
136142
coverage run -m unittest discover && coverage report
137143
```
138144

139-
- Use **interrogate** to check that modules, methods, etc. have been documented thoroughly:
140-
141-
```
142-
interrogate .
145+
Use **interrogate** to check that modules, methods, etc. have been documented thoroughly:
146+
```bash
147+
interrogate --verbose .
143148
```
144149

145-
- Use **flake8** to check that code is up to standards (no unused imports, etc.):
146-
```
150+
Use **flake8** to check that code is up to standards (no unused imports, etc.):
151+
```bash
147152
flake8 .
148153
```
149154

150-
- Use **black** to automatically format the code into PEP standards:
151-
```
155+
Additional recommended but optional tools:
156+
157+
Use **black** to automatically format the code into PEP standards:
158+
```bash
152159
black .
153160
```
154161

155-
- Use **isort** to automatically sort import statements:
156-
```
162+
Use **isort** to automatically sort import statements:
163+
```bash
157164
isort .
158165
```
159166

167+
#### Optional: Pre-commit Hooks
168+
169+
To automatically run style checks before each commit (recommended but optional):
170+
171+
```bash
172+
# Install pre-commit (already included in dev dependencies)
173+
pre-commit install
174+
```
175+
176+
The hooks will run automatically on each commit, but can be bypassed with `git commit --no-verify` if needed.
177+
178+
Code style specifications:
179+
- Line length: 79 characters
180+
- Python version: 3.10+
181+
- Style guide: PEP 8 (enforced by flake8)
182+
160183
### Pull requests
161184

162185
For internal members, please create a branch. For external members, please fork the repo and open a pull request from the fork. We'll primarily use [Angular](https://github.com/angular/angular/blob/main/CONTRIBUTING.md#commit) style for commit messages. Roughly, they should follow the pattern:
@@ -178,7 +201,7 @@ where scope (optional) describes the packages affected by the code changes and t
178201
### Documentation
179202
To generate the rst files source files for documentation, run
180203
```
181-
sphinx-apidoc -o doc_template/source/ src
204+
sphinx-apidoc -o doc_template/source/ src
182205
```
183206
Then to create the documentation html files, run
184207
```
@@ -188,7 +211,7 @@ More info on sphinx installation can be found here: https://www.sphinx-doc.org/e
188211

189212
### API Response Codes
190213
There are 6 possible status code responses for aind-metadata-service:
191-
- **200**: successfully retrieved valid data without any problems.
214+
- **200**: successfully retrieved valid data without any problems.
192215
- **406**: successfully retrieved some data, but failed to validate against pydantic models.
193216
- **404**: found no data that matches query.
194217
- **300**: queried the server, but more items were returned than expected.

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ dev = [
3434
'interrogate',
3535
'isort',
3636
'Sphinx',
37+
'pre-commit',
3738
]
3839

3940
server = [

src/aind_metadata_service/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
"""REST service to retrieve metadata from databases."""
22

3-
__version__ = "0.22.0"
3+
__version__ = "0.23.0"

src/aind_metadata_service/server.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,32 @@ async def retrieve_slims_water_restriction(
304304
return response
305305

306306

307+
@app.get("/slims/viral_injections")
308+
async def retrieve_slims_injections(
309+
subject_id: Optional[str] = Query(None, alias="subject_id"),
310+
start_date_gte: Optional[str] = Query(
311+
None,
312+
alias="start_date_gte",
313+
description="Experiment run created on or after. (ISO format)",
314+
),
315+
end_date_lte: Optional[str] = Query(
316+
None,
317+
alias="end_date_lte",
318+
description="Experiment run created on or before. (ISO format)",
319+
),
320+
):
321+
"""
322+
Retrieves Viral Injection data from SLIMS server
323+
"""
324+
response = await run_in_threadpool(
325+
slims_client.get_slims_viral_injection_response,
326+
subject_id=subject_id,
327+
start_date=start_date_gte,
328+
end_date=end_date_lte,
329+
)
330+
return response
331+
332+
307333
@app.get("/subject/{subject_id}")
308334
async def retrieve_subject(subject_id):
309335
"""

src/aind_metadata_service/slims/client.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@
2828
from aind_metadata_service.slims.histology.mapping import SlimsHistologyMapper
2929
from aind_metadata_service.slims.imaging.handler import SlimsImagingHandler
3030
from aind_metadata_service.slims.imaging.mapping import SlimsSpimMapper
31+
from aind_metadata_service.slims.viral_injection.handler import (
32+
SlimsViralInjectionHandler,
33+
)
34+
from aind_metadata_service.slims.viral_injection.mapping import (
35+
SlimsViralInjectionMapper,
36+
)
3137
from aind_metadata_service.slims.water_restriction.handler import (
3238
SlimsWaterRestrictionHandler,
3339
)
@@ -500,3 +506,66 @@ def get_water_restriction_procedures_model_response(
500506
except Exception as e:
501507
logging.exception(e)
502508
return ModelResponse.internal_server_error_response()
509+
510+
def get_slims_viral_injection_response(
511+
self,
512+
subject_id: str,
513+
start_date: Optional[str],
514+
end_date: Optional[str],
515+
) -> JSONResponse:
516+
"""
517+
518+
Parameters
519+
----------
520+
subject_id : str | None
521+
start_date : str | None
522+
Optional ISO Format datetime string
523+
end_date : str | None
524+
Optional ISO Format datetime string
525+
Returns
526+
-------
527+
JSONResponse
528+
"""
529+
if subject_id is not None and subject_id == "":
530+
return ModelResponse.bad_request_error_response(
531+
message="subject_id cannot be an empty string!"
532+
).map_to_json_response()
533+
parsed_start_date = self._parse_date(start_date)
534+
if isinstance(parsed_start_date, ModelResponse):
535+
return parsed_start_date.map_to_json_response()
536+
parsed_end_date = self._parse_date(end_date)
537+
if isinstance(parsed_end_date, ModelResponse):
538+
return parsed_end_date.map_to_json_response()
539+
try:
540+
slims_vm_handler = SlimsViralInjectionHandler(
541+
client=self.client.db
542+
)
543+
slims_vm_data = (
544+
slims_vm_handler.get_viral_injection_info_from_slims(
545+
subject_id=subject_id,
546+
start_date_greater_than_or_equal=parsed_start_date,
547+
end_date_less_than_or_equal=parsed_end_date,
548+
)
549+
)
550+
vm_data = SlimsViralInjectionMapper(
551+
slims_vm_data=slims_vm_data
552+
).map_info_from_slims()
553+
if len(vm_data) == 0:
554+
m = ModelResponse.no_data_found_error_response()
555+
return m.map_to_json_response()
556+
response = JSONResponse(
557+
status_code=StatusCodes.VALID_DATA.value,
558+
content=(
559+
{
560+
"message": "Data from SLIMS",
561+
"data": [
562+
json.loads(m.model_dump_json()) for m in vm_data
563+
],
564+
}
565+
),
566+
)
567+
return response
568+
except Exception as e:
569+
logging.exception(e)
570+
m = ModelResponse.internal_server_error_response()
571+
return m.map_to_json_response()

src/aind_metadata_service/slims/ecephys/mapping.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
Module to handle mapping data from SLIMS
33
"""
44

5+
import html
56
from datetime import datetime
67
from decimal import Decimal
78
from typing import List, Optional
@@ -52,8 +53,8 @@ class StreamModule(BaseModel):
5253
)
5354
def parse_micrometer_unit(cls, v):
5455
"""Parse HTML entity for micrometer"""
55-
if isinstance(v, str) and v.strip() == "μm":
56-
return "um"
56+
if isinstance(v, str):
57+
v = html.unescape(v)
5758
return v
5859

5960

src/aind_metadata_service/slims/histology/handler.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ def _get_specimen_data(
153153
specimen_id = None
154154
subject_id = None
155155
for record in records:
156-
n_subject_id = get_attr_or_none(record, "cntn_cf_parentBarcode")
156+
n_subject_id = get_attr_or_none(record, "cntn_id")
157157
if n_subject_id is not None:
158158
subject_id = n_subject_id
159159
n_specimen_id = get_attr_or_none(record, "cntn_barCode")

0 commit comments

Comments
 (0)