Skip to content

Commit 531769a

Browse files
Merge pull request #315 from LucaMarconato/contribution-guide
Contribution guide
2 parents d2fe0bc + 595eea7 commit 531769a

8 files changed

Lines changed: 309 additions & 15 deletions

File tree

.github/workflows/release.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
- name: Set up Python 3.12
1414
uses: actions/setup-python@v5
1515
with:
16-
python-version: "3.12"
16+
python-version: "3.13"
1717
cache: pip
1818
- name: Install build dependencies
1919
run: python -m pip install --upgrade pip wheel twine build

.github/workflows/test.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
strategy:
1919
fail-fast: false
2020
matrix:
21-
python: ["3.10", "3.12"]
21+
python: ["3.11", "3.12", "3.13"]
2222
os: [ubuntu-latest]
2323

2424
env:
@@ -52,7 +52,7 @@ jobs:
5252
pip install --pre -e ".[dev,test]"
5353
5454
- name: Download artifact of test data
55-
if: matrix.python == '3.12'
55+
if: matrix.python == '3.13'
5656
uses: dawidd6/action-download-artifact@v9
5757
with:
5858
workflow: prepare_test_data.yaml

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
[badge-pypi]: https://badge.fury.io/py/spatialdata-io.svg
1515
[link-pypi]: https://pypi.org/project/spatialdata-io/
1616

17+
_We encourage contributions from the community and from developers of spatial technologies. Please see the "How to Contribute" section below._
18+
1719
This package contains reader functions to load common spatial omics formats into SpatialData. Currently, we provide support for:
1820

1921
- 10x Genomics Visium®
@@ -32,6 +34,8 @@ This package contains reader functions to load common spatial omics formats into
3234

3335
Note: all mentioned technologies are registered trademarks of their respective companies.
3436

