From 82a1520dfb98b88729d5d6fce9093be1c0e54039 Mon Sep 17 00:00:00 2001 From: Mike VanDenburgh Date: Wed, 12 Nov 2025 19:51:06 +0000 Subject: [PATCH 01/13] Add docker volume for pre-commit cache Similar to what we do for `uv`, this will persist pre-commit hooks and make it so they don't need to be re-initialized everytime the container is rebuilt. --- docker-compose.override.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docker-compose.override.yml b/docker-compose.override.yml index 7d9fb98f4..8941b80f8 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -23,6 +23,7 @@ services: volumes: - .:/home/vscode/dandi - uv_cache:/home/vscode/uv + - pre-commit_cache:/home/vscode/.cache/pre-commit ports: - 8000:8000 depends_on: @@ -64,6 +65,7 @@ services: volumes: - .:/home/vscode/dandi - uv_cache:/home/vscode/uv + - pre-commit_cache:/home/vscode/.cache/pre-commit depends_on: postgres: condition: service_healthy @@ -74,3 +76,4 @@ services: volumes: uv_cache: + pre-commit_cache: From a377ff9fa5c84a376c053a05099596e6f03a34cd Mon Sep 17 00:00:00 2001 From: Jacob Nesbitt Date: Thu, 13 Nov 2025 13:29:14 -0500 Subject: [PATCH 02/13] Add create_test_user management command --- .../management/commands/create_test_user.py | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 dandiapi/api/management/commands/create_test_user.py diff --git a/dandiapi/api/management/commands/create_test_user.py b/dandiapi/api/management/commands/create_test_user.py new file mode 100644 index 000000000..c39a0034c --- /dev/null +++ b/dandiapi/api/management/commands/create_test_user.py @@ -0,0 +1,63 @@ +from __future__ import annotations + +from allauth.socialaccount.models import SocialAccount +from django.contrib.auth.models import User +from django.db import transaction +import djclick as click +import faker + +from dandiapi.api.models.user import UserMetadata + + +@click.command() +@click.option( + '--auto-approve', + is_flag=True, + help='Auto approve this user, skipping the questionnaire.', +) +@click.option( + '--password', + default='password', + show_default=True, + help='The password for this user.', +) +def create_test_user(*, auto_approve: bool, password: str): + fake = faker.Faker() + + with transaction.atomic(): + email = fake.email() + user = User.objects.create( + first_name=fake.first_name(), + last_name=fake.last_name(), + username=email, + email=email, + ) + + user.set_password(password) + user.save() + + UserMetadata.objects.create( + user=user, + status=UserMetadata.Status.APPROVED if auto_approve else UserMetadata.Status.INCOMPLETE, + ) + + uid = fake.random_number(digits=8, fix_len=True) + SocialAccount.objects.create( + user=user, + provider='github', + uid=uid, + extra_data={ + 'id': uid, + 'name': user.get_full_name(), + 'email': user.email, + 'login': fake.user_name(), + }, + ) + + click.echo( + click.style( + f'Created user "{user.email}" with password "{password}"', + fg='green', + bold=True, + ) + ) From de744824cc3db4fdaf3d1a7491695c15ebdff176 Mon Sep 17 00:00:00 2001 From: Jacob Nesbitt Date: Thu, 13 Nov 2025 16:16:39 -0500 Subject: [PATCH 03/13] Remove unnecessary setting --- dandiapi/api/views/info.py | 3 ++- dandiapi/settings/base.py | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/dandiapi/api/views/info.py b/dandiapi/api/views/info.py index fa2c410ac..08760fb33 100644 --- a/dandiapi/api/views/info.py +++ b/dandiapi/api/views/info.py @@ -3,6 +3,7 @@ import importlib.metadata from urllib.parse import ParseResult, urlencode, urlparse, urlunparse +from dandischema.consts import ALLOWED_INPUT_SCHEMAS from django.conf import settings from django.urls import reverse from drf_yasg.utils import no_body, swagger_auto_schema @@ -74,7 +75,7 @@ def info_view(request): data={ 'schema_version': settings.DANDI_SCHEMA_VERSION, 'schema_url': get_schema_url(), - 'allowed_schema_versions': settings.ALLOWED_DANDI_SCHEMA_VERSIONS, + 'allowed_schema_versions': ALLOWED_INPUT_SCHEMAS, 'version': importlib.metadata.version('dandiapi'), 'cli-minimal-version': '0.60.0', 'cli-bad-versions': [], diff --git a/dandiapi/settings/base.py b/dandiapi/settings/base.py index 7af8db68b..ec32d4395 100644 --- a/dandiapi/settings/base.py +++ b/dandiapi/settings/base.py @@ -7,7 +7,6 @@ from urllib.parse import urlunparse from corsheaders.defaults import default_headers -from dandischema.consts import ALLOWED_INPUT_SCHEMAS from dandischema.consts import DANDI_SCHEMA_VERSION as _DEFAULT_DANDI_SCHEMA_VERSION import django_stubs_ext from environ import Env @@ -176,7 +175,6 @@ DANDI_SCHEMA_VERSION: str = env.str( 'DJANGO_DANDI_SCHEMA_VERSION', default=_DEFAULT_DANDI_SCHEMA_VERSION ) -ALLOWED_DANDI_SCHEMA_VERSIONS: list[str] = ALLOWED_INPUT_SCHEMAS DANDI_ZARR_PREFIX_NAME: str = env.str('DJANGO_DANDI_ZARR_PREFIX_NAME', default='zarr') From 1aed085706008464d9fefdf9566c9d9ef929dfd4 Mon Sep 17 00:00:00 2001 From: Isaac To Date: Fri, 21 Nov 2025 08:15:16 -0800 Subject: [PATCH 04/13] Add vendorization support (#2584) * feat: have the `/info/` endpoint provide instance config info * test: update `test_rest_info()` To include the output of instance config value * tmp: temporarily set the dandischema dependency to the devendorize branch * test: provide env var to set schema config instance name * feat: make dandischema instance config available through Django settings * feat: provide instance name through dandischema instance config instead of a hardcoded value * feat: provide instance name through dandischema instance config instead of a hardcoded value * feat: replace `"DANDI` with instance name in `DandisetList` vue component * ci: add DJANGO_DANDI_INSTANCE_NAME env var to frontend CI * feat: replace hardcoded RRID with one set in schema instance config * ci: add instance identifier to backend CI env * feat: replace the use of hardcoded license * feat: use dandiarchive's version in producing `PublishActivity` metadata Per @yarikoptic this is an improvement over the hardcoded '0.1.0' * feat: vendorize software name in `PublishActivity` metadata * test: update tests for the `PublishActivity` metadata changes * style: replace use of `dandiapi.__version__` with `importlib.metadata.version` Co-authored-by: Mike VanDenburgh <37340715+mvandenburgh@users.noreply.github.com> * rf: remove outdated patching of JSON schema For discussion about this removal, see https://github.com/dandi/dandi-archive/pull/2584#issuecomment-3408003388 for details * style: replace use of `dandiapi.__version__` with `importlib.metadata.version` in tests * rf: import `dandischema.conf.get_instance_config` as `get_schema_instance_config` Importing the function under this name emphasizes the returned config pertains to dandischema and its usage * feat: call avoid using `dandiapi/settings/base.py` to store schema instance config `dandiapi/settings/base.py` is reserved for used for configuring settings, possibly for the Django app alone. See https://github.com/dandi/dandi-archive/pull/2584#discussion_r2436938965 for rationale for the change. * revert: revert naming of API service in generated asset metadata * Always use first sorted license value in create_dev_dandiset * Don't rename `get_instance_config` to `get_schema_instance_config` * Don't use global variables for instance config * Reuse identical schema object for tests * Require DJANGO_DANDI_INSTANCE_IDENTIFIER env var * Use dummy instance config values for local dev and CI * Revert change of version value in PublishableMetadataMixin * Update tests regarding Pydantic and None values This more directly tests the issue we care about. As it was written, the test could still pass unintentionally, if the default values were the same as the values we had set them to. * Require DJANGO_DANDI_DOI_API_PREFIX env var * Use schema vendorization for DOIs * Use schema vendorization for id/identifier * Revert unnecessary change to `$route.query` * Use function to supply default license value * Fix linting errors * Fix malformed license field in create_dev_dandiset * Move `get_default_license` to conftest.py and rename * Set dandischema dependency to 0.12.0 --------- Co-authored-by: Mike VanDenburgh <37340715+mvandenburgh@users.noreply.github.com> Co-authored-by: Jacob Nesbitt --- .github/workflows/backend-ci.yml | 3 + .github/workflows/frontend-ci.yml | 3 + dandiapi/api/doi.py | 6 +- .../commands/create_dev_dandiset.py | 3 +- dandiapi/api/models/metadata.py | 8 +- dandiapi/api/models/version.py | 6 +- dandiapi/api/services/metadata/__init__.py | 10 +- dandiapi/api/services/publish/__init__.py | 6 +- dandiapi/api/tests/factories.py | 9 +- dandiapi/api/tests/fuzzy.py | 14 +- dandiapi/api/tests/test_asset.py | 20 +- dandiapi/api/tests/test_dandiset.py | 47 ++- dandiapi/api/tests/test_info.py | 16 + dandiapi/api/tests/test_tasks.py | 36 +- dandiapi/api/tests/test_version.py | 52 +-- dandiapi/api/views/dandiset.py | 3 +- dandiapi/api/views/info.py | 7 + dandiapi/conftest.py | 5 + dandiapi/settings/base.py | 11 +- dev/.env.docker-compose | 3 + dev/.env.docker-compose-native | 3 + pyproject.toml | 2 +- uv.lock | 319 +++--------------- web/src/components/DandisetList.vue | 11 +- web/src/types/index.ts | 7 + web/src/utils/schema.ts | 4 - 26 files changed, 256 insertions(+), 358 deletions(-) create mode 100644 dandiapi/api/tests/test_info.py diff --git a/.github/workflows/backend-ci.yml b/.github/workflows/backend-ci.yml index bf3d5157f..4282ee9fc 100644 --- a/.github/workflows/backend-ci.yml +++ b/.github/workflows/backend-ci.yml @@ -53,6 +53,9 @@ jobs: DJANGO_DANDI_WEB_APP_URL: http://localhost:8085 DJANGO_DANDI_API_URL: http://localhost:8000 DJANGO_DANDI_JUPYTERHUB_URL: https://hub.dandiarchive.org/ + DJANGO_DANDI_INSTANCE_NAME: DEV-DANDI + DJANGO_DANDI_INSTANCE_IDENTIFIER: "RRID:ABC_123456" + DJANGO_DANDI_DOI_API_PREFIX: "10.80507" steps: - uses: actions/checkout@v5 with: diff --git a/.github/workflows/frontend-ci.yml b/.github/workflows/frontend-ci.yml index f06c5e383..7ce03f8eb 100644 --- a/.github/workflows/frontend-ci.yml +++ b/.github/workflows/frontend-ci.yml @@ -78,6 +78,9 @@ jobs: DJANGO_DANDI_WEB_APP_URL: http://localhost:8085 DJANGO_DANDI_API_URL: http://localhost:8000 DJANGO_DANDI_JUPYTERHUB_URL: https://hub.dandiarchive.org/ + DJANGO_DANDI_INSTANCE_NAME: DEV-DANDI + DJANGO_DANDI_INSTANCE_IDENTIFIER: "RRID:ABC_123456" + DJANGO_DANDI_DOI_API_PREFIX: "10.80507" # Web client env vars VITE_APP_DANDI_API_ROOT: http://localhost:8000/api/ diff --git a/dandiapi/api/doi.py b/dandiapi/api/doi.py index 5cd18cbd9..2869d358a 100644 --- a/dandiapi/api/doi.py +++ b/dandiapi/api/doi.py @@ -3,6 +3,7 @@ import logging from typing import TYPE_CHECKING +from dandischema.conf import get_instance_config from django.conf import settings import requests @@ -29,10 +30,11 @@ def _generate_doi_data(version: Version): publish = settings.DANDI_DOI_PUBLISH # Use the DANDI test datacite instance as a placeholder if PREFIX isn't set - prefix = settings.DANDI_DOI_API_PREFIX or '10.80507' + prefix = settings.DANDI_DOI_API_PREFIX + instance_name: str = get_instance_config().instance_name dandiset_id = version.dandiset.identifier version_id = version.version - doi = f'{prefix}/dandi.{dandiset_id}/{version_id}' + doi = f'{prefix}/{instance_name.lower()}.{dandiset_id}/{version_id}' metadata = version.metadata metadata['doi'] = doi return (doi, to_datacite(metadata, publish=publish)) diff --git a/dandiapi/api/management/commands/create_dev_dandiset.py b/dandiapi/api/management/commands/create_dev_dandiset.py index 045748960..5467950bb 100644 --- a/dandiapi/api/management/commands/create_dev_dandiset.py +++ b/dandiapi/api/management/commands/create_dev_dandiset.py @@ -2,6 +2,7 @@ from uuid import uuid4 +from dandischema.conf import get_instance_config from django.conf import settings from django.contrib.auth.models import User from django.core.files.uploadedfile import SimpleUploadedFile @@ -32,7 +33,7 @@ def create_dev_dandiset(*, name: str, email: str, num_extra_owners: int): version_metadata = { 'description': 'An informative description', - 'license': ['spdx:CC0-1.0'], + 'license': [sorted(x.value for x in get_instance_config().licenses)[0]], } dandiset, draft_version = create_open_dandiset( user=owner, version_name=name, version_metadata=version_metadata diff --git a/dandiapi/api/models/metadata.py b/dandiapi/api/models/metadata.py index 103778051..35c885abb 100644 --- a/dandiapi/api/models/metadata.py +++ b/dandiapi/api/models/metadata.py @@ -3,6 +3,8 @@ from typing import TYPE_CHECKING from uuid import uuid4 +from dandischema.conf import get_instance_config + if TYPE_CHECKING: import datetime @@ -10,6 +12,8 @@ class PublishableMetadataMixin: @classmethod def published_by(cls, now: datetime.datetime): + schema_config = get_instance_config() + return { 'id': uuid4().urn, 'name': 'DANDI publish', @@ -20,8 +24,8 @@ def published_by(cls, now: datetime.datetime): 'wasAssociatedWith': [ { 'id': uuid4().urn, - 'identifier': 'RRID:SCR_017571', - 'name': 'DANDI API', + 'identifier': schema_config.instance_identifier, + 'name': f'{schema_config.instance_name} API', # TODO: version the API 'version': '0.1.0', 'schemaKey': 'Software', diff --git a/dandiapi/api/models/version.py b/dandiapi/api/models/version.py index ef3e148ea..9363b8aa3 100644 --- a/dandiapi/api/models/version.py +++ b/dandiapi/api/models/version.py @@ -4,6 +4,7 @@ import logging from typing import TypedDict +from dandischema.conf import get_instance_config from dandischema.models import AccessType from django.conf import settings from django.contrib.postgres.indexes import HashIndex @@ -229,6 +230,7 @@ def _populate_access_metadata(self): def _populate_metadata(self): from dandiapi.api.manifests import manifest_location + schema_config = get_instance_config() metadata = { **self.metadata, '@context': ( @@ -237,9 +239,9 @@ def _populate_metadata(self): ), 'manifestLocation': manifest_location(self), 'name': self.name, - 'identifier': f'DANDI:{self.dandiset.identifier}', + 'identifier': (f'{schema_config.instance_name}:{self.dandiset.identifier}'), 'version': self.version, - 'id': f'DANDI:{self.dandiset.identifier}/{self.version}', + 'id': (f'{schema_config.instance_name}:{self.dandiset.identifier}/{self.version}'), 'repository': settings.DANDI_WEB_APP_URL, 'url': ( f'{settings.DANDI_WEB_APP_URL}/dandiset/{self.dandiset.identifier}/{self.version}' diff --git a/dandiapi/api/services/metadata/__init__.py b/dandiapi/api/services/metadata/__init__.py index 9c9fdc80a..5ee47e1f4 100644 --- a/dandiapi/api/services/metadata/__init__.py +++ b/dandiapi/api/services/metadata/__init__.py @@ -3,6 +3,7 @@ from typing import TYPE_CHECKING from celery.utils.log import get_task_logger +from dandischema.conf import get_instance_config import dandischema.exceptions from dandischema.metadata import aggregate_assets_summary, validate from django.conf import settings @@ -108,6 +109,8 @@ def version_aggregate_assets_summary(version: Version) -> None: def validate_version_metadata(*, version: Version) -> None: def _build_validatable_version_metadata(version: Version) -> dict: + schema_config = get_instance_config() + # since Version.Status.VALID is a proxy for a version being publishable, we need to # validate against the PublishedDandiset schema even though we lack several things # at validation time: id, url, doi, and assetsSummary. this tricks the validator into @@ -116,13 +119,16 @@ def _build_validatable_version_metadata(version: Version) -> dict: metadata_for_validation = publishable_version.metadata metadata_for_validation['id'] = ( - f'DANDI:{publishable_version.dandiset.identifier}/{publishable_version.version}' + f'{schema_config.instance_name}:' + f'{publishable_version.dandiset.identifier}/{publishable_version.version}' ) metadata_for_validation['url'] = ( f'{settings.DANDI_WEB_APP_URL}/dandiset/' f'{publishable_version.dandiset.identifier}/{publishable_version.version}' ) - metadata_for_validation['doi'] = '10.80507/dandi.123456/0.123456.1234' + metadata_for_validation['doi'] = ( + f'{schema_config.doi_prefix}/{schema_config.instance_name.lower()}.123456/0.123456.1234' + ) metadata_for_validation['assetsSummary'] = { 'schemaKey': 'AssetsSummary', 'numberOfBytes': 1 diff --git a/dandiapi/api/services/publish/__init__.py b/dandiapi/api/services/publish/__init__.py index 216b32c4e..1e5e5307d 100644 --- a/dandiapi/api/services/publish/__init__.py +++ b/dandiapi/api/services/publish/__init__.py @@ -4,6 +4,7 @@ import datetime from typing import TYPE_CHECKING +from dandischema.conf import get_instance_config from dandischema.metadata import aggregate_assets_summary, validate from django.contrib.auth.models import User from django.db import transaction @@ -182,7 +183,10 @@ def _publish_dandiset(dandiset_id: int, user_id: int) -> None: old_version.save() # Inject a dummy DOI so the metadata is valid - new_version.metadata['doi'] = '10.80507/dandi.123456/0.123456.1234' + schema_config = get_instance_config() + new_version.metadata['doi'] = ( + f'{schema_config.doi_prefix}/{schema_config.instance_name.lower()}.123456/0.123456.1234' + ) validate(new_version.metadata, schema_key='PublishedDandiset', json_validation=True) diff --git a/dandiapi/api/tests/factories.py b/dandiapi/api/tests/factories.py index 3fa29f47d..51a87d61c 100644 --- a/dandiapi/api/tests/factories.py +++ b/dandiapi/api/tests/factories.py @@ -4,6 +4,7 @@ import hashlib from allauth.socialaccount.models import SocialAccount +from dandischema.conf import get_instance_config from dandischema.models import AccessType from django.conf import settings from django.contrib.auth.models import User @@ -113,6 +114,8 @@ class Meta: @factory.lazy_attribute def metadata(self) -> dict: + from dandiapi.conftest import get_first_allowed_license + metadata = { **faker.Faker().pydict(value_types=['str', 'float', 'int']), 'schemaVersion': settings.DANDI_SCHEMA_VERSION, @@ -134,7 +137,7 @@ def metadata(self) -> dict: 'schemaKey': 'Person', } ], - 'license': ['spdx:CC0-1.0'], + 'license': [get_first_allowed_license()], } # Remove faked data that might conflict with the schema types for key in ['about']: @@ -162,7 +165,9 @@ class DraftVersionFactory(BaseVersionFactory): class PublishedVersionFactory(BaseVersionFactory): doi = factory.LazyAttribute( - lambda self: f'10.80507/dandi.{self.dandiset.identifier}/{self.version}' + lambda self: f'{get_instance_config().doi_prefix}/' + f'{get_instance_config().instance_name}.' + f'{self.dandiset.identifier}/{self.version}' ) status = Version.Status.PUBLISHED diff --git a/dandiapi/api/tests/fuzzy.py b/dandiapi/api/tests/fuzzy.py index d005501a5..d416d8f77 100644 --- a/dandiapi/api/tests/fuzzy.py +++ b/dandiapi/api/tests/fuzzy.py @@ -2,6 +2,8 @@ import re +from dandischema.conf import get_instance_config + class Re: def __init__(self, pattern): @@ -23,12 +25,22 @@ def __hash__(self): return hash(self.pattern) +schema_config = get_instance_config() + TIMESTAMP_RE = Re(r'\d{4}-\d{2}-\d{2}T\d{2}\:\d{2}\:\d{2}\.\d{6}Z') UTC_ISO_TIMESTAMP_RE = Re(r'\d{4}-\d{2}-\d{2}T\d{2}\:\d{2}\:\d{2}\.\d{6}\+[0-9]{2}:[0-9]{2}') DATE_RE = Re(r'\d{4}-\d{2}-\d{2}') DANDISET_ID_RE = Re(r'\d{6}') -DANDISET_SCHEMA_ID_RE = Re(r'DANDI:\d{6}') +DANDISET_SCHEMA_ID_RE = Re(rf'{schema_config.instance_name}:\d{{6}}') VERSION_ID_RE = Re(r'0\.\d{6}\.\d{4}') HTTP_URL_RE = Re(r'http[s]?\://[^/]+(/[^/]+)*[/]?(&.+)?') UUID_RE = Re(r'[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}') URN_RE = Re(r'urn:uuid:[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}') + +DEFAULT_WAS_ASSOCIATED_WITH = { + 'id': URN_RE, + 'identifier': schema_config.instance_identifier, + 'name': f'{schema_config.instance_name} API', + 'version': '0.1.0', + 'schemaKey': 'Software', +} diff --git a/dandiapi/api/tests/test_asset.py b/dandiapi/api/tests/test_asset.py index 32c541184..5ecb5447a 100644 --- a/dandiapi/api/tests/test_asset.py +++ b/dandiapi/api/tests/test_asset.py @@ -28,7 +28,14 @@ from dandiapi.zarr.tasks import ingest_zarr_archive from dandiapi.zarr.tests.factories import ZarrArchiveFactory -from .fuzzy import HTTP_URL_RE, TIMESTAMP_RE, URN_RE, UTC_ISO_TIMESTAMP_RE, UUID_RE +from .fuzzy import ( + DEFAULT_WAS_ASSOCIATED_WITH, + HTTP_URL_RE, + TIMESTAMP_RE, + URN_RE, + UTC_ISO_TIMESTAMP_RE, + UUID_RE, +) # Model tests @@ -127,16 +134,7 @@ def test_publish_asset(draft_asset: Asset): 'name': 'DANDI publish', 'startDate': UTC_ISO_TIMESTAMP_RE, 'endDate': UTC_ISO_TIMESTAMP_RE, - 'wasAssociatedWith': [ - { - 'id': URN_RE, - 'identifier': 'RRID:SCR_017571', - 'name': 'DANDI API', - # TODO: version the API - 'version': '0.1.0', - 'schemaKey': 'Software', - } - ], + 'wasAssociatedWith': [DEFAULT_WAS_ASSOCIATED_WITH], 'schemaKey': 'PublishActivity', }, 'datePublished': UTC_ISO_TIMESTAMP_RE, diff --git a/dandiapi/api/tests/test_dandiset.py b/dandiapi/api/tests/test_dandiset.py index e74493966..64236cf77 100644 --- a/dandiapi/api/tests/test_dandiset.py +++ b/dandiapi/api/tests/test_dandiset.py @@ -4,6 +4,7 @@ from typing import TYPE_CHECKING from urllib.parse import urlencode +from dandischema.conf import get_instance_config from django.conf import settings from django.contrib.auth.models import AnonymousUser from django.utils import timezone @@ -21,6 +22,7 @@ PublishedVersionFactory, UserFactory, ) +from dandiapi.conftest import get_first_allowed_license from .fuzzy import ( DANDISET_ID_RE, @@ -34,6 +36,9 @@ from rest_framework.test import APIClient +_SCHEMA_CONFIG = get_instance_config() + + @pytest.mark.django_db def test_dandiset_identifier(): dandiset = DandisetFactory.create() @@ -452,7 +457,7 @@ def test_dandiset_rest_create(api_client): ], 'name': name, 'identifier': DANDISET_SCHEMA_ID_RE, - 'id': f'DANDI:{dandiset.identifier}/draft', + 'id': f'{_SCHEMA_CONFIG.instance_name}:{dandiset.identifier}/draft', 'version': 'draft', 'url': url, 'dateCreated': UTC_ISO_TIMESTAMP_RE, @@ -489,7 +494,7 @@ def test_dandiset_rest_create_with_identifier(api_client): api_client.force_authenticate(user=user) name = 'Test Dandiset' identifier = '123456' - metadata = {'foo': 'bar', 'identifier': f'DANDI:{identifier}'} + metadata = {'foo': 'bar', 'identifier': f'{_SCHEMA_CONFIG.instance_name}:{identifier}'} response = api_client.post('/api/dandisets/', {'name': name, 'metadata': metadata}) assert response.data == { @@ -534,8 +539,8 @@ def test_dandiset_rest_create_with_identifier(api_client): f'{settings.DANDI_API_URL}/api/dandisets/{dandiset.identifier}/versions/draft/assets/' ], 'name': name, - 'identifier': f'DANDI:{identifier}', - 'id': f'DANDI:{dandiset.identifier}/draft', + 'identifier': f'{_SCHEMA_CONFIG.instance_name}:{identifier}', + 'id': f'{_SCHEMA_CONFIG.instance_name}:{dandiset.identifier}/draft', 'version': 'draft', 'url': url, 'dateCreated': UTC_ISO_TIMESTAMP_RE, @@ -574,7 +579,7 @@ def test_dandiset_rest_create_with_contributor(api_client): identifier = '123456' metadata = { 'foo': 'bar', - 'identifier': f'DANDI:{identifier}', + 'identifier': f'{_SCHEMA_CONFIG.instance_name}:{identifier}', # This contributor is different from the user 'contributor': [ { @@ -631,8 +636,8 @@ def test_dandiset_rest_create_with_contributor(api_client): f'{settings.DANDI_API_URL}/api/dandisets/{dandiset.identifier}/versions/draft/assets/' ], 'name': name, - 'identifier': f'DANDI:{identifier}', - 'id': f'DANDI:{dandiset.identifier}/draft', + 'identifier': f'{_SCHEMA_CONFIG.instance_name}:{identifier}', + 'id': f'{_SCHEMA_CONFIG.instance_name}:{dandiset.identifier}/draft', 'version': 'draft', 'url': url, 'dateCreated': UTC_ISO_TIMESTAMP_RE, @@ -712,7 +717,7 @@ def test_dandiset_rest_create_embargoed(api_client): ], 'name': name, 'identifier': DANDISET_SCHEMA_ID_RE, - 'id': f'DANDI:{dandiset.identifier}/draft', + 'id': f'{_SCHEMA_CONFIG.instance_name}:{dandiset.identifier}/draft', 'version': 'draft', 'url': url, 'dateCreated': UTC_ISO_TIMESTAMP_RE, @@ -754,7 +759,11 @@ def test_dandiset_rest_create_embargoed_with_award_info(api_client: APIClient): user = UserFactory.create() api_client.force_authenticate(user=user) name = 'Test Embargoed Dandiset' - metadata = {'name': name, 'description': 'Test embargoed dandiset', 'license': ['spdx:CC0-1.0']} + metadata = { + 'name': name, + 'description': 'Test embargoed dandiset', + 'license': [get_first_allowed_license()], + } # Create embargoed dandiset with funding and award info embargo_end_date = (timezone.now().date() + datetime.timedelta(days=365)).isoformat() @@ -802,7 +811,11 @@ def test_dandiset_rest_create_embargoed_no_funding_info(api_client: APIClient): user = UserFactory.create() api_client.force_authenticate(user=user) name = 'Test Embargoed Dandiset - No Funding' - metadata = {'name': name, 'description': 'Test embargoed dandiset', 'license': ['spdx:CC0-1.0']} + metadata = { + 'name': name, + 'description': 'Test embargoed dandiset', + 'license': [get_first_allowed_license()], + } # Create embargoed dandiset without funding info query_params = {'embargo': 'true'} @@ -847,7 +860,11 @@ def test_dandiset_rest_create_embargoed_funding_no_award(api_client: APIClient): user = UserFactory.create() api_client.force_authenticate(user=user) name = 'Test Embargoed Dandiset - Funding Only' - metadata = {'name': name, 'description': 'Test embargoed dandiset', 'license': ['spdx:CC0-1.0']} + metadata = { + 'name': name, + 'description': 'Test embargoed dandiset', + 'license': [get_first_allowed_license()], + } # Create embargoed dandiset with funding source but no award number query_params = { @@ -867,7 +884,11 @@ def test_dandiset_rest_create_embargoed_award_no_funding(api_client: APIClient): user = UserFactory.create() api_client.force_authenticate(user=user) name = 'Test Embargoed Dandiset - Award Only' - metadata = {'name': name, 'description': 'Test embargoed dandiset', 'license': ['spdx:CC0-1.0']} + metadata = { + 'name': name, + 'description': 'Test embargoed dandiset', + 'license': [get_first_allowed_license()], + } # Create embargoed dandiset with award number but no funding source query_params = { @@ -888,7 +909,7 @@ def test_dandiset_rest_create_with_duplicate_identifier(api_client): api_client.force_authenticate(user=user) name = 'Test Dandiset' identifier = dandiset.identifier - metadata = {'foo': 'bar', 'identifier': f'DANDI:{identifier}'} + metadata = {'foo': 'bar', 'identifier': f'{_SCHEMA_CONFIG.instance_name}:{identifier}'} response = api_client.post('/api/dandisets/', {'name': name, 'metadata': metadata}) assert response.status_code == 400 diff --git a/dandiapi/api/tests/test_info.py b/dandiapi/api/tests/test_info.py new file mode 100644 index 000000000..2c02b0870 --- /dev/null +++ b/dandiapi/api/tests/test_info.py @@ -0,0 +1,16 @@ +from __future__ import annotations + +from dandischema.conf import get_instance_config + + +def test_rest_info_instance_config_include_none(api_client): + resp = api_client.get('/api/info/') + assert resp.status_code == 200 + + # Ensure that there is no difference in the keys being returned from the info endpoint. If the + # info endpoint were missing values, it would allow for default values to be creep in when + # de-serializing the JSON into a Pydantic model. + assert ( + resp.json()['instance_config'].keys() + == get_instance_config().model_dump(mode='json', exclude_none=False).keys() + ) diff --git a/dandiapi/api/tests/test_tasks.py b/dandiapi/api/tests/test_tasks.py index 433a93aea..6d1d78795 100644 --- a/dandiapi/api/tests/test_tasks.py +++ b/dandiapi/api/tests/test_tasks.py @@ -5,6 +5,7 @@ from pathlib import Path from typing import TYPE_CHECKING +from dandischema.conf import get_instance_config from django.conf import settings from django.core.files.storage import default_storage from django.forms.models import model_to_dict @@ -20,12 +21,15 @@ from dandiapi.api.tests.factories import DraftVersionFactory, UserFactory from dandiapi.zarr.models import ZarrArchiveStatus -from .fuzzy import HTTP_URL_RE, URN_RE, UTC_ISO_TIMESTAMP_RE +from .fuzzy import DEFAULT_WAS_ASSOCIATED_WITH, HTTP_URL_RE, URN_RE, UTC_ISO_TIMESTAMP_RE if TYPE_CHECKING: from rest_framework.test import APIClient +_SCHEMA_CONFIG = get_instance_config() + + @pytest.mark.django_db def test_calculate_checksum_task(asset_blob_factory): asset_blob = asset_blob_factory(blob__data=b'known-content', size=13, sha256=None) @@ -379,29 +383,23 @@ def test_publish_task( 'name': 'DANDI publish', 'startDate': UTC_ISO_TIMESTAMP_RE, 'endDate': UTC_ISO_TIMESTAMP_RE, - 'wasAssociatedWith': [ - { - 'id': URN_RE, - 'identifier': 'RRID:SCR_017571', - 'name': 'DANDI API', - # TODO: version the API - 'version': '0.1.0', - 'schemaKey': 'Software', - } - ], + 'wasAssociatedWith': [DEFAULT_WAS_ASSOCIATED_WITH], 'schemaKey': 'PublishActivity', }, 'datePublished': UTC_ISO_TIMESTAMP_RE, 'manifestLocation': [HTTP_URL_RE], - 'identifier': f'DANDI:{draft_version.dandiset.identifier}', + 'identifier': f'{_SCHEMA_CONFIG.instance_name}:{draft_version.dandiset.identifier}', 'version': published_version.version, - 'id': f'DANDI:{draft_version.dandiset.identifier}/{published_version.version}', + 'id': ( + f'{_SCHEMA_CONFIG.instance_name}:' + f'{draft_version.dandiset.identifier}/{published_version.version}' + ), 'url': ( f'{settings.DANDI_WEB_APP_URL}/dandiset/{draft_version.dandiset.identifier}' f'/{published_version.version}' ), 'citation': published_version.citation(published_version.metadata), - 'doi': f'10.80507/dandi.{draft_version.dandiset.identifier}/{published_version.version}', + 'doi': published_version.doi, # Once the assets are linked, assetsSummary should be computed properly 'assetsSummary': { 'schemaKey': 'AssetsSummary', @@ -439,15 +437,7 @@ def test_publish_task( 'startDate': UTC_ISO_TIMESTAMP_RE, # TODO: endDate needs to be defined before publish is complete 'endDate': UTC_ISO_TIMESTAMP_RE, - 'wasAssociatedWith': [ - { - 'id': URN_RE, - 'identifier': 'RRID:SCR_017571', - 'name': 'DANDI API', - 'version': '0.1.0', - 'schemaKey': 'Software', - } - ], + 'wasAssociatedWith': [DEFAULT_WAS_ASSOCIATED_WITH], 'schemaKey': 'PublishActivity', }, } diff --git a/dandiapi/api/tests/test_version.py b/dandiapi/api/tests/test_version.py index 9aea61ded..78409de81 100644 --- a/dandiapi/api/tests/test_version.py +++ b/dandiapi/api/tests/test_version.py @@ -4,6 +4,7 @@ from time import sleep from typing import TYPE_CHECKING +from dandischema.conf import get_instance_config from dandischema.models import AccessType from django.conf import settings from freezegun import freeze_time @@ -28,7 +29,16 @@ from dandiapi.api.services.publish import _build_publishable_version_from_draft from dandiapi.zarr.tasks import ingest_zarr_archive -from .fuzzy import HTTP_URL_RE, TIMESTAMP_RE, URN_RE, UTC_ISO_TIMESTAMP_RE, VERSION_ID_RE +from .fuzzy import ( + DEFAULT_WAS_ASSOCIATED_WITH, + HTTP_URL_RE, + TIMESTAMP_RE, + URN_RE, + UTC_ISO_TIMESTAMP_RE, + VERSION_ID_RE, +) + +_SCHEMA_CONFIG = get_instance_config() @freeze_time() @@ -68,9 +78,12 @@ def test_draft_version_metadata_computed(): **original_metadata, 'manifestLocation': [HTTP_URL_RE], 'name': draft_version.name, - 'identifier': f'DANDI:{draft_version.dandiset.identifier}', + 'identifier': f'{_SCHEMA_CONFIG.instance_name}:{draft_version.dandiset.identifier}', 'version': draft_version.version, - 'id': f'DANDI:{draft_version.dandiset.identifier}/{draft_version.version}', + 'id': ( + f'{_SCHEMA_CONFIG.instance_name}:' + f'{draft_version.dandiset.identifier}/{draft_version.version}' + ), 'url': ( f'{settings.DANDI_WEB_APP_URL}/dandiset/' f'{draft_version.dandiset.identifier}/{draft_version.version}' @@ -103,11 +116,16 @@ def test_published_version_metadata_computed(): **original_metadata, 'manifestLocation': [HTTP_URL_RE], 'name': published_version.name, - 'identifier': f'DANDI:{published_version.dandiset.identifier}', + 'identifier': f'{_SCHEMA_CONFIG.instance_name}:{published_version.dandiset.identifier}', 'version': published_version.version, - 'id': f'DANDI:{published_version.dandiset.identifier}/{published_version.version}', + 'id': ( + f'{_SCHEMA_CONFIG.instance_name}:' + f'{published_version.dandiset.identifier}/{published_version.version}' + ), 'doi': ( - f'10.80507/dandi.{published_version.dandiset.identifier}/{published_version.version}' + f'{settings.DANDI_DOI_API_PREFIX}/' + f'{_SCHEMA_CONFIG.instance_name}.' + f'{published_version.dandiset.identifier}/{published_version.version}' ), 'url': ( f'{settings.DANDI_WEB_APP_URL}/dandiset/' @@ -308,24 +326,18 @@ def test_version_publish_version(asset): 'name': 'DANDI publish', 'startDate': UTC_ISO_TIMESTAMP_RE, 'endDate': UTC_ISO_TIMESTAMP_RE, - 'wasAssociatedWith': [ - { - 'id': URN_RE, - 'identifier': 'RRID:SCR_017571', - 'name': 'DANDI API', - # TODO: version the API - 'version': '0.1.0', - 'schemaKey': 'Software', - } - ], + 'wasAssociatedWith': [DEFAULT_WAS_ASSOCIATED_WITH], 'schemaKey': 'PublishActivity', }, 'dateCreated': UTC_ISO_TIMESTAMP_RE, 'datePublished': UTC_ISO_TIMESTAMP_RE, 'manifestLocation': [HTTP_URL_RE], - 'identifier': f'DANDI:{publish_version.dandiset.identifier}', + 'identifier': f'{_SCHEMA_CONFIG.instance_name}:{publish_version.dandiset.identifier}', 'version': publish_version.version, - 'id': f'DANDI:{publish_version.dandiset.identifier}/{publish_version.version}', + 'id': ( + f'{_SCHEMA_CONFIG.instance_name}:' + f'{publish_version.dandiset.identifier}/{publish_version.version}' + ), 'url': ( f'{settings.DANDI_WEB_APP_URL}/dandiset/{publish_version.dandiset.identifier}' f'/{publish_version.version}' @@ -569,8 +581,8 @@ def test_version_rest_update(api_client): f'{settings.DANDI_API_URL}/api/dandisets/{draft_version.dandiset.identifier}/versions/draft/assets/' ], 'name': new_name, - 'identifier': f'DANDI:{draft_version.dandiset.identifier}', - 'id': f'DANDI:{draft_version.dandiset.identifier}/draft', + 'identifier': f'{_SCHEMA_CONFIG.instance_name}:{draft_version.dandiset.identifier}', + 'id': f'{_SCHEMA_CONFIG.instance_name}:{draft_version.dandiset.identifier}/draft', 'version': 'draft', 'url': url, 'repository': settings.DANDI_WEB_APP_URL, diff --git a/dandiapi/api/views/dandiset.py b/dandiapi/api/views/dandiset.py index c0466555a..f0ee4a873 100644 --- a/dandiapi/api/views/dandiset.py +++ b/dandiapi/api/views/dandiset.py @@ -4,6 +4,7 @@ from typing import TYPE_CHECKING from allauth.socialaccount.models import SocialAccount +from dandischema.conf import get_instance_config from django.contrib.auth.models import User from django.contrib.postgres.lookups import Unaccent from django.db import transaction @@ -434,7 +435,7 @@ def create(self, request: Request): identifier = None if 'identifier' in serializer.validated_data['metadata']: identifier = serializer.validated_data['metadata']['identifier'] - identifier = identifier.removeprefix('DANDI:') + identifier = identifier.removeprefix(f'{get_instance_config().instance_name}:') try: identifier = int(identifier) diff --git a/dandiapi/api/views/info.py b/dandiapi/api/views/info.py index 08760fb33..d4c857606 100644 --- a/dandiapi/api/views/info.py +++ b/dandiapi/api/views/info.py @@ -3,6 +3,7 @@ import importlib.metadata from urllib.parse import ParseResult, urlencode, urlparse, urlunparse +from dandischema.conf import get_instance_config from dandischema.consts import ALLOWED_INPUT_SCHEMAS from django.conf import settings from django.urls import reverse @@ -51,6 +52,9 @@ def __init__(self, *args, **kwargs): } ) + # Instance Configuration + instance_config = serializers.JSONField() + # Schema schema_version = serializers.CharField() schema_url = serializers.URLField() @@ -73,6 +77,9 @@ def info_view(request): api_url = f'{settings.DANDI_API_URL}/api' serializer = ApiInfoSerializer( data={ + # Set exclude_none=False to prevent any fields set to `None` from being set to a + # different default value when the JSON is de-serialized into a Pydantic model. + 'instance_config': get_instance_config().model_dump(mode='json', exclude_none=False), 'schema_version': settings.DANDI_SCHEMA_VERSION, 'schema_url': get_schema_url(), 'allowed_schema_versions': ALLOWED_INPUT_SCHEMAS, diff --git a/dandiapi/conftest.py b/dandiapi/conftest.py index 69ab457cd..b34019d3f 100644 --- a/dandiapi/conftest.py +++ b/dandiapi/conftest.py @@ -1,5 +1,6 @@ from __future__ import annotations +from dandischema.conf import get_instance_config import dandischema.digests.dandietag import pytest from pytest_factoryboy import register @@ -72,3 +73,7 @@ def version(request): @pytest.fixture def api_client() -> APIClient: return APIClient() + + +def get_first_allowed_license() -> str: + return sorted(x.value for x in get_instance_config().licenses)[0] diff --git a/dandiapi/settings/base.py b/dandiapi/settings/base.py index ec32d4395..4fb4c01da 100644 --- a/dandiapi/settings/base.py +++ b/dandiapi/settings/base.py @@ -175,7 +175,6 @@ DANDI_SCHEMA_VERSION: str = env.str( 'DJANGO_DANDI_SCHEMA_VERSION', default=_DEFAULT_DANDI_SCHEMA_VERSION ) - DANDI_ZARR_PREFIX_NAME: str = env.str('DJANGO_DANDI_ZARR_PREFIX_NAME', default='zarr') # Required environment variables @@ -183,11 +182,19 @@ DANDI_API_URL = urlunparse(cast('ParseResult', env.url('DJANGO_DANDI_API_URL'))) DANDI_JUPYTERHUB_URL = urlunparse(cast('ParseResult', env.url('DJANGO_DANDI_JUPYTERHUB_URL'))) +# These are not used by us directly, but are used by dandi-schema. Not including them would modify +# the archive's behavior, so we require they be set. +DANDI_INSTANCE_NAME = env.str('DJANGO_DANDI_INSTANCE_NAME') +DANDI_INSTANCE_IDENTIFIER = env.str('DJANGO_DANDI_INSTANCE_IDENTIFIER') +DANDI_DOI_API_PREFIX = env.str( + 'DJANGO_DANDI_DOI_API_PREFIX' +) # This is used by us directly, but not in all circumstances + +# Non-required environment variabless _dandi_doi_api_url = cast('ParseResult | None', env.url('DJANGO_DANDI_DOI_API_URL', default=None)) DANDI_DOI_API_URL: str | None = urlunparse(_dandi_doi_api_url) if _dandi_doi_api_url else None DANDI_DOI_API_USER: str | None = env.str('DJANGO_DANDI_DOI_API_USER', default=None) DANDI_DOI_API_PASSWORD: str | None = env.str('DJANGO_DANDI_DOI_API_PASSWORD', default=None) -DANDI_DOI_API_PREFIX: str | None = env.str('DJANGO_DANDI_DOI_API_PREFIX', default=None) DANDI_DOI_PUBLISH: bool = env.bool('DJANGO_DANDI_DOI_PUBLISH', default=False) DANDI_VALIDATION_JOB_INTERVAL: int = env.int('DJANGO_DANDI_VALIDATION_JOB_INTERVAL', default=60) diff --git a/dev/.env.docker-compose b/dev/.env.docker-compose index 5273c9421..e694e2498 100644 --- a/dev/.env.docker-compose +++ b/dev/.env.docker-compose @@ -12,3 +12,6 @@ DJANGO_INTERNAL_IPS=0.0.0.0/0 DJANGO_DANDI_WEB_APP_URL=http://localhost:8085 DJANGO_DANDI_API_URL=http://localhost:8000 DJANGO_DANDI_JUPYTERHUB_URL=https://hub.dandiarchive.org/ +DJANGO_DANDI_INSTANCE_NAME=DEV-DANDI +DJANGO_DANDI_INSTANCE_IDENTIFIER=RRID:ABC_123456 +DJANGO_DANDI_DOI_API_PREFIX=10.80507 diff --git a/dev/.env.docker-compose-native b/dev/.env.docker-compose-native index 06c7b7470..db8613aa2 100644 --- a/dev/.env.docker-compose-native +++ b/dev/.env.docker-compose-native @@ -5,3 +5,6 @@ DJANGO_MINIO_STORAGE_URL=http://minioAccessKey:minioSecretKey@localhost:9000/dan DJANGO_DANDI_WEB_APP_URL=http://localhost:8085 DJANGO_DANDI_API_URL=http://localhost:8000 DJANGO_DANDI_JUPYTERHUB_URL=https://hub.dandiarchive.org/ +DJANGO_DANDI_INSTANCE_NAME=DEV-DANDI +DJANGO_DANDI_INSTANCE_IDENTIFIER=RRID:ABC_123456 +DJANGO_DANDI_DOI_API_PREFIX=10.80507 diff --git a/pyproject.toml b/pyproject.toml index 0121d7fa1..3574575f9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,7 +14,7 @@ dependencies = [ "celery", "dandi", # minimal version is also provided in API /info # Pin dandischema to exact version to make explicit which schema version is being used - "dandischema==0.11.1", # schema version 0.6.10 + "dandischema==0.12.0", # schema version 0.7.0 "django[argon2]", "django-allauth", "django-auth-style", diff --git a/uv.lock b/uv.lock index 0f256a552..662e84c38 100644 --- a/uv.lock +++ b/uv.lock @@ -2,19 +2,11 @@ version = 1 revision = 3 requires-python = ">=3.13" resolution-markers = [ - "python_full_version >= '3.14'", + "python_full_version >= '3.14' and platform_machine != 'arm64' and sys_platform == 'darwin'", + "(python_full_version >= '3.14' and platform_machine == 'arm64') or (python_full_version >= '3.14' and sys_platform != 'darwin')", "python_full_version < '3.14'", ] -[[package]] -name = "acres" -version = "0.5.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ec/ba/94b63a9af588fbf7bde25ce44d55456199654a92fb7b2337767198a824b0/acres-0.5.0.tar.gz", hash = "sha256:128b6447bf5df3b6210264feccbfa018b4ac5bd337358319aec6563f99db8f3a", size = 57750, upload-time = "2025-06-04T12:40:30.329Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/39/e8/806475fe4cdfd8635535d3fa11bd61d19b7cc94b61b9147ebdd2ab4cbbee/acres-0.5.0-py3-none-any.whl", hash = "sha256:fcc32b974b510897de0f041609b4234f9ff03e2e960aea088f63973fb106c772", size = 12703, upload-time = "2025-06-04T12:40:28.745Z" }, -] - [[package]] name = "aiohappyeyeballs" version = "2.6.1" @@ -125,6 +117,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, ] +[[package]] +name = "appdirs" +version = "1.4.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d7/d8/05696357e0311f5b5c316d7b95f46c669dd9c15aaeecbb48c7d0aeb88c40/appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41", size = 13470, upload-time = "2020-05-11T07:59:51.037Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/00/2344469e2084fb287c2e0b57b72910309874c3245463acd6cf5e3db69324/appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128", size = 9566, upload-time = "2020-05-11T07:59:49.499Z" }, +] + [[package]] name = "argon2-cffi" version = "25.1.0" @@ -181,12 +182,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f8/ed/e97229a566617f2ae958a6b13e7cc0f585470eac730a73e9e82c32a3cdd2/arrow-1.3.0-py3-none-any.whl", hash = "sha256:c728b120ebc00eb84e01882a6f5e7927a53960aa990ce7dd2b10f39005a67f80", size = 66419, upload-time = "2023-09-30T22:11:16.072Z" }, ] -[[package]] -name = "asciitree" -version = "0.3.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/2d/6a/885bc91484e1aa8f618f6f0228d76d0e67000b0fdd6090673b777e311913/asciitree-0.3.3.tar.gz", hash = "sha256:4aa4b9b649f85e3fcb343363d97564aa1fb62e249677f2e18a96765145cc0f6e", size = 3951, upload-time = "2016-09-05T19:10:42.681Z" } - [[package]] name = "asgiref" version = "3.10.0" @@ -214,33 +209,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" }, ] -[[package]] -name = "bids-validator-deno" -version = "2.1.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7a/ae/2045dacd02307997b612d7dd0c61092c2b6875d72cad3873d7af26dd7f3a/bids_validator_deno-2.1.1.tar.gz", hash = "sha256:1add452625548607d357c018a5ac1365809dcabf612624e71f5bdbb3d2ec081c", size = 73252, upload-time = "2025-09-29T16:25:21.774Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/30/08/0ca16cac7c2ad7ed8846691ab4ee4997a1d936d5c29509f3cec71f36a778/bids_validator_deno-2.1.1-py2.py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ac27e7070229fb45a80ff04b61de0a37551da751ab3d893a45cbd672daed220e", size = 42418556, upload-time = "2025-09-29T16:25:06.696Z" }, - { url = "https://files.pythonhosted.org/packages/18/4d/d98fc4e6c3ed73de84f7af84c695d676e086678f9e4ec3e2fc66c54b0d78/bids_validator_deno-2.1.1-py2.py3-none-macosx_11_0_arm64.whl", hash = "sha256:6082c69cdf4382df0ca82be7591d22444e65b2e2a72d06fb7d87a6e131d6e920", size = 41656505, upload-time = "2025-09-29T16:25:10.639Z" }, - { url = "https://files.pythonhosted.org/packages/15/44/7c8ad5a37d77db13985508aa04d4ee006df4adde8414921760ecd4b642c5/bids_validator_deno-2.1.1-py2.py3-none-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:06a06a27b07a237bcb4eecd09bf656fd8eafb61c77d0f4ae311807db0ac6dba5", size = 43321256, upload-time = "2025-09-29T16:25:13.676Z" }, - { url = "https://files.pythonhosted.org/packages/a8/cf/92cd8c6507068745c55afa0cad60a6ac85d1bd0a17dec41b93f1eab55a5b/bids_validator_deno-2.1.1-py2.py3-none-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:538914e927fe38c6cd80458acf31a5a4e3ff52020b5b2dfdea42d3bc49aac5e7", size = 44433972, upload-time = "2025-09-29T16:25:16.439Z" }, - { url = "https://files.pythonhosted.org/packages/3b/b4/0f9b960ef336426df7f2db4920a99d10c6f57a29001696a93854aa3da0bf/bids_validator_deno-2.1.1-py2.py3-none-win_amd64.whl", hash = "sha256:b163ed4976ece0cf21f94f6293281687633110e8bd2e8966064f2f0e2ed88302", size = 43848264, upload-time = "2025-09-29T16:25:19.546Z" }, -] - -[[package]] -name = "bidsschematools" -version = "1.1.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "acres" }, - { name = "click" }, - { name = "pyyaml" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/82/5d/8b940b4149ba11e5ed5325a79f3c75a0e2ec66cb4123d3d1313222550929/bidsschematools-1.1.0.tar.gz", hash = "sha256:71555f2364a27ba6ac7bff869b5181ff64aa180c5e56725180b43012627c0e06", size = 1752873, upload-time = "2025-09-03T14:24:36.587Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/00/d6/15f1ea70e9dde2fbd5c1ea83db6169e5663c9b20d4854678650ff5cefad7/bidsschematools-1.1.0-py3-none-any.whl", hash = "sha256:aabd6ae19978a5a92777f04fa0740ca9ea4a746075b077d88671d47d46c56653", size = 179191, upload-time = "2025-09-03T14:24:35.2Z" }, -] - [[package]] name = "billiard" version = "4.2.2" @@ -679,27 +647,21 @@ wheels = [ [[package]] name = "dandi" -version = "0.73.0" +version = "0.17.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "bids-validator-deno" }, - { name = "bidsschematools" }, + { name = "appdirs" }, { name = "click" }, { name = "click-didyoumean" }, - { name = "dandischema" }, + { name = "email-validator" }, { name = "etelemetry" }, { name = "fasteners" }, { name = "fscacher" }, - { name = "hdmf" }, { name = "humanize" }, - { name = "interleave" }, { name = "joblib" }, + { name = "jsonschema" }, { name = "keyring" }, { name = "keyrings-alt" }, - { name = "numcodecs" }, - { name = "nwbinspector" }, - { name = "packaging" }, - { name = "platformdirs" }, { name = "pycryptodomex" }, { name = "pydantic" }, { name = "pynwb" }, @@ -709,15 +671,11 @@ dependencies = [ { name = "ruamel-yaml" }, { name = "semantic-version" }, { name = "tenacity" }, - { name = "tensorstore" }, - { name = "urllib3" }, - { name = "yarl" }, - { name = "zarr" }, - { name = "zarr-checksum" }, + { name = "tqdm" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/9e/4c/45f771706639b52aea3c48c7a9dbf5ef3bea27b28d90082a82eed36d96c9/dandi-0.73.0.tar.gz", hash = "sha256:5d3eec4b2bb3bfe516d3457609d97860a97532f4c87ea1c15b71c3b83a1c273a", size = 358133, upload-time = "2025-10-13T17:29:02.207Z" } +sdist = { url = "https://files.pythonhosted.org/packages/81/e0/185a68af1a5e20564e654984b24c7f4e7d42e41d2e038f13d558ad5b1523/dandi-0.17.0.tar.gz", hash = "sha256:5198c9087b1fab0767be8314e382637bfd75ee61092419ce00dd3a341816b99d", size = 143395, upload-time = "2021-05-12T20:54:10.065Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/3f/8d/e0ae1893b6a052e561fb9b6b29c074e8696c52eb849813768a04a3d266ca/dandi-0.73.0-py3-none-any.whl", hash = "sha256:fd5680fa661c056242e95827cb80fd811f66d26dd9c3d5da2eaea22b14cfd43c", size = 362954, upload-time = "2025-10-13T17:29:00.559Z" }, + { url = "https://files.pythonhosted.org/packages/a8/f1/e4ed51dc07f5ccfd44d8a1e2d940b375dc8c3abbb119fd78fdd051d00036/dandi-0.17.0-py3-none-any.whl", hash = "sha256:0dc9d9cabcf083e2169a4fc70c0d1f8ea60762a4dac66f8e27ed6e778f628c67", size = 152512, upload-time = "2021-05-12T20:54:08.554Z" }, ] [[package]] @@ -809,7 +767,7 @@ requires-dist = [ { name = "boto3" }, { name = "celery" }, { name = "dandi" }, - { name = "dandischema", specifier = "==0.11.1" }, + { name = "dandischema", specifier = "==0.12.0" }, { name = "django", extras = ["argon2"] }, { name = "django-allauth" }, { name = "django-auth-style" }, @@ -881,17 +839,19 @@ type = [ [[package]] name = "dandischema" -version = "0.11.1" +version = "0.12.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jsonschema", extra = ["format"] }, + { name = "packaging" }, { name = "pydantic", extra = ["email"] }, + { name = "pydantic-settings" }, { name = "requests" }, { name = "zarr-checksum" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/83/c3/68b59ef8f7d49904af4aa4ea730364f3eb02b94cceaaadf128048cbd6370/dandischema-0.11.1.tar.gz", hash = "sha256:662ca01ec3bd81ca70becc98727b0aadd5b116a40ad257d11216acab0d8dda66", size = 74714, upload-time = "2025-05-15T13:38:37.957Z" } +sdist = { url = "https://files.pythonhosted.org/packages/74/9e/1747cc720270d0add1dde5ce1b6ef5107df796eb59eea91ec520bd40c696/dandischema-0.12.0.tar.gz", hash = "sha256:66542da2341440f93e08510cd544b331b8959b5420338b33f32fb2c59f95a98b", size = 98740, upload-time = "2025-11-20T23:06:32.603Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a2/a6/ea4542a2897f8e0438621bc44cd462021c737c84be85e1964f8625ce6509/dandischema-0.11.1-py3-none-any.whl", hash = "sha256:ac80c29a8dad7b4df2e3c7edd5a830fcb31acabe75c78a4b7323097e136d040f", size = 85309, upload-time = "2025-05-15T13:38:36.83Z" }, + { url = "https://files.pythonhosted.org/packages/5f/7e/de6eee89ee6cbe0a107159090a1da66d616fbdbd5de15fa72927f80af60c/dandischema-0.12.0-py3-none-any.whl", hash = "sha256:be78a49644bb56913966d478ad2d5289ef0c72522592c3c149bb34c51c964b3f", size = 118310, upload-time = "2025-11-20T23:06:30.678Z" }, ] [[package]] @@ -903,18 +863,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" }, ] -[[package]] -name = "deprecated" -version = "1.2.18" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "wrapt" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/98/97/06afe62762c9a8a86af0cfb7bfdab22a43ad17138b07af5b1a58442690a2/deprecated-1.2.18.tar.gz", hash = "sha256:422b6f6d859da6f2ef57857761bfb392480502a64c3028ca9bbe86085d72115d", size = 2928744, upload-time = "2025-01-27T10:46:25.7Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/6e/c6/ac0b6c1e2d138f1002bcf799d330bd6d85084fece321e662a14223794041/Deprecated-1.2.18-py2.py3-none-any.whl", hash = "sha256:bd5011788200372a32418f888e326a09ff80d0214bd961147cfed01b5c018eec", size = 9998, upload-time = "2025-01-27T10:46:09.186Z" }, -] - [[package]] name = "distlib" version = "0.4.0" @@ -1512,23 +1460,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d2/e4/a5c84425feece1383ede342086720f8eba4a67e6afc2e4f990d2ce1d7e94/hdmf-4.1.0-py3-none-any.whl", hash = "sha256:d0cffd8e573d77368892d20351ecba07142a8a21b75c7665d732748132d7a696", size = 336653, upload-time = "2025-05-28T19:02:09.808Z" }, ] -[[package]] -name = "hdmf-zarr" -version = "0.12.0" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "hdmf" }, - { name = "numcodecs" }, - { name = "numpy" }, - { name = "pynwb" }, - { name = "threadpoolctl" }, - { name = "zarr" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9e/56/707bafae46727e0d69030fac436b96fb4ea8fefc693e661474ed45d12987/hdmf_zarr-0.12.0.tar.gz", hash = "sha256:9fa413fcddfc12e825b99f06ebdbc35439c6fad9f00a44936961b9ffd684e0c0", size = 3106640, upload-time = "2025-10-08T18:27:38.921Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/96/74/eeff5039149201bead00c27fab2ddf071899307bd578c3eabcd069ba4eba/hdmf_zarr-0.12.0-py3-none-any.whl", hash = "sha256:aaf866122799f547b9594f54fe906e2ffc4e8b9fe1928bf15a037dfcc864c98b", size = 33787, upload-time = "2025-10-08T18:27:37.44Z" }, -] - [[package]] name = "humanize" version = "4.13.0" @@ -1574,15 +1505,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, ] -[[package]] -name = "interleave" -version = "0.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/cb/73/f06bcda00889d164ee1918126381f2d5e0030a6deaf80c9887e84ba18e31/interleave-0.3.0.tar.gz", hash = "sha256:4c4595e05c5c3286d306db7459972bada9a38088fb8649c1199782c8e4a60a32", size = 15689, upload-time = "2025-01-28T19:48:04.286Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/2b/13b5b2e418bc77574b7215c26be862f72d7850713116641c4ad56add61fd/interleave-0.3.0-py3-none-any.whl", hash = "sha256:09d772693b958c0cbb11e3cd8a6b726c6995c7e12b0530a10ae31a943b458c39", size = 11847, upload-time = "2025-01-28T19:48:03.081Z" }, -] - [[package]] name = "ipython" version = "9.6.0" @@ -1616,15 +1538,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c", size = 8074, upload-time = "2025-01-17T11:24:33.271Z" }, ] -[[package]] -name = "isodate" -version = "0.7.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/54/4d/e940025e2ce31a8ce1202635910747e5a87cc3a6a6bb2d00973375014749/isodate-0.7.2.tar.gz", hash = "sha256:4cd1aa0f43ca76f4a6c6c0292a85f40b35ec2e43e315b59f06e6d32171a953e6", size = 29705, upload-time = "2024-10-08T23:04:11.5Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/15/aa/0aca39a37d3c7eb941ba736ede56d689e7be91cab5d9ca846bde3999eba6/isodate-0.7.2-py3-none-any.whl", hash = "sha256:28009937d8031054830160fce6d409ed342816b543597cece116d966c6d99e15", size = 22320, upload-time = "2024-10-08T23:04:09.501Z" }, -] - [[package]] name = "isoduration" version = "20.11.0" @@ -1975,33 +1888,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/aa/d5/ecfa0a06ab0f0bd060a5908edc839d81c8f37da671a69d19cf9f5773595e/memray-1.18.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:45c3ac6152915df66ff7df4602a82171b981efcfba89b132458808cd47a57f7f", size = 10258751, upload-time = "2025-08-08T19:47:53.955Z" }, ] -[[package]] -name = "ml-dtypes" -version = "0.5.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "numpy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/78/a7/aad060393123cfb383956dca68402aff3db1e1caffd5764887ed5153f41b/ml_dtypes-0.5.3.tar.gz", hash = "sha256:95ce33057ba4d05df50b1f3cfefab22e351868a843b3b15a46c65836283670c9", size = 692316, upload-time = "2025-07-29T18:39:19.454Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/2d/87/1bcc98a66de7b2455dfb292f271452cac9edc4e870796e0d87033524d790/ml_dtypes-0.5.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:5103856a225465371fe119f2fef737402b705b810bd95ad5f348e6e1a6ae21af", size = 663781, upload-time = "2025-07-29T18:38:42.984Z" }, - { url = "https://files.pythonhosted.org/packages/fd/2c/bd2a79ba7c759ee192b5601b675b180a3fd6ccf48ffa27fe1782d280f1a7/ml_dtypes-0.5.3-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4cae435a68861660af81fa3c5af16b70ca11a17275c5b662d9c6f58294e0f113", size = 4956217, upload-time = "2025-07-29T18:38:44.65Z" }, - { url = "https://files.pythonhosted.org/packages/14/f3/091ba84e5395d7fe5b30c081a44dec881cd84b408db1763ee50768b2ab63/ml_dtypes-0.5.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6936283b56d74fbec431ca57ce58a90a908fdbd14d4e2d22eea6d72bb208a7b7", size = 4933109, upload-time = "2025-07-29T18:38:46.405Z" }, - { url = "https://files.pythonhosted.org/packages/bc/24/054036dbe32c43295382c90a1363241684c4d6aaa1ecc3df26bd0c8d5053/ml_dtypes-0.5.3-cp313-cp313-win_amd64.whl", hash = "sha256:d0f730a17cf4f343b2c7ad50cee3bd19e969e793d2be6ed911f43086460096e4", size = 208187, upload-time = "2025-07-29T18:38:48.24Z" }, - { url = "https://files.pythonhosted.org/packages/a6/3d/7dc3ec6794a4a9004c765e0c341e32355840b698f73fd2daff46f128afc1/ml_dtypes-0.5.3-cp313-cp313-win_arm64.whl", hash = "sha256:2db74788fc01914a3c7f7da0763427280adfc9cd377e9604b6b64eb8097284bd", size = 161559, upload-time = "2025-07-29T18:38:50.493Z" }, - { url = "https://files.pythonhosted.org/packages/12/91/e6c7a0d67a152b9330445f9f0cf8ae6eee9b83f990b8c57fe74631e42a90/ml_dtypes-0.5.3-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:93c36a08a6d158db44f2eb9ce3258e53f24a9a4a695325a689494f0fdbc71770", size = 689321, upload-time = "2025-07-29T18:38:52.03Z" }, - { url = "https://files.pythonhosted.org/packages/9e/6c/b7b94b84a104a5be1883305b87d4c6bd6ae781504474b4cca067cb2340ec/ml_dtypes-0.5.3-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0e44a3761f64bc009d71ddb6d6c71008ba21b53ab6ee588dadab65e2fa79eafc", size = 5274495, upload-time = "2025-07-29T18:38:53.797Z" }, - { url = "https://files.pythonhosted.org/packages/5b/38/6266604dffb43378055394ea110570cf261a49876fc48f548dfe876f34cc/ml_dtypes-0.5.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bdf40d2aaabd3913dec11840f0d0ebb1b93134f99af6a0a4fd88ffe924928ab4", size = 5285422, upload-time = "2025-07-29T18:38:56.603Z" }, - { url = "https://files.pythonhosted.org/packages/7c/88/8612ff177d043a474b9408f0382605d881eeb4125ba89d4d4b3286573a83/ml_dtypes-0.5.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:aec640bd94c4c85c0d11e2733bd13cbb10438fb004852996ec0efbc6cacdaf70", size = 661182, upload-time = "2025-07-29T18:38:58.414Z" }, - { url = "https://files.pythonhosted.org/packages/6f/2b/0569a5e88b29240d373e835107c94ae9256fb2191d3156b43b2601859eff/ml_dtypes-0.5.3-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bda32ce212baa724e03c68771e5c69f39e584ea426bfe1a701cb01508ffc7035", size = 4956187, upload-time = "2025-07-29T18:39:00.611Z" }, - { url = "https://files.pythonhosted.org/packages/51/66/273c2a06ae44562b104b61e6b14444da00061fd87652506579d7eb2c40b1/ml_dtypes-0.5.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c205cac07d24a29840c163d6469f61069ce4b065518519216297fc2f261f8db9", size = 4930911, upload-time = "2025-07-29T18:39:02.405Z" }, - { url = "https://files.pythonhosted.org/packages/93/ab/606be3e87dc0821bd360c8c1ee46108025c31a4f96942b63907bb441b87d/ml_dtypes-0.5.3-cp314-cp314-win_amd64.whl", hash = "sha256:cd7c0bb22d4ff86d65ad61b5dd246812e8993fbc95b558553624c33e8b6903ea", size = 216664, upload-time = "2025-07-29T18:39:03.927Z" }, - { url = "https://files.pythonhosted.org/packages/30/a2/e900690ca47d01dffffd66375c5de8c4f8ced0f1ef809ccd3b25b3e6b8fa/ml_dtypes-0.5.3-cp314-cp314-win_arm64.whl", hash = "sha256:9d55ea7f7baf2aed61bf1872116cefc9d0c3693b45cae3916897ee27ef4b835e", size = 160203, upload-time = "2025-07-29T18:39:05.671Z" }, - { url = "https://files.pythonhosted.org/packages/53/21/783dfb51f40d2660afeb9bccf3612b99f6a803d980d2a09132b0f9d216ab/ml_dtypes-0.5.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:e12e29764a0e66a7a31e9b8bf1de5cc0423ea72979f45909acd4292de834ccd3", size = 689324, upload-time = "2025-07-29T18:39:07.567Z" }, - { url = "https://files.pythonhosted.org/packages/09/f7/a82d249c711abf411ac027b7163f285487f5e615c3e0716c61033ce996ab/ml_dtypes-0.5.3-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:19f6c3a4f635c2fc9e2aa7d91416bd7a3d649b48350c51f7f715a09370a90d93", size = 5275917, upload-time = "2025-07-29T18:39:09.339Z" }, - { url = "https://files.pythonhosted.org/packages/7f/3c/541c4b30815ab90ebfbb51df15d0b4254f2f9f1e2b4907ab229300d5e6f2/ml_dtypes-0.5.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5ab039ffb40f3dc0aeeeba84fd6c3452781b5e15bef72e2d10bcb33e4bbffc39", size = 5285284, upload-time = "2025-07-29T18:39:11.532Z" }, -] - [[package]] name = "more-itertools" version = "10.8.0" @@ -2136,15 +2022,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, ] -[[package]] -name = "natsort" -version = "8.4.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/e2/a9/a0c57aee75f77794adaf35322f8b6404cbd0f89ad45c87197a937764b7d0/natsort-8.4.0.tar.gz", hash = "sha256:45312c4a0e5507593da193dedd04abb1469253b601ecaf63445ad80f0a1ea581", size = 76575, upload-time = "2023-06-20T04:17:19.925Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ef/82/7a9d0550484a62c6da82858ee9419f3dd1ccc9aa1c26a1e43da3ecd20b0d/natsort-8.4.0-py3-none-any.whl", hash = "sha256:4732914fb471f56b5cce04d7bae6f164a592c7712e1c85f9ef585e197299521c", size = 38268, upload-time = "2023-06-20T04:17:17.522Z" }, -] - [[package]] name = "nodeenv" version = "1.9.1" @@ -2154,22 +2031,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d2/1d/1b658dbd2b9fa9c4c9f32accbfc0205d532c8c6194dc0f2a4c0428e7128a/nodeenv-1.9.1-py2.py3-none-any.whl", hash = "sha256:ba11c9782d29c27c70ffbdda2d7415098754709be8a7056d79a737cd901155c9", size = 22314, upload-time = "2024-06-04T18:44:08.352Z" }, ] -[[package]] -name = "numcodecs" -version = "0.15.1" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "deprecated" }, - { name = "numpy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/63/fc/bb532969eb8236984ba65e4f0079a7da885b8ac0ce1f0835decbb3938a62/numcodecs-0.15.1.tar.gz", hash = "sha256:eeed77e4d6636641a2cc605fbc6078c7a8f2cc40f3dfa2b3f61e52e6091b04ff", size = 6267275, upload-time = "2025-02-10T10:23:33.254Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/78/57/acbc54b3419e5be65015e47177c76c0a73e037fd3ae2cde5808169194d4d/numcodecs-0.15.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e3d82b70500cf61e8d115faa0d0a76be6ecdc24a16477ee3279d711699ad85f3", size = 1688220, upload-time = "2025-02-10T10:23:23.79Z" }, - { url = "https://files.pythonhosted.org/packages/b6/56/9863fa6dc679f40a31bea5e9713ee5507a31dcd3ee82ea4b1a9268ce52e8/numcodecs-0.15.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1d471a1829ce52d3f365053a2bd1379e32e369517557c4027ddf5ac0d99c591e", size = 1180294, upload-time = "2025-02-10T10:23:25.533Z" }, - { url = "https://files.pythonhosted.org/packages/fa/91/d96999b41e3146b6c0ce6bddc5ad85803cb4d743c95394562c2a4bb8cded/numcodecs-0.15.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1dfdea4a67108205edfce99c1cb6cd621343bc7abb7e16a041c966776920e7de", size = 8834323, upload-time = "2025-02-10T10:23:27.46Z" }, - { url = "https://files.pythonhosted.org/packages/c3/32/233e5ede6568bdb044e6f99aaa9fa39827ff3109c6487fc137315f733586/numcodecs-0.15.1-cp313-cp313-win_amd64.whl", hash = "sha256:a4f7bdb26f1b34423cb56d48e75821223be38040907c9b5954eeb7463e7eb03c", size = 831955, upload-time = "2025-02-10T10:23:30.601Z" }, -] - [[package]] name = "numpy" version = "2.3.3" @@ -2222,29 +2083,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/06/b9/33bba5ff6fb679aa0b1f8a07e853f002a6b04b9394db3069a1270a7784ca/numpy-2.3.3-cp314-cp314t-win_arm64.whl", hash = "sha256:78c9f6560dc7e6b3990e32df7ea1a50bbd0e2a111e05209963f5ddcab7073b0b", size = 10545953, upload-time = "2025-09-09T15:58:40.576Z" }, ] -[[package]] -name = "nwbinspector" -version = "0.6.5" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "aiohttp" }, - { name = "click" }, - { name = "fsspec" }, - { name = "hdmf-zarr" }, - { name = "isodate" }, - { name = "jsonschema" }, - { name = "natsort" }, - { name = "packaging" }, - { name = "pynwb" }, - { name = "pyyaml" }, - { name = "requests" }, - { name = "tqdm" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/45/23/d50bb1dfef6eff622dff58ec64077f6b51ce9191ee4f093881b1394a6ddd/nwbinspector-0.6.5.tar.gz", hash = "sha256:93023bcd72c73add18644211fb26f6b005e012ec9c6a8b3f53e136ddeb58af16", size = 591999, upload-time = "2025-07-25T18:05:22.529Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8f/e7/01fd709430ee0178db611f1db9652bfca71599799c602101e13ce5bdeedc/nwbinspector-0.6.5-py3-none-any.whl", hash = "sha256:f0219a9b06482b07415e7beebddf3ae7d97371cc178e7bc05c7d3edf7285d135", size = 64409, upload-time = "2025-07-25T18:05:21.4Z" }, -] - [[package]] name = "oauthlib" version = "3.3.1" @@ -2616,6 +2454,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8a/ac/9fc61b4f9d079482a290afe8d206b8f490e9fd32d4fc03ed4fc698214e01/pydantic_core-2.41.4-cp314-cp314t-win_arm64.whl", hash = "sha256:d34f950ae05a83e0ede899c595f312ca976023ea1db100cd5aa188f7005e3ab0", size = 1973897, upload-time = "2025-10-14T10:22:13.444Z" }, ] +[[package]] +name = "pydantic-settings" +version = "2.12.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pydantic" }, + { name = "python-dotenv" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/43/4b/ac7e0aae12027748076d72a8764ff1c9d82ca75a7a52622e67ed3f765c54/pydantic_settings-2.12.0.tar.gz", hash = "sha256:005538ef951e3c2a68e1c08b292b5f2e71490def8589d4221b95dab00dafcfd0", size = 194184, upload-time = "2025-11-10T14:25:47.013Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/60/5d4751ba3f4a40a6891f24eec885f51afd78d208498268c734e256fb13c4/pydantic_settings-2.12.0-py3-none-any.whl", hash = "sha256:fddb9fd99a5b18da837b29710391e945b1e30c135477f484084ee513adb93809", size = 51880, upload-time = "2025-11-10T14:25:45.546Z" }, +] + [[package]] name = "pygments" version = "2.19.2" @@ -2786,6 +2638,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, ] +[[package]] +name = "python-dotenv" +version = "1.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f0/26/19cadc79a718c5edbec86fd4919a6b6d3f681039a2f6d66d14be94e75fb9/python_dotenv-1.2.1.tar.gz", hash = "sha256:42667e897e16ab0d66954af0e60a9caa94f0fd4ecf3aaf6d2d260eec1aa36ad6", size = 44221, upload-time = "2025-10-26T15:12:10.434Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/1b/a298b06749107c305e1fe0f814c6c74aea7b2f1e10989cb30f544a1b3253/python_dotenv-1.2.1-py3-none-any.whl", hash = "sha256:b81ee9561e9ca4004139c6cbba3a238c32b03e4894671e181b671e8cb8425d61", size = 21230, upload-time = "2025-10-26T15:12:09.109Z" }, +] + [[package]] name = "pytz" version = "2025.2" @@ -3000,6 +2861,8 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6b/fa/3234f913fe9a6525a7b97c6dad1f51e72b917e6872e051a5e2ffd8b16fbb/ruamel.yaml.clib-0.2.14-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:70eda7703b8126f5e52fcf276e6c0f40b0d314674f896fc58c47b0aef2b9ae83", size = 137970, upload-time = "2025-09-22T19:51:09.472Z" }, { url = "https://files.pythonhosted.org/packages/ef/ec/4edbf17ac2c87fa0845dd366ef8d5852b96eb58fcd65fc1ecf5fe27b4641/ruamel.yaml.clib-0.2.14-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:a0cb71ccc6ef9ce36eecb6272c81afdc2f565950cdcec33ae8e6cd8f7fc86f27", size = 739639, upload-time = "2025-09-22T19:51:10.566Z" }, { url = "https://files.pythonhosted.org/packages/15/18/b0e1fafe59051de9e79cdd431863b03593ecfa8341c110affad7c8121efc/ruamel.yaml.clib-0.2.14-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e7cb9ad1d525d40f7d87b6df7c0ff916a66bc52cb61b66ac1b2a16d0c1b07640", size = 764456, upload-time = "2025-09-22T19:51:11.736Z" }, + { url = "https://files.pythonhosted.org/packages/e7/cd/150fdb96b8fab27fe08d8a59fe67554568727981806e6bc2677a16081ec7/ruamel_yaml_clib-0.2.14-cp314-cp314-win32.whl", hash = "sha256:9b4104bf43ca0cd4e6f738cb86326a3b2f6eef00f417bd1e7efb7bdffe74c539", size = 102394, upload-time = "2025-11-14T21:57:36.703Z" }, + { url = "https://files.pythonhosted.org/packages/bd/e6/a3fa40084558c7e1dc9546385f22a93949c890a8b2e445b2ba43935f51da/ruamel_yaml_clib-0.2.14-cp314-cp314-win_amd64.whl", hash = "sha256:13997d7d354a9890ea1ec5937a219817464e5cc344805b37671562a401ca3008", size = 122673, upload-time = "2025-11-14T21:57:38.177Z" }, ] [[package]] @@ -3045,8 +2908,8 @@ name = "secretstorage" version = "3.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "cryptography" }, - { name = "jeepney" }, + { name = "cryptography", marker = "(python_full_version < '3.14' and platform_machine != 'arm64') or platform_machine == 'arm64' or sys_platform != 'darwin'" }, + { name = "jeepney", marker = "(python_full_version < '3.14' and platform_machine != 'arm64') or platform_machine == 'arm64' or sys_platform != 'darwin'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/31/9f/11ef35cf1027c1339552ea7bfe6aaa74a8516d8b5caf6e7d338daf54fd80/secretstorage-3.4.0.tar.gz", hash = "sha256:c46e216d6815aff8a8a18706a2fbfd8d53fcbb0dce99301881687a1b0289ef7c", size = 19748, upload-time = "2025-09-09T16:42:13.859Z" } wheels = [ @@ -3129,23 +2992,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e5/30/643397144bfbfec6f6ef821f36f33e57d35946c44a2352d3c9f0ae847619/tenacity-9.1.2-py3-none-any.whl", hash = "sha256:f77bf36710d8b73a50b2dd155c97b870017ad21afe6ab300326b0371b3b05138", size = 28248, upload-time = "2025-04-02T08:25:07.678Z" }, ] -[[package]] -name = "tensorstore" -version = "0.1.78" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "ml-dtypes" }, - { name = "numpy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/9f/ee/05eb424437f4db63331c90e4605025eedc0f71da3faff97161d5d7b405af/tensorstore-0.1.78.tar.gz", hash = "sha256:e26074ffe462394cf54197eb76d6569b500f347573cd74da3f4dd5f510a4ad7c", size = 6913502, upload-time = "2025-10-06T17:44:29.649Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/18/36/cfb5a2acf9005896c88f80b93c2aee42f00fab9d0045369fef6e1b297242/tensorstore-0.1.78-cp313-cp313-macosx_10_14_x86_64.whl", hash = "sha256:8a1d0ae7996c80f2e623be5b8cfbc32a307d08dfef3d2dcb455f592908ecd46d", size = 15727334, upload-time = "2025-10-06T17:44:19.93Z" }, - { url = "https://files.pythonhosted.org/packages/54/cd/d1bcc3aab5be4298616dbc060b5aa2012b686270aaa16a9579c7945d0a1c/tensorstore-0.1.78-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:311846cfb2d644cd4a7861005e521a79816093e76d7924c83de5d06ca323067e", size = 13780722, upload-time = "2025-10-06T17:44:21.822Z" }, - { url = "https://files.pythonhosted.org/packages/e2/3b/b0bb4440a9d67859b1abb367e436c62b0a27991dd7109f20be9dabff488f/tensorstore-0.1.78-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:630538a66eb9964bd2975c4e09ae83be9984f2e4ebd5f7969983137bfda92071", size = 18157269, upload-time = "2025-10-06T17:44:23.743Z" }, - { url = "https://files.pythonhosted.org/packages/68/d6/d95cde18ca2475bf317051b2be168cc963c5cfcd67e9c59786326ccdca53/tensorstore-0.1.78-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6886bec93b8ba22f83c4dc9e7c1ee20b11025ea9a5a839de21d0cbf7fd7aada2", size = 20060053, upload-time = "2025-10-06T17:44:25.942Z" }, - { url = "https://files.pythonhosted.org/packages/db/a2/dbd1af0e97d5d549051309d72c6e3f2fe81fae636f9db3692d21adc9c731/tensorstore-0.1.78-cp313-cp313-win_amd64.whl", hash = "sha256:e0073de8fa3074bc4cc92ced0210310fd89851899faf42a5ba256f0ba87d095c", size = 12711250, upload-time = "2025-10-06T17:44:27.926Z" }, -] - [[package]] name = "textual" version = "6.3.0" @@ -3162,15 +3008,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/ff/2a/bca677b0b05ee77b4105f73db0d8ef231a9f1db154d69388abd5c73f9dcc/textual-6.3.0-py3-none-any.whl", hash = "sha256:ec908b4b008662e7670af4a3e7c773847066b0950b1c50126c72fa939b514c97", size = 711457, upload-time = "2025-10-11T11:16:59.754Z" }, ] -[[package]] -name = "threadpoolctl" -version = "3.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b7/4d/08c89e34946fce2aec4fbb45c9016efd5f4d7f24af8e5d93296e935631d8/threadpoolctl-3.6.0.tar.gz", hash = "sha256:8ab8b4aa3491d812b623328249fab5302a68d2d71745c8a4c719a2fcaba9f44e", size = 21274, upload-time = "2025-03-13T13:49:23.031Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/32/d5/f9a850d79b0851d1d4ef6456097579a9005b31fea68726a4ae5f2d82ddd9/threadpoolctl-3.6.0-py3-none-any.whl", hash = "sha256:43a0b8fd5a2928500110039e43a5eed8480b918967083ea48dc3ab9f13c4a7fb", size = 18638, upload-time = "2025-03-13T13:49:21.846Z" }, -] - [[package]] name = "tox" version = "4.31.0" @@ -3454,45 +3291,6 @@ brotli = [ { name = "brotli" }, ] -[[package]] -name = "wrapt" -version = "1.17.3" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/95/8f/aeb76c5b46e273670962298c23e7ddde79916cb74db802131d49a85e4b7d/wrapt-1.17.3.tar.gz", hash = "sha256:f66eb08feaa410fe4eebd17f2a2c8e2e46d3476e9f8c783daa8e09e0faa666d0", size = 55547, upload-time = "2025-08-12T05:53:21.714Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fc/f6/759ece88472157acb55fc195e5b116e06730f1b651b5b314c66291729193/wrapt-1.17.3-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a47681378a0439215912ef542c45a783484d4dd82bac412b71e59cf9c0e1cea0", size = 54003, upload-time = "2025-08-12T05:51:48.627Z" }, - { url = "https://files.pythonhosted.org/packages/4f/a9/49940b9dc6d47027dc850c116d79b4155f15c08547d04db0f07121499347/wrapt-1.17.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:54a30837587c6ee3cd1a4d1c2ec5d24e77984d44e2f34547e2323ddb4e22eb77", size = 39025, upload-time = "2025-08-12T05:51:37.156Z" }, - { url = "https://files.pythonhosted.org/packages/45/35/6a08de0f2c96dcdd7fe464d7420ddb9a7655a6561150e5fc4da9356aeaab/wrapt-1.17.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:16ecf15d6af39246fe33e507105d67e4b81d8f8d2c6598ff7e3ca1b8a37213f7", size = 39108, upload-time = "2025-08-12T05:51:58.425Z" }, - { url = "https://files.pythonhosted.org/packages/0c/37/6faf15cfa41bf1f3dba80cd3f5ccc6622dfccb660ab26ed79f0178c7497f/wrapt-1.17.3-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6fd1ad24dc235e4ab88cda009e19bf347aabb975e44fd5c2fb22a3f6e4141277", size = 88072, upload-time = "2025-08-12T05:52:37.53Z" }, - { url = "https://files.pythonhosted.org/packages/78/f2/efe19ada4a38e4e15b6dff39c3e3f3f73f5decf901f66e6f72fe79623a06/wrapt-1.17.3-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ed61b7c2d49cee3c027372df5809a59d60cf1b6c2f81ee980a091f3afed6a2d", size = 88214, upload-time = "2025-08-12T05:52:15.886Z" }, - { url = "https://files.pythonhosted.org/packages/40/90/ca86701e9de1622b16e09689fc24b76f69b06bb0150990f6f4e8b0eeb576/wrapt-1.17.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:423ed5420ad5f5529db9ce89eac09c8a2f97da18eb1c870237e84c5a5c2d60aa", size = 87105, upload-time = "2025-08-12T05:52:17.914Z" }, - { url = "https://files.pythonhosted.org/packages/fd/e0/d10bd257c9a3e15cbf5523025252cc14d77468e8ed644aafb2d6f54cb95d/wrapt-1.17.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e01375f275f010fcbf7f643b4279896d04e571889b8a5b3f848423d91bf07050", size = 87766, upload-time = "2025-08-12T05:52:39.243Z" }, - { url = "https://files.pythonhosted.org/packages/e8/cf/7d848740203c7b4b27eb55dbfede11aca974a51c3d894f6cc4b865f42f58/wrapt-1.17.3-cp313-cp313-win32.whl", hash = "sha256:53e5e39ff71b3fc484df8a522c933ea2b7cdd0d5d15ae82e5b23fde87d44cbd8", size = 36711, upload-time = "2025-08-12T05:53:10.074Z" }, - { url = "https://files.pythonhosted.org/packages/57/54/35a84d0a4d23ea675994104e667ceff49227ce473ba6a59ba2c84f250b74/wrapt-1.17.3-cp313-cp313-win_amd64.whl", hash = "sha256:1f0b2f40cf341ee8cc1a97d51ff50dddb9fcc73241b9143ec74b30fc4f44f6cb", size = 38885, upload-time = "2025-08-12T05:53:08.695Z" }, - { url = "https://files.pythonhosted.org/packages/01/77/66e54407c59d7b02a3c4e0af3783168fff8e5d61def52cda8728439d86bc/wrapt-1.17.3-cp313-cp313-win_arm64.whl", hash = "sha256:7425ac3c54430f5fc5e7b6f41d41e704db073309acfc09305816bc6a0b26bb16", size = 36896, upload-time = "2025-08-12T05:52:55.34Z" }, - { url = "https://files.pythonhosted.org/packages/02/a2/cd864b2a14f20d14f4c496fab97802001560f9f41554eef6df201cd7f76c/wrapt-1.17.3-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cf30f6e3c077c8e6a9a7809c94551203c8843e74ba0c960f4a98cd80d4665d39", size = 54132, upload-time = "2025-08-12T05:51:49.864Z" }, - { url = "https://files.pythonhosted.org/packages/d5/46/d011725b0c89e853dc44cceb738a307cde5d240d023d6d40a82d1b4e1182/wrapt-1.17.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:e228514a06843cae89621384cfe3a80418f3c04aadf8a3b14e46a7be704e4235", size = 39091, upload-time = "2025-08-12T05:51:38.935Z" }, - { url = "https://files.pythonhosted.org/packages/2e/9e/3ad852d77c35aae7ddebdbc3b6d35ec8013af7d7dddad0ad911f3d891dae/wrapt-1.17.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:5ea5eb3c0c071862997d6f3e02af1d055f381b1d25b286b9d6644b79db77657c", size = 39172, upload-time = "2025-08-12T05:51:59.365Z" }, - { url = "https://files.pythonhosted.org/packages/c3/f7/c983d2762bcce2326c317c26a6a1e7016f7eb039c27cdf5c4e30f4160f31/wrapt-1.17.3-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:281262213373b6d5e4bb4353bc36d1ba4084e6d6b5d242863721ef2bf2c2930b", size = 87163, upload-time = "2025-08-12T05:52:40.965Z" }, - { url = "https://files.pythonhosted.org/packages/e4/0f/f673f75d489c7f22d17fe0193e84b41540d962f75fce579cf6873167c29b/wrapt-1.17.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:dc4a8d2b25efb6681ecacad42fca8859f88092d8732b170de6a5dddd80a1c8fa", size = 87963, upload-time = "2025-08-12T05:52:20.326Z" }, - { url = "https://files.pythonhosted.org/packages/df/61/515ad6caca68995da2fac7a6af97faab8f78ebe3bf4f761e1b77efbc47b5/wrapt-1.17.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:373342dd05b1d07d752cecbec0c41817231f29f3a89aa8b8843f7b95992ed0c7", size = 86945, upload-time = "2025-08-12T05:52:21.581Z" }, - { url = "https://files.pythonhosted.org/packages/d3/bd/4e70162ce398462a467bc09e768bee112f1412e563620adc353de9055d33/wrapt-1.17.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d40770d7c0fd5cbed9d84b2c3f2e156431a12c9a37dc6284060fb4bec0b7ffd4", size = 86857, upload-time = "2025-08-12T05:52:43.043Z" }, - { url = "https://files.pythonhosted.org/packages/2b/b8/da8560695e9284810b8d3df8a19396a6e40e7518059584a1a394a2b35e0a/wrapt-1.17.3-cp314-cp314-win32.whl", hash = "sha256:fbd3c8319de8e1dc79d346929cd71d523622da527cca14e0c1d257e31c2b8b10", size = 37178, upload-time = "2025-08-12T05:53:12.605Z" }, - { url = "https://files.pythonhosted.org/packages/db/c8/b71eeb192c440d67a5a0449aaee2310a1a1e8eca41676046f99ed2487e9f/wrapt-1.17.3-cp314-cp314-win_amd64.whl", hash = "sha256:e1a4120ae5705f673727d3253de3ed0e016f7cd78dc463db1b31e2463e1f3cf6", size = 39310, upload-time = "2025-08-12T05:53:11.106Z" }, - { url = "https://files.pythonhosted.org/packages/45/20/2cda20fd4865fa40f86f6c46ed37a2a8356a7a2fde0773269311f2af56c7/wrapt-1.17.3-cp314-cp314-win_arm64.whl", hash = "sha256:507553480670cab08a800b9463bdb881b2edeed77dc677b0a5915e6106e91a58", size = 37266, upload-time = "2025-08-12T05:52:56.531Z" }, - { url = "https://files.pythonhosted.org/packages/77/ed/dd5cf21aec36c80443c6f900449260b80e2a65cf963668eaef3b9accce36/wrapt-1.17.3-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:ed7c635ae45cfbc1a7371f708727bf74690daedc49b4dba310590ca0bd28aa8a", size = 56544, upload-time = "2025-08-12T05:51:51.109Z" }, - { url = "https://files.pythonhosted.org/packages/8d/96/450c651cc753877ad100c7949ab4d2e2ecc4d97157e00fa8f45df682456a/wrapt-1.17.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:249f88ed15503f6492a71f01442abddd73856a0032ae860de6d75ca62eed8067", size = 40283, upload-time = "2025-08-12T05:51:39.912Z" }, - { url = "https://files.pythonhosted.org/packages/d1/86/2fcad95994d9b572db57632acb6f900695a648c3e063f2cd344b3f5c5a37/wrapt-1.17.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a03a38adec8066d5a37bea22f2ba6bbf39fcdefbe2d91419ab864c3fb515454", size = 40366, upload-time = "2025-08-12T05:52:00.693Z" }, - { url = "https://files.pythonhosted.org/packages/64/0e/f4472f2fdde2d4617975144311f8800ef73677a159be7fe61fa50997d6c0/wrapt-1.17.3-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:5d4478d72eb61c36e5b446e375bbc49ed002430d17cdec3cecb36993398e1a9e", size = 108571, upload-time = "2025-08-12T05:52:44.521Z" }, - { url = "https://files.pythonhosted.org/packages/cc/01/9b85a99996b0a97c8a17484684f206cbb6ba73c1ce6890ac668bcf3838fb/wrapt-1.17.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:223db574bb38637e8230eb14b185565023ab624474df94d2af18f1cdb625216f", size = 113094, upload-time = "2025-08-12T05:52:22.618Z" }, - { url = "https://files.pythonhosted.org/packages/25/02/78926c1efddcc7b3aa0bc3d6b33a822f7d898059f7cd9ace8c8318e559ef/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e405adefb53a435f01efa7ccdec012c016b5a1d3f35459990afc39b6be4d5056", size = 110659, upload-time = "2025-08-12T05:52:24.057Z" }, - { url = "https://files.pythonhosted.org/packages/dc/ee/c414501ad518ac3e6fe184753632fe5e5ecacdcf0effc23f31c1e4f7bfcf/wrapt-1.17.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:88547535b787a6c9ce4086917b6e1d291aa8ed914fdd3a838b3539dc95c12804", size = 106946, upload-time = "2025-08-12T05:52:45.976Z" }, - { url = "https://files.pythonhosted.org/packages/be/44/a1bd64b723d13bb151d6cc91b986146a1952385e0392a78567e12149c7b4/wrapt-1.17.3-cp314-cp314t-win32.whl", hash = "sha256:41b1d2bc74c2cac6f9074df52b2efbef2b30bdfe5f40cb78f8ca22963bc62977", size = 38717, upload-time = "2025-08-12T05:53:15.214Z" }, - { url = "https://files.pythonhosted.org/packages/79/d9/7cfd5a312760ac4dd8bf0184a6ee9e43c33e47f3dadc303032ce012b8fa3/wrapt-1.17.3-cp314-cp314t-win_amd64.whl", hash = "sha256:73d496de46cd2cdbdbcce4ae4bcdb4afb6a11234a1df9c085249d55166b95116", size = 41334, upload-time = "2025-08-12T05:53:14.178Z" }, - { url = "https://files.pythonhosted.org/packages/46/78/10ad9781128ed2f99dbc474f43283b13fea8ba58723e98844367531c18e9/wrapt-1.17.3-cp314-cp314t-win_arm64.whl", hash = "sha256:f38e60678850c42461d4202739f9bf1e3a737c7ad283638251e79cc49effb6b6", size = 38471, upload-time = "2025-08-12T05:52:57.784Z" }, - { url = "https://files.pythonhosted.org/packages/1f/f6/a933bd70f98e9cf3e08167fc5cd7aaaca49147e48411c0bd5ae701bb2194/wrapt-1.17.3-py3-none-any.whl", hash = "sha256:7171ae35d2c33d326ac19dd8facb1e82e5fd04ef8c6c0e394d7af55a55051c22", size = 23591, upload-time = "2025-08-12T05:53:20.674Z" }, -] - [[package]] name = "yarl" version = "1.22.0" @@ -3571,21 +3369,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/73/ae/b48f95715333080afb75a4504487cbe142cae1268afc482d06692d605ae6/yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff", size = 46814, upload-time = "2025-10-06T14:12:53.872Z" }, ] -[[package]] -name = "zarr" -version = "2.18.7" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "asciitree" }, - { name = "fasteners", marker = "sys_platform != 'emscripten'" }, - { name = "numcodecs" }, - { name = "numpy" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/da/1d/01cf9e3ab2d85190278efc3fca9f68563de35ae30ee59e7640e3af98abe3/zarr-2.18.7.tar.gz", hash = "sha256:b2b8f66f14dac4af66b180d2338819981b981f70e196c9a66e6bfaa9e59572f5", size = 3604558, upload-time = "2025-04-09T07:59:28.482Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5e/d8/9ffd8c237b3559945bb52103cf0eed64ea098f7b7f573f8d2962ef27b4b2/zarr-2.18.7-py3-none-any.whl", hash = "sha256:ac3dc4033e9ae4e9d7b5e27c97ea3eaf1003cc0a07f010bd83d5134bf8c4b223", size = 211273, upload-time = "2025-04-09T07:59:27.039Z" }, -] - [[package]] name = "zarr-checksum" version = "0.4.7" diff --git a/web/src/components/DandisetList.vue b/web/src/components/DandisetList.vue index bb11b403d..30c9349a6 100644 --- a/web/src/components/DandisetList.vue +++ b/web/src/components/DandisetList.vue @@ -48,7 +48,7 @@ {{ item.dandiset.embargo_status }} - DANDI:{{ item.dandiset.identifier }} + {{ archiveName }}:{{ item.dandiset.identifier }} ยท Contact {{ item.dandiset.contact_person }} ยท @@ -79,11 +79,12 @@