diff --git a/bioimageio_collection_backoffice/collection_config/id_parts.py b/bioimageio_collection_backoffice/collection_config/id_parts.py index 487e2d76..01cb3b22 100644 --- a/bioimageio_collection_backoffice/collection_config/id_parts.py +++ b/bioimageio_collection_backoffice/collection_config/id_parts.py @@ -65,7 +65,7 @@ def get_icon(self, resource_id: str): return None - def select_type(self, type_: str) -> IdPartsEntry: + def __getitem__(self, type_: str) -> IdPartsEntry: if type_ == "model": return self.model elif type_ == "dataset": diff --git a/bioimageio_collection_backoffice/collection_json.py b/bioimageio_collection_backoffice/collection_json.py index 312c1b29..63adaff8 100644 --- a/bioimageio_collection_backoffice/collection_json.py +++ b/bioimageio_collection_backoffice/collection_json.py @@ -1,7 +1,7 @@ from __future__ import annotations from datetime import datetime -from typing import Literal, Mapping, Optional, Sequence, Union +from typing import Collection, Literal, Mapping, Optional, Sequence, Union from loguru import logger from pydantic import Field, HttpUrl, model_validator @@ -115,3 +115,9 @@ def __lt__(self, other: ConceptSummary): class AllVersions(Node, frozen=True): entries: Sequence[ConceptSummary] + + +class AvailableConceptIds(Node, frozen=True): + model: Collection[str] + dataset: Collection[str] + notebook: Collection[str] diff --git a/bioimageio_collection_backoffice/remote_collection.py b/bioimageio_collection_backoffice/remote_collection.py index 7907610b..b67a13dd 100644 --- a/bioimageio_collection_backoffice/remote_collection.py +++ b/bioimageio_collection_backoffice/remote_collection.py @@ -10,6 +10,7 @@ from collections import defaultdict from dataclasses import dataclass, field from functools import wraps +from itertools import product from pathlib import Path from typing import ( Any, @@ -44,6 +45,7 @@ from .collection_config import CollectionConfig from .collection_json import ( AllVersions, + AvailableConceptIds, CollectionEntry, CollectionJson, CollectionWebsiteConfig, @@ -318,11 +320,11 @@ def _select_parts(self, type_: str): def validate_concept_id(self, concept_id: str, *, type_: str): """check if a concept id follows the defined pattern (not if it exists)""" - self.config.id_parts.select_type(type_).validate_concept_id(concept_id) + self.config.id_parts[type_].validate_concept_id(concept_id) def generate_concpet_id(self, type_: str): """generate a new, unused concept id""" - id_parts = self.config.id_parts.select_type(type_) + id_parts = self.config.id_parts[type_] nouns = list(id_parts.nouns) taken = self.get_taken_concept_ids() n = 9999 @@ -365,6 +367,11 @@ def generate_collection_json( all_versions_file_name: str = ( "all_versions.json" if mode == "published" else f"all_versions_{mode}.json" ) + available_concept_ids_file_name: str = ( + "available_ids.json" + if mode == "published" + else f"available_ids_{mode}.json" + ) id_map_file_name = ( "id_map.json" if mode == "published" else f"id_map_{mode}.json" ) @@ -455,7 +462,24 @@ def generate_collection_json( ) all_versions = AllVersions(entries=concepts_summaries) - + types = ("model", "dataset", "notebook") + taken_ids = { + typ: {cs.concept for cs in concepts_summaries if cs.type == typ} + for typ in types + } + available_concept_ids = AvailableConceptIds.model_validate( + { + typ: { + ci + for a, n in product( + self.config.id_parts[typ].adjectives, + self.config.id_parts[typ].nouns, + ) + if (ci := f"{a}-{n}") not in taken_ids[typ] + } + for typ in types + } + ) # # check that this generated collection is a valid RDF itself # coll_descr = build_description( # collection.model_dump(), context=ValidationContext(perform_io_checks=False) @@ -481,6 +505,10 @@ def generate_collection_json( all_versions_file_name, all_versions.model_dump(mode="json", exclude_defaults=True), ) + self.client.put_json( + available_concept_ids_file_name, + available_concept_ids.model_dump(mode="json", exclude_defaults=True), + ) else: logger.error( "Skipping overriding existing {} with an empty list!", diff --git a/tests/test_remote_collection.py b/tests/test_remote_collection.py index 7e85b97f..d1922470 100644 --- a/tests/test_remote_collection.py +++ b/tests/test_remote_collection.py @@ -14,10 +14,7 @@ def test_validate_concept_id_direct(): from bioimageio_collection_backoffice.collection_config import CollectionConfig config = CollectionConfig.load() - assert ( - config.id_parts.select_type("model").validate_concept_id("affable-shark") - is None - ) + assert config.id_parts["model"].validate_concept_id("affable-shark") is None @pytest.mark.parametrize("type_", ["model", "dataset", "notebook"])