Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

removing old sample and order models pt 1 #4097

Merged
merged 8 commits into from
Jan 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
283 changes: 1 addition & 282 deletions cg/models/orders/samples.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

from cg.constants import DataDelivery
from cg.constants.constants import GenomeVersion, Workflow
from cg.constants.orderforms import ORIGINAL_LAB_ADDRESSES, REGION_CODES
from cg.models.orders.constants import OrderType
from cg.models.orders.sample_base import (
NAME_PATTERN,
Expand All @@ -12,19 +11,7 @@
SexEnum,
StatusEnum,
)
from cg.store.models import Application, Case, Organism, Panel, Pool, Sample


class OptionalIntValidator:
@classmethod
def str_to_int(cls, v: str) -> int | None:
return int(v) if v else None


class OptionalFloatValidator:
@classmethod
def str_to_float(cls, v: str) -> float | None:
return float(v) if v else None
from cg.store.models import Application, Case, Panel, Sample


class OrderInSample(BaseModel):
Expand All @@ -51,274 +38,6 @@ def is_sample_for(cls, project: OrderType):
return project == cls._suitable_project


class Of1508Sample(OrderInSample):
# Orderform 1508
# Order portal specific
internal_id: constr(max_length=Sample.internal_id.property.columns[0].type.length) | None
# "required for new samples"
name: (
constr(
regex=NAME_PATTERN,
min_length=2,
max_length=Sample.name.property.columns[0].type.length,
)
| None
)

# customer
age_at_sampling: float | None
family_name: constr(
regex=NAME_PATTERN,
min_length=2,
max_length=Case.name.property.columns[0].type.length,
)
case_internal_id: constr(max_length=Sample.internal_id.property.columns[0].type.length) | None
sex: SexEnum = SexEnum.unknown
tumour: bool = False
source: str | None
control: ControlEnum | None
volume: str | None
container: ContainerEnum | None
# "required if plate for new samples"
container_name: str | None
well_position: str | None
# "Required if samples are part of trio/family"
mother: (
constr(regex=NAME_PATTERN, max_length=Sample.name.property.columns[0].type.length) | None
)
father: (
constr(regex=NAME_PATTERN, max_length=Sample.name.property.columns[0].type.length) | None
)
# This information is required for panel analysis
capture_kit: str | None
# This information is required for panel- or exome analysis
elution_buffer: str | None
tumour_purity: int | None
# "This information is optional for FFPE-samples for new samples"
formalin_fixation_time: int | None
post_formalin_fixation_time: int | None
tissue_block_size: str | None
# "Not Required"
cohorts: list[str] | None
phenotype_groups: list[str] | None
phenotype_terms: list[str] | None
require_qc_ok: bool = False
quantity: int | None
subject_id: (
constr(regex=NAME_PATTERN, max_length=Sample.subject_id.property.columns[0].type.length)
| None
)
synopsis: str | None

@validator("container", "container_name", "name", "source", "subject_id", "volume")
def required_for_new_samples(cls, value, values, **kwargs):
if not value and not values.get("internal_id"):
raise ValueError(f"required for new sample {values.get('name')}")
return value

@validator(
"tumour_purity",
"formalin_fixation_time",
"post_formalin_fixation_time",
"quantity",
pre=True,
)
def str_to_int(cls, v: str) -> int | None:
return OptionalIntValidator.str_to_int(v=v)

@validator(
"age_at_sampling",
"volume",
pre=True,
)
def str_to_float(cls, v: str) -> float | None:
return OptionalFloatValidator.str_to_float(v=v)


class MipDnaSample(Of1508Sample):
_suitable_project = OrderType.MIP_DNA
# "Required if data analysis in Scout or vcf delivery"
panels: list[constr(min_length=1, max_length=Panel.abbrev.property.columns[0].type.length)]
status: StatusEnum


class BalsamicSample(Of1508Sample):
_suitable_project = OrderType.BALSAMIC


class BalsamicQCSample(Of1508Sample):
_suitable_project = OrderType.BALSAMIC_QC
reference_genome: GenomeVersion | None


class BalsamicUmiSample(Of1508Sample):
_suitable_project = OrderType.BALSAMIC_UMI


class MipRnaSample(Of1508Sample):
_suitable_project = OrderType.MIP_RNA


class RnafusionSample(Of1508Sample):
_suitable_project = OrderType.RNAFUSION


