From a6fa9170f2ad6942d74eaf44c22cedf0b80f0fcd Mon Sep 17 00:00:00 2001 From: Ryan Ly Date: Thu, 25 May 2023 13:56:30 -0700 Subject: [PATCH] Add CI to read first NWB file from each dandiset (#1695) --- .github/workflows/run_dandi_read_tests.yml | 50 +++++++++++++++++++++ CHANGELOG.md | 1 + tests/read_dandi/__init__.py | 0 tests/read_dandi/test_read_dandi.py | 52 ++++++++++++++++++++++ 4 files changed, 103 insertions(+) create mode 100644 .github/workflows/run_dandi_read_tests.yml create mode 100644 tests/read_dandi/__init__.py create mode 100644 tests/read_dandi/test_read_dandi.py diff --git a/.github/workflows/run_dandi_read_tests.yml b/.github/workflows/run_dandi_read_tests.yml new file mode 100644 index 000000000..ec8cc2e84 --- /dev/null +++ b/.github/workflows/run_dandi_read_tests.yml @@ -0,0 +1,50 @@ +name: Run DANDI read tests +on: + schedule: + - cron: '0 6 * * *' # once per day at 1am ET + workflow_dispatch: + +jobs: + run-tests: + runs-on: ubuntu-latest + defaults: + run: + shell: bash -l {0} # necessary for conda + steps: + - name: Cancel non-latest runs + uses: styfle/cancel-workflow-action@0.11.0 + with: + all_but_latest: true + access_token: ${{ github.token }} + + - uses: actions/checkout@v3 + with: + submodules: 'recursive' + fetch-depth: 0 # tags are required for versioneer to determine the version + + - name: Set up Conda + uses: conda-incubator/setup-miniconda@v2 + with: + auto-update-conda: true + activate-environment: ros3 + environment-file: environment-ros3.yml + python-version: "3.11" + channels: conda-forge + auto-activate-base: false + + - name: Install run dependencies + run: | + python -m pip install dandi pytest + python -m pip uninstall -y pynwb # uninstall pynwb + python -m pip install -e . + python -m pip list + + - name: Conda reporting + run: | + conda info + conda config --show-sources + conda list --show-channel-urls + + - name: Run DANDI read tests + run: | + pytest -rP tests/read_dandi/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 542c9c6b0..24cd0985e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Enhancements and minor changes - Add testing support for Python 3.11. @rly [#1687](https://github.com/NeurodataWithoutBorders/pynwb/pull/1687) +- Add CI testing of NWB files on DANDI. @rly [#1695](https://github.com/NeurodataWithoutBorders/pynwb/pull/1695) ### Bug fixes - Remove unused, deprecated `codecov` package from dev installation requirements. @rly diff --git a/tests/read_dandi/__init__.py b/tests/read_dandi/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/read_dandi/test_read_dandi.py b/tests/read_dandi/test_read_dandi.py new file mode 100644 index 000000000..84e9f3f62 --- /dev/null +++ b/tests/read_dandi/test_read_dandi.py @@ -0,0 +1,52 @@ +from dandi.dandiapi import DandiAPIClient +import sys +import traceback + +from pynwb import NWBHDF5IO +from pynwb.testing import TestCase + + +class TestReadNWBDandisets(TestCase): + """Test reading NWB files from the DANDI Archive using ROS3.""" + + def test_read_first_nwb_asset(self): + """Test reading the first NWB asset from each dandiset that uses NWB.""" + client = DandiAPIClient() + dandisets = client.get_dandisets() + + failed_reads = dict() + for i, dandiset in enumerate(dandisets): + dandiset_metadata = dandiset.get_raw_metadata() + + # skip any dandisets that do not use NWB + if not any( + data_standard["identifier"] == "RRID:SCR_015242" # this is the RRID for NWB + for data_standard in dandiset_metadata["assetsSummary"].get("dataStandard", []) + ): + continue + + dandiset_identifier = dandiset_metadata["identifier"] + print("--------------") + print(f"{i}: {dandiset_identifier}") + + # iterate through assets until we get an NWB file (it could be MP4) + assets = dandiset.get_assets() + first_asset = next(assets) + while first_asset.path.split(".")[-1] != "nwb": + first_asset = next(assets) + if first_asset.path.split(".")[-1] != "nwb": + print("No NWB files?!") + continue + + s3_url = first_asset.get_content_url(follow_redirects=1, strip_query=True) + + try: + with NWBHDF5IO(path=s3_url, load_namespaces=True, driver="ros3") as io: + io.read() + except Exception as e: + print(traceback.format_exc()) + failed_reads[dandiset] = e + + if failed_reads: + print(failed_reads) + sys.exit(1)