Skip to content

Main Class#

Bases: BaseModel

Microbial Strain - main class of the new microbial strain data standard.

Config:

  • strict: True
  • extra: allow
  • revalidate_instances: always
  • str_strip_whitespace: True

Fields:

growthConditions: list[GrowthCondition] pydantic-field #

Temperature and pH values

morphType: Morph | None = None pydantic-field #

Applicable and required for fungi only

morphology: list[Morphology] pydantic-field #

Morphology information

origin: list[Origin] pydantic-field #

Sample and isolation data

primaryId: str pydantic-field #

Primary Identifier for this dataset

check_constrains(r_mi: Self) -> bool #

Hard constrains, add more if necessary

Source code in packages/microbial_strain_data_model/src/microbial_strain_data_model/strain.py
def check_constrains(self, r_mi: Self) -> bool:
    """Hard constrains, add more if necessary"""
    # check if organism types are equal
    if self.organismType is not r_mi.organismType:
        raise ValueError(
            f"Organism types do not match: {self.organismType} != {r_mi.organismType}"
        )
    return True

join(to_join: Self, re_validate: bool = False) -> Self #

Joins this instance with another instance of the same type.

Combines data from two Strain instances by first validating constraints between instances, then merging source references and fixing related data, followed by merging related data references, and finally joining all other fields while maintaining uniqueness.

Parameters:

Name Type Description Default

to_join #

Self

Another Strain instance to join with this one

required

Returns:

Name Type Description
Self Self

The modified instance with joined data

Raises:

Type Description
ValueError

If constraint validation fails

Source code in packages/microbial_strain_data_model/src/microbial_strain_data_model/strain.py
def join(self, to_join: Self, re_validate: bool = False, /) -> Self:
    """Joins this instance with another instance of the same type.

    Combines data from two Strain instances by first validating constraints between
    instances, then merging source references and fixing related data, followed by merging
    related data references, and finally joining all other fields while maintaining
    uniqueness.

    Args:
        to_join: Another Strain instance to join with this one

    Returns:
        Self: The modified instance with joined data

    Raises:
        ValueError: If constraint validation fails
    """
    # Step 1
    self.check_constrains(to_join)
    # Step 2
    source_map_l, source_map_r, links = build_link_mapping_and_merge(
        self.sources, to_join.sources, LinkType.source
    )
    self.sources = links
    to_fix = (
        (self.relatedData, source_map_l),
        (to_join.relatedData, source_map_r),
    )
    for rel_dat, src_map in to_fix:
        for rel in rel_dat:
            fix_source(rel, src_map)
    # Step 3
    related_data_map_l, related_data_map_r, links = build_link_mapping_and_merge(
        self.relatedData, to_join.relatedData, LinkType.related_data
    )
    self.relatedData = links
    # Step 4
    not_cached = len(self._cache) == 0
    for field_name in Strain.model_fields.keys():
        if field_name == "sources" or field_name == "relatedData":
            continue
        attr_right = getattr(to_join, field_name)
        attr_left = getattr(self, field_name)
        if not (isinstance(attr_right, list) and isinstance(attr_left, list)):
            continue

        if not_cached:
            joined = list(
                self._join_field_unique(
                    field_name, attr_left, source_map_l, related_data_map_l
                )
            )
            setattr(self, field_name, joined)
            attr_left = joined
        attr_left.extend(
            self._join_field_unique(
                field_name, attr_right, source_map_r, related_data_map_r
            )
        )
    if re_validate:
        self.model_validate(self)
    return self

split(to_split: int | Source, re_validate: bool = False) -> tuple[Self, Self] #

Splits the Strain instance into two distinct instances based on a specific Source.

This method separates the data associated with a specific source (identified by index or object) from the rest of the data. It effectively creates two new strain records: one containing data linked to the specified source, and another containing the remaining data.

The method splits the strain instance into two by identifying a specific source, separating its associated data and links from the rest, and assigning a new UUID to the resulting instance.

Parameters:

Name Type Description Default

to_split #

int | Source

The source to split by. Can be the integer index of the source in the sources list, or the Source object itself.

required

Returns:

Type Description
tuple[Self, Self]

tuple[Self, Self]: A tuple containing two Strain instances. - The first element is the modified original instance (self), containing data not linked to the split source. - The second element is the new instance containing data only linked to the split source.

Raises:

Type Description
IndexError

If the provided integer index is out of bounds or if the provided Source object is not found in the sources list.

Source code in packages/microbial_strain_data_model/src/microbial_strain_data_model/strain.py
def split(
    self, to_split: int | Source, re_validate: bool = False, /
) -> tuple[Self, Self]:
    """Splits the Strain instance into two distinct instances based on a specific Source.

    This method separates the data associated with a specific source (identified by index
    or object) from the rest of the data. It effectively creates two new strain records:
    one containing data linked to the specified source, and another containing the
    remaining data.

    The method splits the strain instance into two by identifying a specific source,
    separating its associated data and links from the rest, and assigning a new
    UUID to the resulting instance.

    Args:
        to_split (int | Source): The source to split by. Can be the integer index of
            the source in the `sources` list, or the `Source` object itself.

    Returns:
        tuple[Self, Self]: A tuple containing two Strain instances.
            - The first element is the modified original instance (self), containing
              data *not* linked to the split source.
            - The second element is the new instance containing data *only* linked
              to the split source.

    Raises:
        IndexError: If the provided integer index is out of bounds or if the
            provided Source object is not found in the `sources` list.
    """
    to_split_index = to_split
    if isinstance(to_split, Source):
        for sid, source in enumerate(self.sources):
            if source._index() == to_split._index():
                to_split_index = sid
                break
    if not (
        isinstance(to_split_index, int) and 0 <= to_split_index < len(self.sources)
    ):
        raise IndexError("Received incorrect source or index")
    return self._split_by_index(to_split_index, re_validate)