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/CHANGELOG.md b/CHANGELOG.md
index f87676285..e339a7ba4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,39 @@
+# v0.20.0 (Wed Dec 17 2025)
+
+### Release Notes
+
+#### Add vendorization support ([#2584](https://github.com/dandi/dandi-archive/pull/2584))
+
+This PR makes the instance config as defined in `dandischema.conf`, per https://github.com/dandi/dandi-schema/pull/294, available through the `api/info/` endpoint and replaces the hardcoded `DAND:`, the hardcoded RRID, and the supported licenses with the corresponding attributes in schema instance config defined in `dandischema.conf`.
+
+---
+
+#### ๐ Enhancement
+
+- Add vendorization support [#2584](https://github.com/dandi/dandi-archive/pull/2584) ([@candleindark](https://github.com/candleindark) [@jjnesbitt](https://github.com/jjnesbitt))
+
+#### ๐ Bug Fix
+
+- Empty commit for release [#2680](https://github.com/dandi/dandi-archive/pull/2680) ([@jjnesbitt](https://github.com/jjnesbitt))
+- Update management command for schema migration [#2650](https://github.com/dandi/dandi-archive/pull/2650) ([@jjnesbitt](https://github.com/jjnesbitt))
+
+#### ๐ Internal
+
+- Remove `DANDI_SCHEMA_VERSION` as a setting [#2644](https://github.com/dandi/dandi-archive/pull/2644) ([@jjnesbitt](https://github.com/jjnesbitt))
+- Auto approve users from McGill University [#2657](https://github.com/dandi/dandi-archive/pull/2657) ([@kabilar](https://github.com/kabilar))
+- Don't use django setting to store allowed schema versions [#2643](https://github.com/dandi/dandi-archive/pull/2643) ([@jjnesbitt](https://github.com/jjnesbitt))
+- Add create_test_user management command [#2642](https://github.com/dandi/dandi-archive/pull/2642) ([@jjnesbitt](https://github.com/jjnesbitt))
+- Add docker volume for pre-commit cache [#2640](https://github.com/dandi/dandi-archive/pull/2640) ([@mvandenburgh](https://github.com/mvandenburgh))
+
+#### Authors: 4
+
+- Isaac To ([@candleindark](https://github.com/candleindark))
+- Jacob Nesbitt ([@jjnesbitt](https://github.com/jjnesbitt))
+- Kabilar Gunalan ([@kabilar](https://github.com/kabilar))
+- Mike VanDenburgh ([@mvandenburgh](https://github.com/mvandenburgh))
+
+---
+
# v0.19.0 (Thu Nov 13 2025)
### Release Notes
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..becaaf8d9 100644
--- a/dandiapi/api/management/commands/create_dev_dandiset.py
+++ b/dandiapi/api/management/commands/create_dev_dandiset.py
@@ -2,7 +2,8 @@
from uuid import uuid4
-from django.conf import settings
+from dandischema.conf import get_instance_config
+from dandischema.consts import DANDI_SCHEMA_VERSION
from django.contrib.auth.models import User
from django.core.files.uploadedfile import SimpleUploadedFile
import djclick as click
@@ -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
@@ -68,7 +69,7 @@ def create_dev_dandiset(*, name: str, email: str, num_extra_owners: int):
calculate_sha256(blob_id=asset_blob.blob_id)
asset_blob.refresh_from_db()
asset_metadata = {
- 'schemaVersion': settings.DANDI_SCHEMA_VERSION,
+ 'schemaVersion': DANDI_SCHEMA_VERSION,
'encodingFormat': 'text/plain',
'schemaKey': 'Asset',
'path': 'foo/bar.txt',
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,
+ )
+ )
diff --git a/dandiapi/api/management/commands/migrate_version_metadata.py b/dandiapi/api/management/commands/migrate_version_metadata.py
index 9b5417509..060a9486e 100644
--- a/dandiapi/api/management/commands/migrate_version_metadata.py
+++ b/dandiapi/api/management/commands/migrate_version_metadata.py
@@ -1,28 +1,90 @@
from __future__ import annotations
-from dandischema import migrate
+import logging
+
+from dandischema.consts import DANDI_SCHEMA_VERSION
+from dandischema.metadata import migrate
+from django.db import transaction
import djclick as click
from dandiapi.api.models import Version
+from dandiapi.api.services import audit
+from dandiapi.api.services.metadata import validate_version_metadata
+
+logger = logging.getLogger(__name__)
@click.command()
-@click.argument('to_version')
-def migrate_version_metadata(*, to_version: str):
- click.echo(f'Migrating all version metadata to version {to_version}')
- for version in Version.objects.filter(version='draft'):
- click.echo(f'Migrating {version.dandiset.identifier}/{version.version}')
-
- metadata = version.metadata
-
- try:
- metanew = migrate(metadata, to_version=to_version, skip_validation=True)
- except Exception as e: # noqa: BLE001
- click.echo(f'Failed to migrate {version.dandiset.identifier}/{version.version}')
- click.echo(e)
- continue
-
- if version.metadata != metanew:
+@click.argument(
+ 'dandisets',
+ type=click.INT,
+ nargs=-1,
+)
+@click.option(
+ '-a',
+ '--all',
+ 'include_all',
+ is_flag=True,
+ help='Run on all dandisets.',
+)
+def migrate_version_metadata(dandisets: tuple[int, ...], *, include_all: bool):
+ if bool(dandisets) == include_all:
+ raise click.ClickException("Must specify exactly one of 'dandisets' or --all")
+
+ versions = Version.objects.filter(version='draft')
+ if dandisets:
+ versions = versions.filter(dandiset_id__in=dandisets)
+
+ logger.info(
+ 'Migrating %s dandiset draft versions to schema version %s',
+ versions.count(),
+ DANDI_SCHEMA_VERSION,
+ )
+
+ migrated_count = 0
+ failed_count = 0
+ unchanged_count = 0
+ for version in versions.iterator():
+ logger.info('-----------------------------------------')
+ logger.info('Migrating %s', version)
+
+ with transaction.atomic():
+ locked_version = Version.objects.select_for_update().get(id=version.id)
+
+ try:
+ metanew = migrate(locked_version.metadata, skip_validation=True)
+ except Exception as e:
+ logger.exception('Failed to migrate %s', version, exc_info=e)
+ failed_count += 1
+ continue
+
+ if locked_version.metadata == metanew:
+ logger.info('No change in metadata for %s. Skipping save...', version)
+ unchanged_count += 1
+ continue
+
version.metadata = metanew
version.status = Version.Status.PENDING
version.save()
+
+ audit.update_metadata(
+ dandiset=locked_version.dandiset,
+ metadata=locked_version.metadata,
+ user=None,
+ admin=True,
+ description=f'Update schema version to {DANDI_SCHEMA_VERSION}',
+ )
+
+ migrated_count += 1
+ logger.info('Metadata migrated for version %s', version)
+
+ # Validate outside of transaction, since this function uses `select_for_update` itself
+ validate_version_metadata(version=version)
+
+ logger.info(
+ '%d migrated, %d failed, %d left unchanged, out of %d total selected versions',
+ migrated_count,
+ failed_count,
+ unchanged_count,
+ versions.count(),
+ )
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/services/version/metadata.py b/dandiapi/api/services/version/metadata.py
index 198b42756..7c6763fb5 100644
--- a/dandiapi/api/services/version/metadata.py
+++ b/dandiapi/api/services/version/metadata.py
@@ -1,6 +1,6 @@
from __future__ import annotations
-from django.conf import settings
+from dandischema.consts import DANDI_SCHEMA_VERSION
from dandiapi.api.models.version import Version
@@ -18,7 +18,7 @@ def _normalize_version_metadata(raw_version_metadata: dict, name: str, email: st
# not specified in the version_metadata
return {
'schemaKey': 'Dandiset',
- 'schemaVersion': settings.DANDI_SCHEMA_VERSION,
+ 'schemaVersion': DANDI_SCHEMA_VERSION,
'contributor': [
{
'name': name,
diff --git a/dandiapi/api/tests/factories.py b/dandiapi/api/tests/factories.py
index 3fa29f47d..559a35f10 100644
--- a/dandiapi/api/tests/factories.py
+++ b/dandiapi/api/tests/factories.py
@@ -4,8 +4,9 @@
import hashlib
from allauth.socialaccount.models import SocialAccount
+from dandischema.conf import get_instance_config
+from dandischema.consts import DANDI_SCHEMA_VERSION
from dandischema.models import AccessType
-from django.conf import settings
from django.contrib.auth.models import User
import factory
import faker
@@ -113,9 +114,11 @@ 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,
+ 'schemaVersion': DANDI_SCHEMA_VERSION,
'schemaKey': 'Dandiset',
'description': faker.Faker().sentence(),
'access': [
@@ -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
@@ -229,7 +234,7 @@ class Meta:
def metadata(self) -> dict:
metadata = {
**faker.Faker().pydict(value_types=['str', 'float', 'int']),
- 'schemaVersion': settings.DANDI_SCHEMA_VERSION,
+ 'schemaVersion': DANDI_SCHEMA_VERSION,
'encodingFormat': 'application/x-nwb',
'schemaKey': 'Asset',
}
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..ac0428888 100644
--- a/dandiapi/api/tests/test_asset.py
+++ b/dandiapi/api/tests/test_asset.py
@@ -3,6 +3,7 @@
import json
from uuid import uuid4
+from dandischema.consts import DANDI_SCHEMA_VERSION
from dandischema.models import AccessType
from django.conf import settings
from django.db.utils import IntegrityError
@@ -28,7 +29,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 +135,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,
@@ -181,7 +180,7 @@ def test_asset_total_size(asset_factory, asset_blob_factory, zarr_archive_factor
def test_asset_full_metadata(draft_asset_factory):
raw_metadata = {
'foo': 'bar',
- 'schemaVersion': settings.DANDI_SCHEMA_VERSION,
+ 'schemaVersion': DANDI_SCHEMA_VERSION,
}
asset: Asset = draft_asset_factory(metadata=raw_metadata)
@@ -201,7 +200,7 @@ def test_asset_full_metadata(draft_asset_factory):
'contentUrl': [download_url, blob_url],
'contentSize': asset.blob.size,
'digest': asset.blob.digest,
- '@context': f'https://raw.githubusercontent.com/dandi/schema/master/releases/{settings.DANDI_SCHEMA_VERSION}/context.json',
+ '@context': f'https://raw.githubusercontent.com/dandi/schema/master/releases/{DANDI_SCHEMA_VERSION}/context.json',
}
@@ -210,7 +209,7 @@ def test_asset_full_metadata_zarr(draft_asset_factory):
zarr_archive = ZarrArchiveFactory.create()
raw_metadata = {
'foo': 'bar',
- 'schemaVersion': settings.DANDI_SCHEMA_VERSION,
+ 'schemaVersion': DANDI_SCHEMA_VERSION,
}
asset: Asset = draft_asset_factory(metadata=raw_metadata, blob=None, zarr=zarr_archive)
@@ -232,7 +231,7 @@ def test_asset_full_metadata_zarr(draft_asset_factory):
'digest': asset.digest,
# This should be injected on all zarr assets
'encodingFormat': 'application/x-zarr',
- '@context': f'https://raw.githubusercontent.com/dandi/schema/master/releases/{settings.DANDI_SCHEMA_VERSION}/context.json',
+ '@context': f'https://raw.githubusercontent.com/dandi/schema/master/releases/{DANDI_SCHEMA_VERSION}/context.json',
}
@@ -242,7 +241,7 @@ def test_asset_full_metadata_access(
):
raw_metadata = {
'foo': 'bar',
- 'schemaVersion': settings.DANDI_SCHEMA_VERSION,
+ 'schemaVersion': DANDI_SCHEMA_VERSION,
}
embargoed_zarr_asset: Asset = draft_asset_factory(
metadata=raw_metadata, blob=None, zarr=embargoed_zarr_archive_factory()
@@ -788,7 +787,7 @@ def test_asset_create_path_validation(api_client, asset_blob, path, expected_sta
api_client.force_authenticate(user=user)
metadata = {
- 'schemaVersion': settings.DANDI_SCHEMA_VERSION,
+ 'schemaVersion': DANDI_SCHEMA_VERSION,
'encodingFormat': 'application/x-nwb',
'path': path,
}
@@ -815,7 +814,7 @@ def test_asset_create_conflicting_path(api_client, asset_blob):
asset_blob=asset_blob,
metadata={
'path': 'foo/bar.txt',
- 'schemaVersion': settings.DANDI_SCHEMA_VERSION,
+ 'schemaVersion': DANDI_SCHEMA_VERSION,
},
)
@@ -827,7 +826,7 @@ def test_asset_create_conflicting_path(api_client, asset_blob):
asset_blob=asset_blob,
metadata={
'path': 'foo/bar.txt/baz.txt',
- 'schemaVersion': settings.DANDI_SCHEMA_VERSION,
+ 'schemaVersion': DANDI_SCHEMA_VERSION,
},
)
@@ -839,7 +838,7 @@ def test_asset_create_conflicting_path(api_client, asset_blob):
asset_blob=asset_blob,
metadata={
'path': 'foo',
- 'schemaVersion': settings.DANDI_SCHEMA_VERSION,
+ 'schemaVersion': DANDI_SCHEMA_VERSION,
},
)
@@ -1255,7 +1254,7 @@ def test_asset_rest_rename(api_client, asset_blob):
api_client.force_authenticate(user=user)
# Create asset
- metadata = {'path': 'foo/bar', 'schemaVersion': settings.DANDI_SCHEMA_VERSION}
+ metadata = {'path': 'foo/bar', 'schemaVersion': DANDI_SCHEMA_VERSION}
asset = add_asset_to_version(
user=user, version=draft_version, asset_blob=asset_blob, metadata=metadata
)
@@ -1652,7 +1651,7 @@ def test_asset_rest_delete_zarr_modified(
{
'metadata': {
'path': 'sample.zarr',
- 'schemaVersion': settings.DANDI_SCHEMA_VERSION,
+ 'schemaVersion': DANDI_SCHEMA_VERSION,
},
'zarr_id': zarr_archive.zarr_id,
},
diff --git a/dandiapi/api/tests/test_asset_paths.py b/dandiapi/api/tests/test_asset_paths.py
index b67430344..7cdcdbe2c 100644
--- a/dandiapi/api/tests/test_asset_paths.py
+++ b/dandiapi/api/tests/test_asset_paths.py
@@ -1,6 +1,6 @@
from __future__ import annotations
-from django.conf import settings
+from dandischema.consts import DANDI_SCHEMA_VERSION
from django.db.models import Q, QuerySet
import pytest
@@ -383,7 +383,7 @@ def test_asset_path_ordering(asset_blob):
asset_blob=asset_blob,
metadata={
'path': 'a/z',
- 'schemaVersion': settings.DANDI_SCHEMA_VERSION,
+ 'schemaVersion': DANDI_SCHEMA_VERSION,
},
)
add_asset_to_version(
@@ -392,7 +392,7 @@ def test_asset_path_ordering(asset_blob):
asset_blob=asset_blob,
metadata={
'path': 'aa/z',
- 'schemaVersion': settings.DANDI_SCHEMA_VERSION,
+ 'schemaVersion': DANDI_SCHEMA_VERSION,
},
)
diff --git a/dandiapi/api/tests/test_dandiset.py b/dandiapi/api/tests/test_dandiset.py
index e74493966..c4e6a7487 100644
--- a/dandiapi/api/tests/test_dandiset.py
+++ b/dandiapi/api/tests/test_dandiset.py
@@ -4,6 +4,8 @@
from typing import TYPE_CHECKING
from urllib.parse import urlencode
+from dandischema.conf import get_instance_config
+from dandischema.consts import DANDI_SCHEMA_VERSION
from django.conf import settings
from django.contrib.auth.models import AnonymousUser
from django.utils import timezone
@@ -21,6 +23,7 @@
PublishedVersionFactory,
UserFactory,
)
+from dandiapi.conftest import get_first_allowed_license
from .fuzzy import (
DANDISET_ID_RE,
@@ -34,6 +37,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 +458,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,
@@ -460,8 +466,8 @@ def test_dandiset_rest_create(api_client):
f'{user.last_name}, {user.first_name} ({year}) {name} '
f'(Version draft) [Data set]. DANDI Archive. {url}'
),
- '@context': f'https://raw.githubusercontent.com/dandi/schema/master/releases/{settings.DANDI_SCHEMA_VERSION}/context.json',
- 'schemaVersion': settings.DANDI_SCHEMA_VERSION,
+ '@context': f'https://raw.githubusercontent.com/dandi/schema/master/releases/{DANDI_SCHEMA_VERSION}/context.json',
+ 'schemaVersion': DANDI_SCHEMA_VERSION,
'schemaKey': 'Dandiset',
'access': [{'schemaKey': 'AccessRequirements', 'status': 'dandi:OpenAccess'}],
'repository': settings.DANDI_WEB_APP_URL,
@@ -489,7 +495,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 +540,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,
@@ -543,8 +549,8 @@ def test_dandiset_rest_create_with_identifier(api_client):
f'{user.last_name}, {user.first_name} ({year}) {name} '
f'(Version draft) [Data set]. DANDI Archive. {url}'
),
- '@context': f'https://raw.githubusercontent.com/dandi/schema/master/releases/{settings.DANDI_SCHEMA_VERSION}/context.json',
- 'schemaVersion': settings.DANDI_SCHEMA_VERSION,
+ '@context': f'https://raw.githubusercontent.com/dandi/schema/master/releases/{DANDI_SCHEMA_VERSION}/context.json',
+ 'schemaVersion': DANDI_SCHEMA_VERSION,
'schemaKey': 'Dandiset',
'access': [{'schemaKey': 'AccessRequirements', 'status': 'dandi:OpenAccess'}],
'repository': settings.DANDI_WEB_APP_URL,
@@ -574,7 +580,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,14 +637,14 @@ 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,
'citation': (f'Jane Doe ({year}) {name} (Version draft) [Data set]. DANDI Archive. {url}'),
- '@context': f'https://raw.githubusercontent.com/dandi/schema/master/releases/{settings.DANDI_SCHEMA_VERSION}/context.json',
- 'schemaVersion': settings.DANDI_SCHEMA_VERSION,
+ '@context': f'https://raw.githubusercontent.com/dandi/schema/master/releases/{DANDI_SCHEMA_VERSION}/context.json',
+ 'schemaVersion': DANDI_SCHEMA_VERSION,
'schemaKey': 'Dandiset',
'access': [{'schemaKey': 'AccessRequirements', 'status': 'dandi:OpenAccess'}],
'repository': settings.DANDI_WEB_APP_URL,
@@ -712,7 +718,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,
@@ -720,8 +726,8 @@ def test_dandiset_rest_create_embargoed(api_client):
f'{user.last_name}, {user.first_name} ({year}) {name} '
f'(Version draft) [Data set]. DANDI Archive. {url}'
),
- '@context': f'https://raw.githubusercontent.com/dandi/schema/master/releases/{settings.DANDI_SCHEMA_VERSION}/context.json',
- 'schemaVersion': settings.DANDI_SCHEMA_VERSION,
+ '@context': f'https://raw.githubusercontent.com/dandi/schema/master/releases/{DANDI_SCHEMA_VERSION}/context.json',
+ 'schemaVersion': DANDI_SCHEMA_VERSION,
'schemaKey': 'Dandiset',
'access': [
{
@@ -754,7 +760,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 +812,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 +861,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 +885,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 +910,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..4252cbc42 100644
--- a/dandiapi/api/tests/test_version.py
+++ b/dandiapi/api/tests/test_version.py
@@ -4,6 +4,8 @@
from time import sleep
from typing import TYPE_CHECKING
+from dandischema.conf import get_instance_config
+from dandischema.consts import DANDI_SCHEMA_VERSION
from dandischema.models import AccessType
from django.conf import settings
from freezegun import freeze_time
@@ -28,7 +30,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()
@@ -58,7 +69,7 @@ def test_version_next_published_version_preexisting():
@pytest.mark.django_db
def test_draft_version_metadata_computed():
draft_version = DraftVersionFactory.create()
- original_metadata = {'schemaVersion': settings.DANDI_SCHEMA_VERSION}
+ original_metadata = {'schemaVersion': DANDI_SCHEMA_VERSION}
draft_version.metadata = original_metadata
# Save the version to add computed properties to the metadata
@@ -68,9 +79,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}'
@@ -78,7 +92,7 @@ def test_draft_version_metadata_computed():
'repository': settings.DANDI_WEB_APP_URL,
'dateCreated': draft_version.dandiset.created.isoformat(),
'access': [{'schemaKey': 'AccessRequirements', 'status': AccessType.OpenAccess.value}],
- '@context': f'https://raw.githubusercontent.com/dandi/schema/master/releases/{settings.DANDI_SCHEMA_VERSION}/context.json',
+ '@context': f'https://raw.githubusercontent.com/dandi/schema/master/releases/{DANDI_SCHEMA_VERSION}/context.json',
'assetsSummary': {
'numberOfBytes': 0,
'numberOfFiles': 0,
@@ -93,7 +107,7 @@ def test_draft_version_metadata_computed():
@pytest.mark.django_db
def test_published_version_metadata_computed():
published_version = PublishedVersionFactory.create()
- original_metadata = {'schemaVersion': settings.DANDI_SCHEMA_VERSION}
+ original_metadata = {'schemaVersion': DANDI_SCHEMA_VERSION}
published_version.metadata = original_metadata
# Save the version to add computed properties to the metadata
@@ -103,11 +117,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/'
@@ -116,7 +135,7 @@ def test_published_version_metadata_computed():
'repository': settings.DANDI_WEB_APP_URL,
'dateCreated': published_version.dandiset.created.isoformat(),
'access': [{'schemaKey': 'AccessRequirements', 'status': AccessType.OpenAccess.value}],
- '@context': f'https://raw.githubusercontent.com/dandi/schema/master/releases/{settings.DANDI_SCHEMA_VERSION}/context.json',
+ '@context': f'https://raw.githubusercontent.com/dandi/schema/master/releases/{DANDI_SCHEMA_VERSION}/context.json',
'assetsSummary': {
'numberOfBytes': 0,
'numberOfFiles': 0,
@@ -308,24 +327,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}'
@@ -543,9 +556,9 @@ def test_version_rest_update(api_client):
new_metadata = {
'@context': (
'https://raw.githubusercontent.com/dandi/schema/master/releases/'
- f'{settings.DANDI_SCHEMA_VERSION}/context.json'
+ f'{DANDI_SCHEMA_VERSION}/context.json'
),
- 'schemaVersion': settings.DANDI_SCHEMA_VERSION,
+ 'schemaVersion': DANDI_SCHEMA_VERSION,
'foo': 'bar',
'num': 123,
'list': ['a', 'b', 'c'],
@@ -564,13 +577,13 @@ def test_version_rest_update(api_client):
url = f'{settings.DANDI_WEB_APP_URL}/dandiset/{draft_version.dandiset.identifier}/draft'
saved_metadata = {
**new_metadata,
- 'schemaVersion': settings.DANDI_SCHEMA_VERSION,
+ 'schemaVersion': DANDI_SCHEMA_VERSION,
'manifestLocation': [
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,
@@ -634,9 +647,9 @@ def test_version_rest_update_unembargo_in_progress(api_client):
new_metadata = {
'@context': (
'https://raw.githubusercontent.com/dandi/schema/master/releases/'
- f'{settings.DANDI_SCHEMA_VERSION}/context.json'
+ f'{DANDI_SCHEMA_VERSION}/context.json'
),
- 'schemaVersion': settings.DANDI_SCHEMA_VERSION,
+ 'schemaVersion': DANDI_SCHEMA_VERSION,
'num': 123,
}
diff --git a/dandiapi/api/tests/test_webdav.py b/dandiapi/api/tests/test_webdav.py
index 1ab109658..009ffcc49 100644
--- a/dandiapi/api/tests/test_webdav.py
+++ b/dandiapi/api/tests/test_webdav.py
@@ -1,6 +1,6 @@
from __future__ import annotations
-from django.conf import settings
+from dandischema.consts import DANDI_SCHEMA_VERSION
import pytest
from dandiapi.api.models.dandiset import Dandiset
@@ -18,7 +18,7 @@ def test_asset_atpath_root_path(api_client, asset_blob):
asset_blob=asset_blob,
metadata={
'path': 'a.txt',
- 'schemaVersion': settings.DANDI_SCHEMA_VERSION,
+ 'schemaVersion': DANDI_SCHEMA_VERSION,
},
)
add_asset_to_version(
@@ -27,7 +27,7 @@ def test_asset_atpath_root_path(api_client, asset_blob):
asset_blob=asset_blob,
metadata={
'path': 'b.txt',
- 'schemaVersion': settings.DANDI_SCHEMA_VERSION,
+ 'schemaVersion': DANDI_SCHEMA_VERSION,
},
)
@@ -73,7 +73,7 @@ def test_asset_atpath_asset(api_client, asset_blob):
asset_blob=asset_blob,
metadata={
'path': path,
- 'schemaVersion': settings.DANDI_SCHEMA_VERSION,
+ 'schemaVersion': DANDI_SCHEMA_VERSION,
},
)
@@ -140,7 +140,7 @@ def test_asset_atpath_folder(api_client, asset_blob):
asset_blob=asset_blob,
metadata={
'path': 'foo/bar.txt',
- 'schemaVersion': settings.DANDI_SCHEMA_VERSION,
+ 'schemaVersion': DANDI_SCHEMA_VERSION,
},
)
add_asset_to_version(
@@ -149,7 +149,7 @@ def test_asset_atpath_folder(api_client, asset_blob):
asset_blob=asset_blob,
metadata={
'path': 'foo/baz.txt',
- 'schemaVersion': settings.DANDI_SCHEMA_VERSION,
+ 'schemaVersion': DANDI_SCHEMA_VERSION,
},
)
@@ -209,7 +209,7 @@ def test_asset_atpath_trailing_slash(api_client, asset_blob):
asset_blob=asset_blob,
metadata={
'path': 'foo',
- 'schemaVersion': settings.DANDI_SCHEMA_VERSION,
+ 'schemaVersion': DANDI_SCHEMA_VERSION,
},
)
@@ -237,7 +237,7 @@ def test_asset_atpath_path_missing(api_client, asset_blob):
asset_blob=asset_blob,
metadata={
'path': 'foo/bar.txt',
- 'schemaVersion': settings.DANDI_SCHEMA_VERSION,
+ 'schemaVersion': DANDI_SCHEMA_VERSION,
},
)
diff --git a/dandiapi/api/views/asset.py b/dandiapi/api/views/asset.py
index 962f801c4..34daed73d 100644
--- a/dandiapi/api/views/asset.py
+++ b/dandiapi/api/views/asset.py
@@ -3,7 +3,7 @@
import re
from typing import TYPE_CHECKING, cast
-from django.conf import settings
+from dandischema.consts import DANDI_SCHEMA_VERSION
from django.db import transaction
from django.http import HttpResponse, HttpResponseRedirect
from django_filters import rest_framework as filters
@@ -220,7 +220,7 @@ def validate(self, data):
# will be caught further up the stack and be converted to a DRF ValidationError
validate_asset_path(data['metadata']['path'])
- data['metadata'].setdefault('schemaVersion', settings.DANDI_SCHEMA_VERSION)
+ data['metadata'].setdefault('schemaVersion', DANDI_SCHEMA_VERSION)
return data
diff --git a/dandiapi/api/views/auth.py b/dandiapi/api/views/auth.py
index e9dab98d9..2f2d27f19 100644
--- a/dandiapi/api/views/auth.py
+++ b/dandiapi/api/views/auth.py
@@ -135,6 +135,7 @@ def user_questionnaire_form_view(request: AuthenticatedRequest) -> HttpResponse:
'@janelia.hhmi.org',
'@ccf.org',
'.ac.uk',
+ '.mcgill.ca',
]
)
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 fa2c410ac..69b1752a2 100644
--- a/dandiapi/api/views/info.py
+++ b/dandiapi/api/views/info.py
@@ -3,6 +3,8 @@
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, DANDI_SCHEMA_VERSION
from django.conf import settings
from django.urls import reverse
from drf_yasg.utils import no_body, swagger_auto_schema
@@ -50,6 +52,9 @@ def __init__(self, *args, **kwargs):
}
)
+ # Instance Configuration
+ instance_config = serializers.JSONField()
+
# Schema
schema_version = serializers.CharField()
schema_url = serializers.URLField()
@@ -72,9 +77,12 @@ def info_view(request):
api_url = f'{settings.DANDI_API_URL}/api'
serializer = ApiInfoSerializer(
data={
- 'schema_version': settings.DANDI_SCHEMA_VERSION,
+ # 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': 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/api/views/serializers.py b/dandiapi/api/views/serializers.py
index 240e58a5f..0132735d1 100644
--- a/dandiapi/api/views/serializers.py
+++ b/dandiapi/api/views/serializers.py
@@ -3,7 +3,7 @@
from datetime import date, timedelta
from typing import TYPE_CHECKING, Any
-from django.conf import settings
+from dandischema.consts import DANDI_SCHEMA_VERSION
from django.contrib.auth.validators import UnicodeUsernameValidator
from django.db.models.query_utils import Q
from django.utils import timezone
@@ -145,7 +145,7 @@ class Meta:
validators = []
def validate(self, data):
- data['metadata'].setdefault('schemaVersion', settings.DANDI_SCHEMA_VERSION)
+ data['metadata'].setdefault('schemaVersion', DANDI_SCHEMA_VERSION)
return super().validate(data)
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 7af8db68b..5eae2f8fd 100644
--- a/dandiapi/settings/base.py
+++ b/dandiapi/settings/base.py
@@ -7,8 +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
from resonant_settings.allauth import *
@@ -170,14 +168,6 @@
# Configure the logging level on all DANDI loggers.
logging.getLogger('dandiapi').setLevel(_dandi_log_level)
-# This is where the schema version should be set.
-# It can optionally be overwritten with the environment variable, but that should only be
-# considered a temporary fix.
-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')
# Required environment variables
@@ -185,11 +175,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/dandiapi/zarr/tests/test_ingest_zarr_archive.py b/dandiapi/zarr/tests/test_ingest_zarr_archive.py
index e5d056813..9eca0ef2d 100644
--- a/dandiapi/zarr/tests/test_ingest_zarr_archive.py
+++ b/dandiapi/zarr/tests/test_ingest_zarr_archive.py
@@ -1,6 +1,6 @@
from __future__ import annotations
-from django.conf import settings
+from dandischema.consts import DANDI_SCHEMA_VERSION
import pytest
from zarr_checksum.checksum import EMPTY_CHECKSUM
@@ -127,7 +127,7 @@ def test_ingest_zarr_archive_modified(zarr_archive_factory, zarr_file_factory):
zarr_archive=zarr_archive,
metadata={
'path': 'sample.zarr',
- 'schemaVersion': settings.DANDI_SCHEMA_VERSION,
+ 'schemaVersion': DANDI_SCHEMA_VERSION,
},
)
assert asset.size == 100
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/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:
diff --git a/pyproject.toml b/pyproject.toml
index 0121d7fa1..bff794d16 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -12,9 +12,9 @@ dependencies = [
# Runtime dependencies, always needed
"boto3",
"celery",
- "dandi", # minimal version is also provided in API /info
+ "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.1", # schema version 0.7.0
"django[argon2]",
"django-allauth",
"django-auth-style",
diff --git a/uv.lock b/uv.lock
index 0f256a552..773b6462d 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1,20 +1,12 @@
version = 1
-revision = 3
+revision = 2
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.1" },
{ 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.1"
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/23/b7/b44244184c16c1b1cc69070b53325965285b81843ad7755c429e1ffaa341/dandischema-0.12.1.tar.gz", hash = "sha256:481ed1da9481090d8000b3b2373a0d4876043f48d4cddc3364c18e0a97bd0c22", size = 98659, upload-time = "2025-11-26T20:16:58.672Z" }
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/cb/16/959ccdc06e45781d3c41cd552baeaaf563ffb5fe240fbd24efd26b8b2b12/dandischema-0.12.1-py3-none-any.whl", hash = "sha256:88f84cefd7883ce15d5c2f6b92411b1d00fd76468b77950fff25381e17eaab44", size = 118561, upload-time = "2025-11-26T20:16:57.121Z" },
]
[[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 @@