Skip to content

Commit

Permalink
example new test format (#136)
Browse files Browse the repository at this point in the history
* example new test format

* generalised tests to all classes

* split tests into 2 using test class and change complete dictionary creation
  • Loading branch information
sherwoodf authored Aug 6, 2024
1 parent 80bb8d7 commit 2570be2
Show file tree
Hide file tree
Showing 3 changed files with 402 additions and 358 deletions.
41 changes: 26 additions & 15 deletions bia-shared-datamodels/src/bia_shared_datamodels/semantic_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,14 @@ class Study(ConfiguredBaseModel):
)
release_date: date = Field(description="""Date of first publication""")
description: str = Field(
None, description="""Brief description of the study."""
description="""Brief description of the study."""
)
keyword: Optional[List[str]] = Field(
default_factory=list,
description="""Keywords or tags used to describe the subject or context of the study.""",
)
acknowledgement: Optional[str] = Field(
default_factory=list,
None,
description="""Any person or group that should be acknowledged outside of the authors/main contributors to the study.""",
)

Expand All @@ -55,7 +55,7 @@ class Study(ConfiguredBaseModel):
default_factory=list, description="""The grants that funded the study."""
)
funding_statement: Optional[str] = Field(
default_factory=list, description="""Description of how the study was funded."""
None, description="""Description of how the study was funded."""
)

# TODO: In order to maintian consistency these will be endpoints that run a query in the DB, rather than a stored field.
Expand Down Expand Up @@ -164,7 +164,7 @@ class OrganisationMixin(ConfiguredBaseModel):
None, description="""Comma separated lines of the address."""
)
website: Optional[AnyUrl] = Field(
default=None,
None,
description="""The website page with information about the Organisation.""",
)

Expand All @@ -178,14 +178,13 @@ class Contributor(PersonMixin, OrganisationMixin):
description="""Name as it should be displayed on the BioImage Archive."""
)
affiliation: List[Affiliation] = Field(
default_factory=list,
description="""The organisation(s) a contributor is afiliated with.""",
)
contact_email: Optional[EmailStr] = Field(
default=None, description="""An email address to contact the Contributor."""
None, description="""An email address to contact the Contributor."""
)
role: Optional[str] = Field(
default=None, description="""The role of the contributor."""
None, description="""The role of the contributor."""
)


Expand Down Expand Up @@ -329,7 +328,7 @@ class ImageRepresentation(ConfiguredBaseModel):
description="""Size of temporal dimension of the data array of the image.""",
)
image_viewer_setting: Optional[List[RenderedView]] = Field(
None,
default_factory=list,
description="""Settings of a particular view of an image, such as a specific timestamp of a timeseries, or camera placement in a 3D model.""",
)
# TODO: representation_of information is stored in representation_of_uuid defined in bia_data_model.
Expand All @@ -356,7 +355,7 @@ class RenderedView(ConfiguredBaseModel):
None, description="""A t-value for the timestamp of the image view"""
)
channel_information: Optional[List[Channel]] = Field(
None,
default_factory=list,
description="""Information about the channels involved in displaying this view of the image.""",
)

Expand All @@ -368,7 +367,7 @@ class Channel(ConfiguredBaseModel):