37+
Please refer to the list of [open Pull Requests](https://github.com/scverse/spatialdata-io/pulls?q=sort%3Aupdated-desc+is%3Apr+is%3Aopen) for readers that are currently being developed.
38+
3539
## Known limitations
3640

3741
Contributions for addressing the below limitations are very welcomed.
@@ -42,9 +46,7 @@ Contributions for addressing the below limitations are very welcomed.
4246

4347
1. **Open a GitHub Issue**: Start by opening a new issue or commenting on an existing one in the repository. Clearly describe the problem and your proposed changes to avoid overlapping efforts with others.
4448

45-
2. **Submit a Pull Request (PR)**: Once the issue is discussed, submit a PR to the `spatialdata-io` repository. Ensure your PR includes information about a suitable dataset for testing the reader, ideally no larger than 10 GB. Include clear instructions for accessing the data, preferably with a `curl` or `wget` command for easy downloading.
46-
47-
3. **Optional Enhancements**: To facilitate reproducibility and ease of data access, consider adding a folder in the [spatialdata-sandbox](https://github.com/giovp/spatialdata-sandbox) repository. Include a `download.py` and `to_zarr.py` script (refer to examples in the repository) to enable others to reproduce your reader by simply running these scripts sequentially.
49+
2. **Submit a Pull Request (PR)**: Once the issue is discussed, submit a PR to the `spatialdata-io` repository. If you are contributing a new reader, or extending the reader for a new versions of a technologies, please consult our [contribution guide](https://spatialdata.scverse.org/projects/io/en/latest/contributing.html), which describes the steps to ensure that the pull request can be tested on suitable example data and reviewed efficiently.
4850

4951
## Getting started
5052

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
info = metadata("spatialdata-io")
2424
project_name = info["Name"]
2525
author = info["Author"]
26-
copyright = f"{datetime.now():%Y}, {author}."
26+
copyright = f"{datetime.now():%Y}, {author}"
2727
version = info["Version"]
2828
repository_url = f"https://github.com/scverse/{project_name}"
2929

docs/contributing.md

Lines changed: 226 additions & 2 deletions
Large diffs are not rendered by default.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ dynamic= [
1010
]
1111
description = "SpatialData IO for common techs"
1212
readme = "README.md"
13-
requires-python = ">=3.10"
13+
requires-python = ">=3.11"
1414
license = {file = "LICENSE"}
1515
authors = [
1616
{name = "scverse"},

tests/_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,6 @@ def skip_if_below_python_version() -> pytest.mark.skipif:
2525
>>> def test_some_feature():
2626
>>> assert True
2727
"""
28-
MIN_VERSION = (3, 12)
28+
MIN_VERSION = (3, 13)
2929
reason = f"Test requires Python {'.'.join(map(str, MIN_VERSION))} or higher"
3030
return pytest.mark.skipif(sys.version_info < MIN_VERSION, reason=reason)

tests/test_xenium.py

Lines changed: 72 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
import numpy as np
66
import pytest
77
from click.testing import CliRunner
8-
from spatialdata import read_zarr
8+
from spatialdata import match_table_to_element, read_zarr
9+
from spatialdata.models import TableModel, get_table_keys
910

1011
from spatialdata_io.__main__ import xenium_wrapper
1112
from spatialdata_io.readers.xenium import (
@@ -51,11 +52,17 @@ def test_roundtrip_with_data_limits() -> None:
5152
@pytest.mark.parametrize(
5253
"dataset,expected",
5354
[
54-
("Xenium_V1_human_Breast_2fov_outs", "{'y': (0, 3529), 'x': (0, 5792), 'z': (10, 25)}"),
55-
("Xenium_V1_human_Lung_2fov_outs", "{'y': (0, 3553), 'x': (0, 5793), 'z': (7, 32)}"),
55+
(
56+
"Xenium_V1_human_Breast_2fov_outs",
57+
"{'y': (0, 3529), 'x': (0, 5792), 'z': (10, 25)}",
58+
),
59+
(
60+
"Xenium_V1_human_Lung_2fov_outs",
61+
"{'y': (0, 3553), 'x': (0, 5793), 'z': (7, 32)}",
62+
),
5663
],
5764
)
58-
def test_example_data(dataset: str, expected: str) -> None:
65+
def test_example_data_data_extent(dataset: str, expected: str) -> None:
5966
f = Path("./data") / dataset
6067
assert f.is_dir()
6168
sdata = xenium(f, cells_as_circles=False)
@@ -66,6 +73,67 @@ def test_example_data(dataset: str, expected: str) -> None:
6673
assert str(extent) == expected
6774

6875

76+
# TODO: add tests for Xenium 3.0.0
77+
@skip_if_below_python_version()
78+
@pytest.mark.parametrize("dataset", ["Xenium_V1_human_Breast_2fov_outs", "Xenium_V1_human_Lung_2fov_outs"])
79+
def test_example_data_index_integrity(dataset: str) -> None:
80+
f = Path("./data") / dataset
81+
assert f.is_dir()
82+
sdata = xenium(f, cells_as_circles=False)
83+
84+
if dataset == "Xenium_V1_human_Breast_2fov_outs":
85+
# fmt: off
86+
# test elements
87+
assert sdata["morphology_focus"]["scale0"]["image"].sel(c="DAPI", y=20.5, x=20.5).data.compute() == 94
88+
assert sdata["morphology_focus"]["scale0"]["image"].sel(c="AlphaSMA/Vimentin", y=3528.5, x=5775.5).data.compute() == 1
89+
assert sdata["cell_labels"]["scale0"]["image"].sel(y=73.5, x=33.5).data.compute() == 4088
90+
assert sdata["cell_labels"]["scale0"]["image"].sel(y=76.5, x=33.5).data.compute() == 4081
91+
assert sdata["nucleus_labels"]["scale0"]["image"].sel(y=11.5, x=1687.5).data.compute() == 5030
92+
assert sdata["nucleus_labels"]["scale0"]["image"].sel(y=3515.5, x=4618.5).data.compute() == 6392
93+
assert np.allclose(sdata['transcripts'].compute().loc[[0, 10000, 1113949]]['x'], [2.608911, 194.917831, 1227.499268])
94+
assert np.isclose(sdata['cell_boundaries'].loc['oipggjko-1'].geometry.centroid.x,736.4864931162789)
95+
assert np.isclose(sdata['nucleus_boundaries'].loc['oipggjko-1'].geometry.centroid.x,736.4931256878282)
96+
assert np.array_equal(sdata['table'].X.indices[:3], [1, 3, 34])
97+
# fmt: on
98+
99+
# test table annotation
100+
region, region_key, instance_key = get_table_keys(sdata["table"])
101+
assert region == "cell_labels"
102+
matched_table = match_table_to_element(sdata, element_name=region, table_name="table")
103+
assert len(matched_table) == 7275
104+
assert matched_table.obs["cell_id"][:3].tolist() == [
105+
"aaaiikim-1",
106+
"aaaljapa-1",
107+
"aabhbgmg-1",
108+
]
109+
else:
110+
assert dataset == "Xenium_V1_human_Lung_2fov_outs"
111+
# fmt: off
112+
# test elements
113+
assert sdata["morphology_focus"]["scale0"]["image"].sel(c="DAPI", y=0.5, x=2215.5).data.compute() == 1
114+
assert sdata["morphology_focus"]["scale0"]["image"].sel(c="DAPI", y=11.5, x=4437.5).data.compute() == 2007
115+
assert sdata["cell_labels"]["scale0"]["image"].sel(y=0.5, x=2940.5).data.compute() == 2605
116+
assert sdata["cell_labels"]["scale0"]["image"].sel(y=3.5, x=4801.5).data.compute() == 7618
117+
assert sdata["nucleus_labels"]["scale0"]["image"].sel(y=8.5, x=4359.5).data.compute() == 7000
118+
assert sdata["nucleus_labels"]["scale0"]["image"].sel(y=18.5, x=3015.5).data.compute() == 2764
119+
assert np.allclose(sdata['transcripts'].compute().loc[[0, 10000, 20000]]['x'], [174.258392, 12.210024, 214.759186])
120+
assert np.isclose(sdata['cell_boundaries'].loc['aaanbaof-1'].geometry.centroid.x, 43.96894317275074)
121+
assert np.isclose(sdata['nucleus_boundaries'].loc['aaanbaof-1'].geometry.centroid.x,43.31874577809517)
122+
assert np.array_equal(sdata['table'].X.indices[:3], [1, 8, 19])
123+
# fmt: on
124+
125+
# test table annotation
126+
region, region_key, instance_key = get_table_keys(sdata["table"])
127+
assert region == "cell_labels"
128+
matched_table = match_table_to_element(sdata, element_name=region, table_name="table")
129+
assert len(matched_table) == 11898
130+
assert matched_table.obs["cell_id"][:3].tolist() == [
131+
"aaafiiei-1",
132+
"aaanbaof-1",
133+
"aabdiein-1",
134+
]
135+
136+
69137
# TODO: add tests for Xenium 3.0.0
70138
@skip_if_below_python_version()
71139
@pytest.mark.parametrize("dataset", ["Xenium_V1_human_Breast_2fov_outs", "Xenium_V1_human_Lung_2fov_outs"])

0 commit comments

Comments
 (0)