Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions dandischema/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1677,6 +1677,25 @@ def contributor_musthave_contact(
pattern=rf"^{ID_PATTERN}:\d{{6}}$",
json_schema_extra={"readOnly": True, "nskey": "schema"},
)

otherIdentifiers: Annotated[
list[
Annotated[
str, StringConstraints(pattern=rf"^{UNVENDORED_ID_PATTERN}:\d{{6}}$")
]
],
Field(
default_factory=list,
title="Other Dandiset identifiers",
description="Alternative Dandiset identifiers for this Dandiset as it is "
"identified in other DANDI instances.",
json_schema_extra={
"readOnly": True,
"nskey": DANDI_NSKEY,
},
),
]

name: str = Field(
title="Dandiset title",
description="A title associated with the Dandiset.",
Expand Down
162 changes: 120 additions & 42 deletions dandischema/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,53 @@
_INSTANCE_CONFIG = get_instance_config()


@pytest.fixture
def base_dandiset_metadata() -> dict[str, Any]:
"""
Fixture providing basic Dandiset metadata for constructing a `Dandiset` instance.

Returns:
Dict[str, Any]
A dictionary containing basic Dandiset metadata without `doi`, `datePublished`,
and `publishedBy`, suitable for constructing a `Dandiset` instance but not a
`PublishedDandiset` instance.

Note:
This metadata is returned by a fixture to ensure that each test receives a fresh
copy of the metadata dictionary.
"""

return {
"identifier": f"{INSTANCE_NAME}:999999",
"id": f"{INSTANCE_NAME}:999999/draft",
"version": "1.0.0",
"name": "testing dataset",
"description": "testing",
"contributor": [
{
"name": "last name, first name",
"email": "someone@dandiarchive.org",
"roleName": [RoleType("dcite:ContactPerson")],
"schemaKey": "Person",
}
],
"license": [LicenseType("spdx:CC-BY-4.0")],
"citation": "Last, first (2021). Test citation.",
"assetsSummary": {
"numberOfBytes": 0,
"numberOfFiles": 0,
"dataStandard": [{"name": "NWB"}],
"approach": [{"name": "electrophysiology"}],
"measurementTechnique": [{"name": "two-photon microscopy technique"}],
"species": [{"name": "Human"}],
},
"manifestLocation": [
"https://api.dandiarchive.org/api/dandisets/999999/versions/draft/assets/"
],
"url": "https://dandiarchive.org/dandiset/999999/draft",
}


@pytest.mark.parametrize(
("y_type", "anys_value"),
[
Expand Down Expand Up @@ -403,46 +450,15 @@ def test_autogenerated_titles() -> None:


@skipif_no_doi_prefix
def test_dandimeta_1() -> None:
def test_dandimeta_1(base_dandiset_metadata: dict[str, Any]) -> None:
"""checking basic metadata for publishing"""

assert DOI_PREFIX is not None

# metadata without doi, datePublished and publishedBy
meta_dict: Dict[str, Any] = {
"identifier": f"{INSTANCE_NAME}:999999",
"id": f"{INSTANCE_NAME}:999999/draft",
"version": "1.0.0",
"name": "testing dataset",
"description": "testing",
"contributor": [
{
"name": "last name, first name",
"email": "someone@dandiarchive.org",
"roleName": [RoleType("dcite:ContactPerson")],
"schemaKey": "Person",
}
],
"license": [LicenseType("spdx:CC-BY-4.0")],
"citation": "Last, first (2021). Test citation.",
"assetsSummary": {
"numberOfBytes": 0,
"numberOfFiles": 0,
"dataStandard": [{"name": "NWB"}],
"approach": [{"name": "electrophysiology"}],
"measurementTechnique": [{"name": "two-photon microscopy technique"}],
"species": [{"name": "Human"}],
},
"manifestLocation": [
"https://api.dandiarchive.org/api/dandisets/999999/versions/draft/assets/"
],
"url": "https://dandiarchive.org/dandiset/999999/draft",
}

# should work for Dandiset but PublishedDandiset should raise an error
Dandiset(**meta_dict)
Dandiset(**base_dandiset_metadata)
with pytest.raises(ValidationError) as exc:
PublishedDandiset(**meta_dict)
PublishedDandiset(**base_dandiset_metadata)

ErrDetail = namedtuple("ErrDetail", ["type", "msg"])

Expand Down Expand Up @@ -490,21 +506,23 @@ def test_dandimeta_1() -> None:

# after adding basic meta required to publish: doi, datePublished, publishedBy, assetsSummary,
# so PublishedDandiset should work
meta_dict["url"] = "https://dandiarchive.org/dandiset/999999/0.0.0"
meta_dict["id"] = f"{INSTANCE_NAME}:999999/0.0.0"
meta_dict["version"] = "0.0.0"
meta_dict.update(
base_dandiset_metadata["url"] = "https://dandiarchive.org/dandiset/999999/0.0.0"
base_dandiset_metadata["id"] = f"{INSTANCE_NAME}:999999/0.0.0"
base_dandiset_metadata["version"] = "0.0.0"
base_dandiset_metadata.update(
basic_publishmeta(INSTANCE_NAME, dandi_id="999999", prefix=DOI_PREFIX)
)
meta_dict["assetsSummary"].update(**{"numberOfBytes": 1, "numberOfFiles": 1})
base_dandiset_metadata["assetsSummary"].update(
**{"numberOfBytes": 1, "numberOfFiles": 1}
)

# Test that releaseNotes is optional (can be omitted)
dandiset_without_notes = PublishedDandiset(**meta_dict)
dandiset_without_notes = PublishedDandiset(**base_dandiset_metadata)
assert dandiset_without_notes.releaseNotes is None

# Test that releaseNotes can be set to a string value
meta_dict["releaseNotes"] = "Releasing during testing"
dandiset_with_notes = PublishedDandiset(**meta_dict)
base_dandiset_metadata["releaseNotes"] = "Releasing during testing"
dandiset_with_notes = PublishedDandiset(**base_dandiset_metadata)
assert dandiset_with_notes.releaseNotes == "Releasing during testing"

# Test that releaseNotes appears in model_dump
Expand Down Expand Up @@ -983,3 +1001,63 @@ class VendoredFieldModel(BaseModel):
# Validate the invalid vendored fields against the vendored patterns
with pytest.raises(ValidationError):
VendoredFieldModel.model_validate(invalid_vendored_fields)


class TestOtherIdentifiers:
def test_not_specified(self, base_dandiset_metadata: dict[str, Any]) -> None:
"""
Test the case that `otherIdentifiers` is not specified
"""
dandiset = Dandiset.model_validate(base_dandiset_metadata)
assert dandiset.otherIdentifiers == []

def test_empty_list(self, base_dandiset_metadata: dict[str, Any]) -> None:
"""
Test the case that `otherIdentifiers` is an empty list
"""
base_dandiset_metadata["otherIdentifiers"] = []
dandiset = Dandiset.model_validate(base_dandiset_metadata)
assert dandiset.otherIdentifiers == []

@pytest.mark.parametrize(
"identifiers",
[
["DANDI-SANDBOX:123456"],
["EMBER-DANDI:123456"],
["DANDI-SANDBOX:123456", "EMBER-DANDI:123456"],
["A:123456", "B:654321"],
],
)
def test_with_valid_identifiers(
self, identifiers: list[str], base_dandiset_metadata: dict[str, Any]
) -> None:
"""
Test the case that `otherIdentifiers` is set to a list of valid identifiers
"""
base_dandiset_metadata["otherIdentifiers"] = identifiers
dandiset = Dandiset.model_validate(base_dandiset_metadata)
assert dandiset.otherIdentifiers == identifiers

@pytest.mark.parametrize(
"identifiers",
[
# List of invalid identifiers
["-A:123456"],
["DANDI-SANDBOX:12345"],
["DANDI-SANDBOX:123456", "EMBER-DANDI123456"],
[42],
# Value that is not a list
"DANDI-SANDBOX:123456",
42,
],
)
def test_with_invalid_identifiers(
self, identifiers: Any, base_dandiset_metadata: dict[str, Any]
) -> None:
"""
Test the case that `otherIdentifiers` is set to a list of invalid identifiers
or a value that is not a list
"""
base_dandiset_metadata["otherIdentifiers"] = identifiers
with pytest.raises(ValidationError):
Dandiset.model_validate(base_dandiset_metadata)
Loading