colormap_start: float = Field(description="""Start value of colormap""")
colormap_end: float = Field(description="""End value of colormap""")
scale_factor: float = Field(None)
scale_factor: Optional[float] = Field(None)
label: Optional[str] = Field(
None, description="""Label describing the channel for display."""
)
Expand Down Expand Up @@ -399,9 +398,11 @@ class ExperimentalImagingDataset(DatasetMixin):
# description="""Processes involved in the growth of the samples that were then imaged."""
# )
analysis_method: Optional[list[ImageAnalysisMethod]] = Field(
default_factory=list,
description="""Data analysis processes performed on the images."""
)
correlation_method: Optional[list[ImageCorrelationMethod]] = Field(
default_factory=list,
description="""Processes performed to correlate image data."""
)
example_image_uri: list[str] = Field(
Expand Down Expand Up @@ -441,6 +442,7 @@ class ImageAcquisition(ProtocolMixin):
description="""Names, types, or description of how the instruments used to create the image."""
)
fbbi_id: Optional[List[str]] = Field(
default_factory=list,
description="""Biological Imaging Methods Ontology id indicating the kind of imaging that was perfomed."""
)
imaging_method_name: Optional[str] = Field(
Expand All @@ -453,7 +455,10 @@ class SpecimenImagingPrepartionProtocol(ProtocolMixin):
The process to prepare biological entity for imaging.
"""

signal_channel_information: Optional[List[SignalChannelInformation]]
signal_channel_information: Optional[List[SignalChannelInformation]] = Field(
default_factory=list,
description="""Information about how channels in the image relate to image signal generation."""
)


class SignalChannelInformation(ConfiguredBaseModel):
Expand Down Expand Up @@ -512,12 +517,15 @@ class BioSample(ConfiguredBaseModel):
description="""A short description of the biological entity."""
)
experimental_variable_description: Optional[List[str]] = Field(
default_factory=list,
description="""What is intentionally varied (e.g. time) between multiple entries in this study component"""
)
extrinsic_variable_description: Optional[List[str]] = Field(
default_factory=list,
description="External treatment (e.g. reagent)."
)
intrinsic_variable_description: Optional[List[str]] = Field(
default_factory=list,
description="Intrinsic (e.g. genetic) alteration."
)

Expand All @@ -527,9 +535,9 @@ class Taxon(ConfiguredBaseModel):
The classification of a biological entity.
"""

common_name: Optional[str] = Field(None)
scientific_name: Optional[str] = Field(None)
ncbi_id: Optional[str] = Field(None)
common_name: Optional[str] = Field(None, description="""Name used to refer to the species that can vary by locallity.""")
scientific_name: Optional[str] = Field(None, description="""unique name used by the scientific community to identify species.""")
ncbi_id: Optional[str] = Field(None, description="""unique name used by the scientific community to identify species.""")


class ImageAnalysisMethod(ProtocolMixin):
Expand Down Expand Up @@ -584,9 +592,10 @@ class AnnotationMethod(ProtocolMixin):
# description="""The datasets that were annotated."""
# )
annotation_criteria: Optional[str] = Field(
description="""Rules used to generate annotations."""
None, description="""Rules used to generate annotations."""
)
annotation_coverage: Optional[str] = Field(
None,
description="""Which images from the dataset were annotated, and what percentage of the data has been annotated from what is available."""
)
method_type: AnnotationType = Field(
Expand All @@ -608,9 +617,11 @@ class AnnotationMixin(ConfiguredBaseModel):
# description="""The process that was followed to create the annotation."""
# )
transformation_description: Optional[str] = Field(
None,
description="""Any transformations required to link annotations to the image."""
)
spatial_information: Optional[str] = Field(
None,
description="""Spatial information for non-pixel annotations."""
)

Expand Down
147 changes: 78 additions & 69 deletions bia-shared-datamodels/test/test_shared_models.py
Original file line number Diff line number Diff line change
@@ -1,97 +1,106 @@
from uuid import uuid4
import pytest
from pydantic import ValidationError
from pydantic import ValidationError, BaseModel
from . import utils
from bia_shared_datamodels import bia_data_model


from bia_shared_datamodels import bia_data_model, semantic_models
from typing import Callable

@pytest.mark.parametrize(
("expected_model_type", "dict_creation_func"),
(
"expected_model_type",
"model_creation_func",
),
(
(
bia_data_model.Study,
utils.get_template_study,
),
(
bia_data_model.FileReference,
utils.get_template_file_reference,
),
(semantic_models.Taxon, utils.get_taxon_dict),
(semantic_models.Channel, utils.get_channel_dict),
(semantic_models.RenderedView, utils.get_rendered_view_dict),
(
bia_data_model.ImageRepresentation,
utils.get_template_image_representation,
semantic_models.SignalChannelInformation,
utils.get_signal_channel_information_dict,
),
(
bia_data_model.ExperimentalImagingDataset,
utils.get_template_experimental_imaging_dataset,
bia_data_model.SpecimenImagingPrepartionProtocol,
utils.get_specimen_imaging_preparation_protocol_dict,
),
(
bia_data_model.Specimen,
utils.get_template_specimen,
bia_data_model.SpecimenGrowthProtocol,
utils.get_specimen_growth_protocol_dict,
),
(bia_data_model.BioSample, utils.get_biosample_dict),
(bia_data_model.Specimen, utils.get_specimen_dict),
(bia_data_model.AnnotationMethod, utils.get_annotation_method_dict),
(
bia_data_model.ExperimentallyCapturedImage,
utils.get_template_experimentally_captured_image,
),
(
bia_data_model.ImageAcquisition,
utils.get_template_image_acquisition,
),
(
bia_data_model.SpecimenImagingPrepartionProtocol,
utils.get_template_specimen_imaging_preparation_protocol,
),
(
bia_data_model.BioSample,
utils.get_template_biosample,
utils.get_experimentally_captured_image_dict,
),
(bia_data_model.DerivedImage, utils.get_derived_image_dict),
(
bia_data_model.ImageAnnotationDataset,
utils.get_template_image_annotation_dataset,
),
(
bia_data_model.AnnotationFileReference,
utils.get_template_annotation_file_reference,
utils.get_image_annotation_dataset_dict,
),
(bia_data_model.ImageAcquisition, utils.get_image_acquisition_dict),
(semantic_models.ImageAnalysisMethod, utils.get_image_analysis_method_dict),
(
bia_data_model.DerivedImage,
utils.get_template_derived_image,
semantic_models.ImageCorrelationMethod,
utils.get_image_correlation_method_dict,
),
(
bia_data_model.AnnotationMethod,
utils.get_template_annotation_method,
bia_data_model.ExperimentalImagingDataset,
utils.get_experimental_imaging_dataset_dict,
),
(
bia_data_model.SpecimenGrowthProtocol,
utils.get_template_specimen_growth_protocol,
bia_data_model.AnnotationFileReference,
utils.get_annotation_file_reference_dict,
),
(bia_data_model.FileReference, utils.get_file_reference_dict),
(bia_data_model.ImageRepresentation, utils.get_image_representation_dict),
(semantic_models.Affiliation, utils.get_affiliation_dict),
(semantic_models.Contributor, utils.get_contributor_dict),
(bia_data_model.Study, utils.get_study_dict),
),
)
def test_create_models(expected_model_type, model_creation_func):
expected_model = model_creation_func()
assert type(expected_model) is expected_model_type

class TestCreateObject:
def test_create_complete_object(
self,
expected_model_type: BaseModel,
dict_creation_func: Callable[[utils.Completeness], dict],
):
complete_dict = dict_creation_func(utils.Completeness.COMPLETE)
complete_model: BaseModel = expected_model_type(**complete_dict)

def test_create_specimen_with_empty_lists_fails():
with pytest.raises(ValidationError):
specimen = bia_data_model.Specimen.model_validate(
{
"sample_of": [],
"preparation_method": [],
}
# Check that the model is created
assert type(complete_model) is expected_model_type
# Check that the dictionary is indeed "Complete" - no optional fields missed
assert complete_model.model_dump() == complete_dict
# Check that there are no inconsistencies in the model definition
assert (
type(expected_model_type(**complete_model.model_dump()))
is expected_model_type
)
specimen = bia_data_model.Specimen.model_validate(
{
"sample_of": [uuid4()],
"preparation_method": [],
}
)
specimen = bia_data_model.Specimen.model_validate(
{
"sample_of": [],
"preparation_method": [uuid4()],
}


def test_create_minimal_object(
self,
expected_model_type: BaseModel,
dict_creation_func: Callable[[utils.Completeness], dict],
):
mimimal_dict = dict_creation_func(utils.Completeness.MINIMAL)
minimal_model: BaseModel = expected_model_type(**mimimal_dict)

# Check that the model is created
assert type(minimal_model) is expected_model_type
# Check that the dictionary is indeed "Minimal" - no optional fields included, and list fields are of minimum length
for key in mimimal_dict:
less_than_minimal_dict = mimimal_dict.copy()
if (
isinstance(less_than_minimal_dict[key], list)
and len(less_than_minimal_dict[key]) > 0
):
less_than_minimal_dict[key] = []
with pytest.raises(ValidationError):
expected_model_type(**less_than_minimal_dict)

del less_than_minimal_dict[key]
with pytest.raises(ValidationError):
expected_model_type(**less_than_minimal_dict)
# Check that there are no inconsistencies in the model definition's optional fields
assert (
type(expected_model_type(**minimal_model.model_dump()))
is expected_model_type
)
Loading

0 comments on commit 2570be2

Please sign in to comment.