class TomteSample(MipDnaSample):
_suitable_project = OrderType.TOMTE
reference_genome: GenomeVersion | None


class FastqSample(OrderInSample):
_suitable_project = OrderType.FASTQ

# Orderform 1508
# "required"
container: ContainerEnum | None
sex: SexEnum = SexEnum.unknown
source: str
tumour: bool
# "required if plate"
container_name: str | None
well_position: str | None
elution_buffer: str
# This information is required for panel analysis
capture_kit: str | None
# "Not Required"
quantity: int | None
subject_id: str | None

@validator("quantity", pre=True)
def str_to_int(cls, v: str) -> int | None:
return OptionalIntValidator.str_to_int(v=v)


class PacBioSample(OrderInSample):
_suitable_project = OrderType.PACBIO_LONG_READ

container: ContainerEnum
container_name: str | None = None
sex: SexEnum = SexEnum.unknown
source: str
subject_id: str
tumour: bool
well_position: str | None = None


class RmlSample(OrderInSample):
_suitable_project = OrderType.RML

# 1604 Orderform Ready made libraries (RML)
# Order portal specific
# "This information is required"
pool: constr(max_length=Pool.name.property.columns[0].type.length)
concentration: float
concentration_sample: float | None
index: str
index_number: str | None
# "Required if Plate"
rml_plate_name: str | None
well_position_rml: str | None
# "Automatically generated (if not custom) or custom"
index_sequence: str | None
# "Not required"
control: str | None

@validator("concentration_sample", pre=True)
def str_to_float(cls, v: str) -> float | None:
return OptionalFloatValidator.str_to_float(v=v)


class FluffySample(RmlSample):
_suitable_project = OrderType.FLUFFY
# 1604 Orderform Ready made libraries (RML)


class MetagenomeSample(Of1508Sample):
_suitable_project = OrderType.METAGENOME
# "This information is required"
source: str
# "This information is not required"
concentration_sample: float | None
family_name: None = None
subject_id: None = None

@validator("concentration_sample", pre=True)
def str_to_float(cls, v: str) -> float | None:
return OptionalFloatValidator.str_to_float(v=v)

@validator("subject_id", pre=True)
def required_for_new_samples(cls, v: str) -> None:
"""Overrides the parent validator since subject_id is optional for these samples."""
return None


class TaxprofilerSample(MetagenomeSample):
_suitable_project = OrderType.TAXPROFILER


class MicrobialSample(OrderInSample):
# 1603 Orderform Microbial WHOLE_GENOME_SEQUENCING
# "These fields are required"
organism: constr(max_length=Organism.internal_id.property.columns[0].type.length)
reference_genome: constr(max_length=Sample.reference_genome.property.columns[0].type.length)
elution_buffer: str
extraction_method: str | None
container: ContainerEnum
# "Required if Plate"
container_name: str | None
well_position: str | None
# "Required if "Other" is chosen in column "Species""
organism_other: constr(max_length=Organism.internal_id.property.columns[0].type.length) | None
verified_organism: bool | None # sent to LIMS
control: str | None


class MicrobialFastqSample(OrderInSample):
_suitable_project = OrderType.MICROBIAL_FASTQ

elution_buffer: str
container: ContainerEnum
# "Required if Plate"
container_name: str | None
well_position: str | None
# "These fields are not required"
control: str | None


class MicrosaltSample(MicrobialSample):
_suitable_project = OrderType.MICROSALT
# 1603 Orderform Microbial WHOLE_GENOME_SEQUENCING


class SarsCov2Sample(MicrobialSample):
_suitable_project = OrderType.SARS_COV_2

# 2184 Orderform SARS-COV-2
# "These fields are required"
collection_date: str
lab_code: str = None
primer: str
original_lab: str
original_lab_address: str = None
pre_processing_method: str
region: str
region_code: str = None
selection_criteria: str
volume: str | None

@validator("lab_code", pre=True, always=True)
def set_lab_code(cls, value):
return "SE100 Karolinska"
diitaz93 marked this conversation as resolved.
Show resolved Hide resolved

@validator("region_code", pre=True, always=True)
def set_region_code(cls, value, values):
return value if value else REGION_CODES[values["region"]]

@validator("original_lab_address", pre=True, always=True)
def set_original_lab_address(cls, value, values):
return value if value else ORIGINAL_LAB_ADDRESSES[values["original_lab"]]
diitaz93 marked this conversation as resolved.
Show resolved Hide resolved


