Skip to content

Commit

Permalink
Add clean version of model version and object version to the models i…
Browse files Browse the repository at this point in the history
…n version control (#128)
  • Loading branch information
liviuba authored and ctr26 committed Jul 31, 2024
1 parent 6170771 commit 60cb696
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 11 deletions.
72 changes: 61 additions & 11 deletions bia-shared-datamodels/src/bia_shared_datamodels/bia_data_model.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,55 @@
from __future__ import annotations

from . import semantic_models
from . import semantic_models, exceptions
from pydantic import BaseModel, Field, ConfigDict
from typing import List, Optional
from uuid import UUID
from enum import Enum


class ModelMetadata(BaseModel):
type_name: str = Field()
version: int = Field()


class DocumentMixin(BaseModel):

# Throw error if you try to validate/create model from a dictionary with keys that aren't a field in the model
model_config = ConfigDict(extra="forbid")

uuid: UUID = Field(
description="""Unique ID (across the BIA database) used to refer to and identify a document."""
)

version: int = Field(
description="""Document version. This can't be optional to make sure we never persist objects without it"""
)
model: Optional[ModelMetadata] = Field(
description="""Model type and version. Used to map arbitrary objects to a known (possibly previously-used) type.
Optional because for some usecases (e.g. api) we want to accept objects without it because we have the info we need to set it."""
)

# Throw error if you try to validate/create model from a dictionary with keys that aren't a field in the model
model_config = ConfigDict(extra="forbid")

def __init__(self, *args, **data):
model_version_spec = self.model_config.get("model_version_latest")
if model_version_spec is None:
raise exceptions.ModelDefinitionInvalid(
f"Class {self.__class__.__name__} missing 'model_version_latest' in its model_config"
)

model_metadata_expected = ModelMetadata(
type_name=self.__class__.__name__,
version=model_version_spec,
)
model_metadata_existing = data.get("model", None)
if model_metadata_existing:
if model_metadata_existing != model_metadata_expected:
raise exceptions.UnexpectedDocumentType(
f"Document {str(data.get('uuid'))} has model metadata {model_metadata_existing}, expected : {model_metadata_expected}"
)
else:
data["model"] = model_metadata_expected.model_dump()

super().__init__(*args, **data)


class UserIdentifiedObject(BaseModel):
title_id: str = Field(
Expand All @@ -31,13 +63,17 @@ class Study(
):
author: List[semantic_models.Contributor] = Field(min_length=1)

model_config = ConfigDict(model_version_latest=1)


class FileReference(
semantic_models.FileReference,
DocumentMixin,
):
submission_dataset_uuid: UUID = Field()

model_config = ConfigDict(model_version_latest=1)


class ImageRepresentation(
semantic_models.ImageRepresentation,
Expand All @@ -47,6 +83,8 @@ class ImageRepresentation(
original_file_reference_uuid: Optional[List[UUID]] = Field()
representation_of_uuid: UUID = Field()

model_config = ConfigDict(model_version_latest=1)


class ExperimentalImagingDataset(
semantic_models.ExperimentalImagingDataset,
Expand All @@ -55,12 +93,16 @@ class ExperimentalImagingDataset(
):
submitted_in_study_uuid: UUID = Field()

model_config = ConfigDict(model_version_latest=1)


class Specimen(semantic_models.Specimen, DocumentMixin):
imaging_preparation_protocol_uuid: List[UUID] = Field(min_length=1)
sample_of_uuid: List[UUID] = Field(min_length=1)
growth_protocol_uuid: List[UUID] = Field()

model_config = ConfigDict(model_version_latest=1)


class ExperimentallyCapturedImage(
semantic_models.ExperimentallyCapturedImage,
Expand All @@ -70,36 +112,39 @@ class ExperimentallyCapturedImage(
submission_dataset_uuid: UUID = Field()
subject_uuid: UUID = Field()

model_config = ConfigDict(model_version_latest=1)


class ImageAcquisition(
semantic_models.ImageAcquisition,
DocumentMixin,
UserIdentifiedObject,
):
pass
model_config = ConfigDict(model_version_latest=1)


class SpecimenImagingPrepartionProtocol(
semantic_models.SpecimenImagingPrepartionProtocol,
DocumentMixin,
UserIdentifiedObject,
):
pass
model_config = ConfigDict(model_version_latest=1)


class SpecimenGrowthProtocol(
semantic_models.SpecimenGrowthProtocol,
DocumentMixin,
UserIdentifiedObject,
):
pass
model_config = ConfigDict(model_version_latest=1)


class BioSample(
semantic_models.BioSample,
DocumentMixin,
UserIdentifiedObject,
):
pass
model_config = ConfigDict(model_version_latest=1)


class ImageAnnotationDataset(
Expand All @@ -109,6 +154,8 @@ class ImageAnnotationDataset(
):
submitted_in_study_uuid: UUID = Field()

model_config = ConfigDict(model_version_latest=1)


class AnnotationFileReference(
semantic_models.AnnotationFileReference,
Expand All @@ -118,6 +165,8 @@ class AnnotationFileReference(
source_image_uuid: List[UUID] = Field()
creation_process_uuid: List[UUID] = Field()

model_config = ConfigDict(model_version_latest=1)


class DerivedImage(
semantic_models.DerivedImage,
Expand All @@ -127,11 +176,12 @@ class DerivedImage(
submission_dataset_uuid: UUID = Field()
creation_process_uuid: List[UUID] = Field()

model_config = ConfigDict(model_version_latest=1)


class AnnotationMethod(
semantic_models.AnnotationMethod,
DocumentMixin,
UserIdentifiedObject,
):
pass

model_config = ConfigDict(model_version_latest=1)
5 changes: 5 additions & 0 deletions bia-shared-datamodels/src/bia_shared_datamodels/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class UnexpectedDocumentType(Exception):
pass

class ModelDefinitionInvalid(Exception):
pass

0 comments on commit 60cb696

Please sign in to comment.