diff --git a/cg/services/orders/validate_order_services/validate_case_order.py b/cg/services/orders/validate_order_services/validate_case_order.py deleted file mode 100644 index 24fe4a5376..0000000000 --- a/cg/services/orders/validate_order_services/validate_case_order.py +++ /dev/null @@ -1,102 +0,0 @@ -from cg.exc import OrderError -from cg.models.orders.constants import OrderType -from cg.models.orders.order import OrderIn -from cg.models.orders.samples import Of1508Sample, OrderInSample -from cg.services.orders.store_order_services.store_order_service import ValidateOrderService -from cg.store.models import Customer, Sample -from cg.store.store import Store - - -class ValidateCaseOrderService(ValidateOrderService): - - def __init__(self, status_db: Store): - self.status_db = status_db - - def validate_order(self, order: OrderIn) -> None: - self._validate_subject_sex(samples=order.samples, customer_id=order.customer) - self._validate_samples_available_to_customer( - samples=order.samples, customer_id=order.customer - ) - self._validate_case_names_are_unique(samples=order.samples, customer_id=order.customer) - if order.order_type == OrderType.RNAFUSION: - self._validate_only_one_sample_per_case(samples=order.samples) - - def _validate_subject_sex(self, samples: [Of1508Sample], customer_id: str): - """Validate that sex is consistent with existing samples, skips samples of unknown sex - - Args: - samples (list[dict]): Samples to validate - customer_id (str): Customer that the samples belong to - Returns: - Nothing - """ - sample: Of1508Sample - for sample in samples: - subject_id: str = sample.subject_id - if not subject_id: - continue - new_gender: str = sample.sex - if new_gender == "unknown": - continue - - existing_samples: list[Sample] = self.status_db.get_samples_by_customer_and_subject_id( - customer_internal_id=customer_id, subject_id=subject_id - ) - existing_sample: Sample - for existing_sample in existing_samples: - previous_gender = existing_sample.sex - if previous_gender == "unknown": - continue - - if previous_gender != new_gender: - raise OrderError( - f"Sample gender inconsistency for subject_id: {subject_id}: previous gender {previous_gender}, new gender {new_gender}" - ) - - def _validate_samples_available_to_customer( - self, samples: list[OrderInSample], customer_id: str - ) -> None: - """Validate that the customer have access to all samples""" - sample: Of1508Sample - for sample in samples: - if not sample.internal_id: - continue - - existing_sample: Sample = self.status_db.get_sample_by_internal_id( - internal_id=sample.internal_id - ) - - data_customer: Customer = self.status_db.get_customer_by_internal_id( - customer_internal_id=customer_id - ) - - if existing_sample.customer not in data_customer.collaborators: - raise OrderError(f"Sample not available: {sample.name}") - - def _validate_case_names_are_unique( - self, samples: list[OrderInSample], customer_id: str - ) -> None: - """Validate that the names of all cases are unused for all samples""" - - customer: Customer = self.status_db.get_customer_by_internal_id( - customer_internal_id=customer_id - ) - - sample: Of1508Sample - for sample in samples: - if self._is_rerun_of_existing_case(sample=sample): - continue - if self.status_db.get_case_by_name_and_customer( - customer=customer, case_name=sample.family_name - ): - raise OrderError(f"Case name {sample.family_name} already in use") - - @staticmethod - def _is_rerun_of_existing_case(sample: Of1508Sample) -> bool: - return sample.case_internal_id is not None - - @staticmethod - def _validate_only_one_sample_per_case(samples: list[Of1508Sample]) -> None: - """Validates that each case contains only one sample.""" - if len({sample.family_name for sample in samples}) != len(samples): - raise OrderError("Each case in an RNAFUSION order must have exactly one sample.") diff --git a/tests/fixture_plugins/orders_fixtures/order_to_submit_fixtures.py b/tests/fixture_plugins/orders_fixtures/order_to_submit_fixtures.py index d81ecbeb8c..deb3c632b8 100644 --- a/tests/fixture_plugins/orders_fixtures/order_to_submit_fixtures.py +++ b/tests/fixture_plugins/orders_fixtures/order_to_submit_fixtures.py @@ -21,6 +21,7 @@ from cg.services.order_validation_service.workflows.mutant.models.order import MutantOrder 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 @pytest.fixture(scope="session") @@ -156,6 +157,6 @@ def all_orders_to_submit( OrderType.MIP_RNA: MipRnaOrder.model_validate(mip_rna_order_to_submit), OrderType.PACBIO_LONG_READ: PacbioOrder.model_validate(pacbio_order_to_submit), OrderType.RML: RmlOrder.model_validate(rml_order_to_submit), - # OrderType.RNAFUSION: RnaFusionOrder.model_validate(rnafusion_order_to_submit), + OrderType.RNAFUSION: RnaFusionOrder.model_validate(rnafusion_order_to_submit), OrderType.SARS_COV_2: MutantOrder.model_validate(sarscov2_order_to_submit), } diff --git a/tests/meta/orders/test_meta_orders_api.py b/tests/meta/orders/test_meta_orders_api.py index ddd6e2b44b..4767454cf2 100644 --- a/tests/meta/orders/test_meta_orders_api.py +++ b/tests/meta/orders/test_meta_orders_api.py @@ -20,7 +20,6 @@ from cg.services.order_validation_service.workflows.balsamic.models.order import BalsamicOrder from cg.services.order_validation_service.workflows.mip_dna.models.order import MipDnaOrder from cg.services.order_validation_service.workflows.mip_rna.models.order import MipRnaOrder -from cg.services.orders.validate_order_services.validate_case_order import ValidateCaseOrderService from cg.store.models import Case, Customer, Pool, Sample from cg.store.store import Store from tests.store_helpers import StoreHelpers @@ -416,131 +415,6 @@ def test_submit_unique_sample_case_name( # Then no exception about duplicate names should be thrown -def test_validate_sex_inconsistent_sex( - orders_api: OrdersAPI, mip_dna_order_to_submit: dict, helpers: StoreHelpers -): - # GIVEN we have an order with a sample that is already in the database but with different sex - order_data = MipDnaOrder.model_validate(mip_dna_order_to_submit) - store = orders_api.validation_service.store - customer: Customer = store.get_customer_by_internal_id(customer_internal_id=order_data.customer) - - # add sample with different sex than in order - samples: list[Sample] = [sample for _, _, sample in order_data.enumerated_new_samples] - for sample in samples: - sample_obj: Sample = helpers.add_sample( - store=store, - customer_id=customer.internal_id, - sex=Sex.MALE if sample.sex == Sex.FEMALE else Sex.FEMALE, - name=sample.name, - subject_id=sample.subject_id, - ) - store.session.add(sample_obj) - store.session.commit() - assert sample_obj.sex != sample.sex - - validator = ValidateCaseOrderService(status_db=store) - - # WHEN calling _validate_sex - # THEN an OrderError should be raised on non-matching sex - with pytest.raises(OrderError): - validator._validate_subject_sex(samples=samples, customer_id=order_data.customer) - - -def test_validate_sex_consistent_sex( - orders_api: OrdersAPI, mip_dna_order_to_submit: dict, helpers: StoreHelpers -): - # GIVEN we have an order with a sample that is already in the database and with same gender - order_data = MipDnaOrder.model_validate(mip_dna_order_to_submit) - store = orders_api.validation_service.store - customer: Customer = store.get_customer_by_internal_id(customer_internal_id=order_data.customer) - - # add sample with different sex than in order - samples: list[Sample] = [sample for _, _, sample in order_data.enumerated_new_samples] - for sample in samples: - sample_obj: Sample = helpers.add_sample( - store=store, - customer_id=customer.internal_id, - sex=sample.sex, - name=sample.name, - subject_id=sample.subject_id, - ) - store.session.add(sample_obj) - store.session.commit() - assert sample_obj.sex == sample.sex - - validator = ValidateCaseOrderService(status_db=store) - - # WHEN calling _validate_sex - validator._validate_subject_sex(samples=samples, customer_id=order_data.customer) - - # THEN no OrderError should be raised on non-matching sex - - -def test_validate_sex_unknown_existing_sex( - orders_api: OrdersAPI, mip_dna_order_to_submit: dict, helpers: StoreHelpers -): - # GIVEN we have an order with a sample that is already in the database and with different gender but the existing is - # of type "unknown" - order_data = MipDnaOrder.model_validate(mip_dna_order_to_submit) - store = orders_api.validation_service.store - customer: Customer = store.get_customer_by_internal_id(customer_internal_id=order_data.customer) - - # add sample with different sex than in order - samples: list[Sample] = [sample for _, _, sample in order_data.enumerated_new_samples] - for sample in samples: - sample_obj: Sample = helpers.add_sample( - store=store, - customer_id=customer.internal_id, - sex=Sex.UNKNOWN, - name=sample.name, - subject_id=sample.subject_id, - ) - store.session.add(sample_obj) - store.session.commit() - assert sample_obj.sex != sample.sex - - validator = ValidateCaseOrderService(store) - - # WHEN calling _validate_sex - validator._validate_subject_sex(samples=samples, customer_id=order_data.customer) - - # THEN no OrderError should be raised on non-matching sex - - -def test_validate_sex_unknown_new_sex( - orders_api: OrdersAPI, mip_dna_order_to_submit: dict, helpers: StoreHelpers -): - # GIVEN we have an order with a sample that is already in the database and with different gender but the new is of - # type "unknown" - order_data = MipDnaOrder.model_validate(mip_dna_order_to_submit) - store = orders_api.validation_service.store - customer: Customer = store.get_customer_by_internal_id(customer_internal_id=order_data.customer) - - # add sample with different sex than in order - samples: list[Sample] = [sample for _, _, sample in order_data.enumerated_new_samples] - for sample in samples: - sample_obj: Sample = helpers.add_sample( - store=store, - customer_id=customer.internal_id, - sex=sample.sex, - name=sample.name, - subject_id=sample.subject_id, - ) - sample.sex = "unknown" - store.session.add(sample_obj) - store.session.commit() - - for sample in samples: - assert sample_obj.sex != sample.sex - - validator = ValidateCaseOrderService(store) - - # WHEN calling _validate_sex - validator._validate_subject_sex(samples=samples, customer_id=order_data.customer) - - # THEN no OrderError should be raised on non-matching sex - - @pytest.mark.xfail(reason="Change in order validation") @pytest.mark.parametrize( "order_type", diff --git a/tests/services/order_validation_service/test_case_sample_rules.py b/tests/services/order_validation_service/test_case_sample_rules.py index 5ba5088aa9..dd040b9812 100644 --- a/tests/services/order_validation_service/test_case_sample_rules.py +++ b/tests/services/order_validation_service/test_case_sample_rules.py @@ -405,6 +405,77 @@ def test_validate_sex_subject_id_clash(valid_order: OrderWithCases, sample_store assert isinstance(errors[0], SexSubjectIdError) +def test_validate_sex_subject_id_no_clash(valid_order: OrderWithCases, sample_store: Store): + # GIVEN an existing sample + sample = sample_store.session.query(Sample).first() + + # GIVEN an order and sample with the same customer and subject id + valid_order.customer = sample.customer.internal_id + valid_order.cases[0].samples[0].subject_id = "subject" + sample.subject_id = "subject" + + # GIVEN that the order's sample has a matching sex to the one in StatusDB + valid_order.cases[0].samples[0].sex = SexEnum.female + sample.sex = SexEnum.female + + # WHEN validating the order + errors: list[SexSubjectIdError] = validate_subject_sex_consistency( + order=valid_order, + store=sample_store, + ) + + # THEN no error should be returned + assert not errors + + +def test_validate_sex_subject_id_existing_sex_unknown( + valid_order: OrderWithCases, sample_store: Store +): + # GIVEN an existing sample + sample = sample_store.session.query(Sample).first() + + # GIVEN an order and sample with the same customer and subject id + valid_order.customer = sample.customer.internal_id + valid_order.cases[0].samples[0].subject_id = "subject" + sample.subject_id = "subject" + + # GIVEN a sample in the order that has a known sex and the existing sample's sex is unknown + valid_order.cases[0].samples[0].sex = SexEnum.female + sample.sex = SexEnum.unknown + + # WHEN validating the order + errors: list[SexSubjectIdError] = validate_subject_sex_consistency( + order=valid_order, + store=sample_store, + ) + + # THEN no error should be returned + assert not errors + + +def test_validate_sex_subject_id_new_sex_unknown(valid_order: OrderWithCases, sample_store: Store): + # GIVEN an existing sample + sample = sample_store.session.query(Sample).first() + + # GIVEN an order and sample with the same customer and subject id + valid_order.customer = sample.customer.internal_id + valid_order.cases[0].samples[0].subject_id = "subject" + sample.subject_id = "subject" + + # GIVEN a sample in the order that has an unknown sex and the existing sample's sex is known + valid_order.cases[0].samples[0].sex = SexEnum.unknown + sample.sex = SexEnum.female + + # WHEN validating the order + errors: list[SexSubjectIdError] = validate_subject_sex_consistency( + order=valid_order, + store=sample_store, + ) + + # THEN no error should be returned + assert not errors + + def test_validate_sample_names_different_from_case_names( order_with_samples_having_same_names_as_cases: OrderWithCases, ): diff --git a/tests/services/orders/test_validate_order_service/test_validate_generic_order.py b/tests/services/orders/test_validate_order_service/test_validate_generic_order.py deleted file mode 100644 index f74f5842d0..0000000000 --- a/tests/services/orders/test_validate_order_service/test_validate_generic_order.py +++ /dev/null @@ -1,43 +0,0 @@ -import pytest - -from cg.exc import OrderError -from cg.models.orders.constants import OrderType -from cg.models.orders.order import OrderIn -from cg.services.orders.validate_order_services.validate_case_order import ( - ValidateCaseOrderService, -) -from cg.store.store import Store - - -def test__validate_one_sample_per_case_multiple_samples( - base_store: Store, - rnafusion_order_to_submit: dict, -): - """Tests the validation of an RNAFUSION order where two samples have the same family_name.""" - ### GIVEN an RNAFUSION order where the first and last sample share the same case - order_data = OrderIn.parse_obj(obj=rnafusion_order_to_submit, project=OrderType.RNAFUSION) - order_data.samples[-1].family_name = order_data.samples[0].family_name - validator = ValidateCaseOrderService(base_store) - - ### WHEN validating that each case has only one sample - ### THEN an OrderError should be raised - - with pytest.raises(OrderError): - validator._validate_only_one_sample_per_case(order_data.samples) - - -def test__validate_one_sample_per_case_unique_samples( - base_store: Store, - rnafusion_order_to_submit: dict, -): - """Tests the validation of an RNAFUSION order where all samples have unique family_name.""" - ### GIVEN an RNAFUSION order with unique family names - order_data: OrderIn = OrderIn.parse_obj( - obj=rnafusion_order_to_submit, project=OrderType.RNAFUSION - ) - validator = ValidateCaseOrderService(base_store) - - ### WHEN validating that each case has only one sample - validator._validate_only_one_sample_per_case(order_data.samples) - - ### THEN no errors should be raised