def sample_class_for(project: OrderType):
"""Get the sample class for the specified project

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@


def is_sample_missing_capture_kit(sample: BalsamicSample, store: Store) -> bool:
application: Application = store.get_application_by_tag(sample.application)
application: Application | None = store.get_application_by_tag(sample.application)
return (
application.prep_category == SeqLibraryPrepCategory.TARGETED_GENOME_SEQUENCING
application
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this fixes a bg in the validation, if the application does not exists, the validation breaks

and application.prep_category == SeqLibraryPrepCategory.TARGETED_GENOME_SEQUENCING
and not sample.capture_kit
)
11 changes: 0 additions & 11 deletions cg/services/orders/store_order_services/store_order_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import logging
from abc import ABC, abstractmethod

from cg.models.orders.order import OrderIn
from cg.services.order_validation_service.models.order import Order
from cg.services.order_validation_service.models.sample import Sample
from cg.services.orders.order_lims_service.order_lims_service import OrderLimsService
Expand All @@ -12,16 +11,6 @@
LOG = logging.getLogger(__name__)


class ValidateOrderService(ABC):
@abstractmethod
def __init__(self, status_db: Store):
self.status_db = status_db

@abstractmethod
def validate_order(self, order_in: OrderIn):
pass


class StoreOrderService(ABC):
@abstractmethod
def __init__(self, status_db: Store, lims_service: OrderLimsService):
Expand Down
1 change: 1 addition & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@
"tests.fixture_plugins.orders_fixtures.order_form_fixtures",
"tests.fixture_plugins.orders_fixtures.order_to_submit_fixtures",
"tests.fixture_plugins.orders_fixtures.order_fixtures",
"tests.fixture_plugins.orders_fixtures.services_and_api_fixtures",
"tests.fixture_plugins.orders_fixtures.store_fixtures",
"tests.fixture_plugins.orders_fixtures.store_service_fixtures",
"tests.fixture_plugins.pacbio_fixtures.context_fixtures",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
from cg.services.order_validation_service.workflows.pacbio_long_read.models.order import PacbioOrder
from cg.services.order_validation_service.workflows.rml.models.order import RmlOrder
from cg.services.order_validation_service.workflows.rna_fusion.models.order import RnaFusionOrder
from cg.services.order_validation_service.workflows.taxprofiler.models.order import TaxprofilerOrder
from cg.services.order_validation_service.workflows.tomte.models.order import TomteOrder


@pytest.fixture(scope="session")
Expand Down Expand Up @@ -56,7 +58,7 @@ def metagenome_order_to_submit(cgweb_orders_dir: Path) -> dict:
)


@pytest.fixture
@pytest.fixture(scope="session")
def microbial_fastq_order_to_submit(cgweb_orders_dir: Path) -> dict:
"""Load an example microbial order."""
return ReadFile.get_content_from_file(
Expand Down Expand Up @@ -120,6 +122,14 @@ def sarscov2_order_to_submit(cgweb_orders_dir: Path) -> dict:
)


@pytest.fixture(scope="session")
def taxprofiler_order_to_submit(cgweb_orders_dir: Path) -> dict:
"""Load an example Taxprofiler order."""
return ReadFile.get_content_from_file(
file_format=FileFormat.JSON, file_path=Path(cgweb_orders_dir, "taxprofiler.json")
)


@pytest.fixture(scope="session")
def tomte_order_to_submit(cgweb_orders_dir: Path) -> dict:
"""Load an example TOMTE order."""
Expand All @@ -142,6 +152,8 @@ def all_orders_to_submit(
rml_order_to_submit: dict,
rnafusion_order_to_submit: dict,
sarscov2_order_to_submit: dict,
taxprofiler_order_to_submit: dict,
tomte_order_to_submit: dict,
) -> dict[OrderType, Order]:
"""Returns a dict of parsed order for each order type."""
return {
Expand All @@ -159,4 +171,6 @@ def all_orders_to_submit(
OrderType.RML: RmlOrder.model_validate(rml_order_to_submit),
OrderType.RNAFUSION: RnaFusionOrder.model_validate(rnafusion_order_to_submit),
OrderType.SARS_COV_2: MutantOrder.model_validate(sarscov2_order_to_submit),
OrderType.TAXPROFILER: TaxprofilerOrder.model_validate(taxprofiler_order_to_submit),
OrderType.TOMTE: TomteOrder.model_validate(tomte_order_to_submit),
}
Loading
Loading