Source code for autojob.study_group

"""Create study groups."""

from datetime import UTC
from datetime import datetime
import json
from pathlib import Path
import shutil
from tempfile import TemporaryDirectory
from typing import Any
from typing import ClassVar
from typing import Self

from pydantic import UUID4
from pydantic import BaseModel
from pydantic import ConfigDict
from pydantic import Field
from pydantic import FieldSerializationInfo
from pydantic import SerializerFunctionWrapHandler
from pydantic import field_serializer

from autojob import SETTINGS
from autojob.study import Study
from autojob.utils.schemas import id_factory

STUDY_GROUP_FIELDS = [
    "label",
    "study_group_id",
    "date_created",
]


[docs] class StudyGroup(BaseModel): """A collection of studies.""" date_created: datetime = Field( default_factory=lambda: datetime.now(tz=UTC), description="The date and time that the study group was created", ) study_group_id: UUID4 | str = Field( default_factory=id_factory("g"), description="A unique identifier for the study group", union_mode="left_to_right", ) studies: list[Study] = Field( default=[], description="The studies in the study group" ) name: str = Field(default="", description="The name of the study group") notes: list[str] = Field( default=[], description="A list of notes about the study group" ) model_config: ClassVar = ConfigDict(populate_by_name=True)
[docs] @field_serializer( "studies", mode="wrap", return_type=list[Study] | list[str] ) def serialize_studies( self, v: Any, _: SerializerFunctionWrapHandler, info: FieldSerializationInfo, ) -> list[Study] | list[str]: """Serialize the studies in the study group.""" if info.mode == "json": return [str(s.study_id) for s in self.studies] return v
[docs] @classmethod def from_directory( cls, dir_name: Path, *, strict_mode: bool | None = None ) -> Self: """Create a study group from a directory. Args: dir_name: The directory of a study group. strict_mode: Whether or not to require all outputs. If True, errors will be thrown on missing outputs. Defaults to ``SETTINGS.STRICT_MODE``. """ strict_mode = ( SETTINGS.STRICT_MODE if strict_mode is None else strict_mode ) metadata_file = Path(dir_name, SETTINGS.STUDY_GROUP_METADATA_FILE) with metadata_file.open(mode="r", encoding="utf-8") as file: metadata: dict[str, Any] = json.load(file) studies: list[Study] = [] for study in metadata["studies"]: source = Path(dir_name, study) studies.append( Study.from_directory(source, strict_mode=strict_mode) ) metadata["studies"] = studies return cls(**metadata)
[docs] def to_directory(self, src: Path) -> None: """Create a directory for a study group. Args: src: The directory in which to dump the :class:`StudyGroup`. """ with TemporaryDirectory() as tmpdir: metadata = self.model_dump(mode="json") study_group_path = Path(tmpdir, str(self.study_group_id)) study_group_path.mkdir() for study in sorted(self.studies, key=lambda s: s.study_id): dest = Path(study_group_path, str(study.study_id)) study.to_directory(dest=dest) metadata_file = Path( study_group_path, SETTINGS.STUDY_GROUP_METADATA_FILE, ) with metadata_file.open(mode="w", encoding="utf-8") as file: json.dump(metadata, file, indent=4) shutil.copytree(study_group_path, src, dirs_exist_ok=True)