From 184783dcbf586cb0e77fb00740dd84a0479f35a7 Mon Sep 17 00:00:00 2001 From: Sebastian Allard Date: Mon, 29 Jul 2024 21:21:12 +0200 Subject: [PATCH] Validate unique case names (#3482) --- .../order_validation_service/models/errors.py | 5 +++++ .../tomte/validation/inter_field/rules.py | 12 ++++++++--- .../tomte/validation/inter_field/utils.py | 18 +++++++++++++--- .../workflows/tomte/validation_rules.py | 6 ++++-- .../order_validation_service/conftest.py | 19 +++++++++++++---- .../test_tomte_inter_field_validators.py | 21 ++++++++++++++++--- 6 files changed, 66 insertions(+), 15 deletions(-) diff --git a/cg/services/order_validation_service/models/errors.py b/cg/services/order_validation_service/models/errors.py index 2eb3f05505..2b7525b2d8 100644 --- a/cg/services/order_validation_service/models/errors.py +++ b/cg/services/order_validation_service/models/errors.py @@ -55,6 +55,11 @@ class OccupiedWellError(CaseSampleError): message: str = "Well is already occupied" +class RepeatedCaseNameError(CaseError): + field: str = "name" + message: str = "Case name already used" + + class RepeatedSampleNameError(CaseSampleError): field: str = "name" message: str = "Sample name already used" diff --git a/cg/services/order_validation_service/workflows/tomte/validation/inter_field/rules.py b/cg/services/order_validation_service/workflows/tomte/validation/inter_field/rules.py index 926ec28048..9d4c4ff396 100644 --- a/cg/services/order_validation_service/workflows/tomte/validation/inter_field/rules.py +++ b/cg/services/order_validation_service/workflows/tomte/validation/inter_field/rules.py @@ -1,5 +1,6 @@ from cg.services.order_validation_service.models.errors import ( OccupiedWellError, + RepeatedCaseNameError, RepeatedSampleNameError, ) from cg.services.order_validation_service.workflows.tomte.models.order import TomteOrder @@ -7,7 +8,8 @@ _get_errors, _get_excess_samples, _get_plate_samples, - get_duplicate_sample_name_errors, + get_repeated_case_name_errors, + get_repeated_sample_name_errors, ) @@ -17,9 +19,13 @@ def validate_wells_contain_at_most_one_sample(order: TomteOrder) -> list[Occupie return _get_errors(samples) -def validate_unique_sample_names_in_cases(order: TomteOrder) -> list[RepeatedSampleNameError]: +def validate_no_repeated_case_names(order: TomteOrder) -> list[RepeatedCaseNameError]: + return get_repeated_case_name_errors(order) + + +def validate_no_repeated_sample_names(order: TomteOrder) -> list[RepeatedSampleNameError]: errors: list[RepeatedSampleNameError] = [] for case in order.cases: - case_errors = get_duplicate_sample_name_errors(case) + case_errors = get_repeated_sample_name_errors(case) errors.extend(case_errors) return errors diff --git a/cg/services/order_validation_service/workflows/tomte/validation/inter_field/utils.py b/cg/services/order_validation_service/workflows/tomte/validation/inter_field/utils.py index 7aa76251e9..68bc10d42f 100644 --- a/cg/services/order_validation_service/workflows/tomte/validation/inter_field/utils.py +++ b/cg/services/order_validation_service/workflows/tomte/validation/inter_field/utils.py @@ -2,6 +2,7 @@ from cg.models.orders.sample_base import ContainerEnum from cg.services.order_validation_service.models.errors import ( OccupiedWellError, + RepeatedCaseNameError, RepeatedSampleNameError, ) from cg.services.order_validation_service.workflows.tomte.models.case import TomteCase @@ -53,12 +54,23 @@ def _get_sample_well_map(plate_samples_with_cases: list[tuple[TomteSample, Tomte return sample_well_map -def get_duplicate_sample_names(case: TomteCase) -> list[str]: +def get_repeated_case_names(order: TomteOrder) -> list[str]: + case_names = [case.name for case in order.cases] + count = Counter(case_names) + return [name for name, freq in count.items() if freq > 1] + + +def get_repeated_case_name_errors(order: TomteOrder) -> list[RepeatedCaseNameError]: + case_names = get_repeated_case_names(order) + return [RepeatedCaseNameError(case_name=name) for name in case_names] + + +def get_repeated_sample_names(case: TomteCase) -> list[str]: sample_names = [sample.name for sample in case.samples] count = Counter(sample_names) return [name for name, freq in count.items() if freq > 1] -def get_duplicate_sample_name_errors(case: TomteCase) -> list[RepeatedSampleNameError]: - sample_names = get_duplicate_sample_names(case) +def get_repeated_sample_name_errors(case: TomteCase) -> list[RepeatedSampleNameError]: + sample_names = get_repeated_sample_names(case) return [RepeatedSampleNameError(sample_name=name, case_name=case.name) for name in sample_names] diff --git a/cg/services/order_validation_service/workflows/tomte/validation_rules.py b/cg/services/order_validation_service/workflows/tomte/validation_rules.py index e87e45fb49..da94526f2d 100644 --- a/cg/services/order_validation_service/workflows/tomte/validation_rules.py +++ b/cg/services/order_validation_service/workflows/tomte/validation_rules.py @@ -7,7 +7,8 @@ validate_ticket_number_required_if_connected, ) from cg.services.order_validation_service.workflows.tomte.validation.inter_field.rules import ( - validate_unique_sample_names_in_cases, + validate_no_repeated_case_names, + validate_no_repeated_sample_names, validate_wells_contain_at_most_one_sample, ) @@ -19,6 +20,7 @@ ] TOMTE_CASE_SAMPLE_RULES = [ - validate_unique_sample_names_in_cases, + validate_no_repeated_case_names, + validate_no_repeated_sample_names, validate_wells_contain_at_most_one_sample, ] diff --git a/tests/services/order_validation_service/conftest.py b/tests/services/order_validation_service/conftest.py index 0d417740a0..801f9110ae 100644 --- a/tests/services/order_validation_service/conftest.py +++ b/tests/services/order_validation_service/conftest.py @@ -74,7 +74,7 @@ def order_with_samples_in_same_well(case_with_samples_in_same_well: TomteCase) - @pytest.fixture -def case_with_samples_with_duplicate_names() -> TomteCase: +def case_with_samples_with_repeated_names() -> TomteCase: sample_1: TomteSample = create_sample(1) sample_2: TomteSample = create_sample(1) sample_1.name = sample_2.name @@ -82,7 +82,18 @@ def case_with_samples_with_duplicate_names() -> TomteCase: @pytest.fixture -def order_with_duplicate_sample_names( - case_with_samples_with_duplicate_names: TomteCase, +def order_with_repeated_sample_names( + case_with_samples_with_repeated_names: TomteCase, ) -> TomteOrder: - return create_order([case_with_samples_with_duplicate_names]) + return create_order([case_with_samples_with_repeated_names]) + + +@pytest.fixture +def case() -> TomteCase: + sample: TomteSample = create_sample(1) + return create_case([sample]) + + +@pytest.fixture +def order_with_repeated_case_names(case: TomteCase) -> TomteOrder: + return create_order([case, case]) diff --git a/tests/services/order_validation_service/test_tomte_inter_field_validators.py b/tests/services/order_validation_service/test_tomte_inter_field_validators.py index 9fef1a06b1..7bd3462c97 100644 --- a/tests/services/order_validation_service/test_tomte_inter_field_validators.py +++ b/tests/services/order_validation_service/test_tomte_inter_field_validators.py @@ -1,10 +1,12 @@ from cg.services.order_validation_service.models.errors import ( OccupiedWellError, + RepeatedCaseNameError, RepeatedSampleNameError, ) from cg.services.order_validation_service.workflows.tomte.models.order import TomteOrder from cg.services.order_validation_service.workflows.tomte.validation.inter_field.rules import ( - validate_unique_sample_names_in_cases, + validate_no_repeated_case_names, + validate_no_repeated_sample_names, validate_wells_contain_at_most_one_sample, ) @@ -34,14 +36,27 @@ def test_order_without_multiple_samples_in_well(valid_order: TomteOrder): assert not errors -def test_duplicate_sample_names_not_allowed(order_with_duplicate_sample_names: TomteOrder): +def test_repeated_sample_names_not_allowed(order_with_repeated_sample_names: TomteOrder): # Given an order with samples in a case with the same name # WHEN validating the order - errors = validate_unique_sample_names_in_cases(order_with_duplicate_sample_names) + errors = validate_no_repeated_sample_names(order_with_repeated_sample_names) # THEN errors are returned assert errors # THEN the errors are about the sample names assert isinstance(errors[0], RepeatedSampleNameError) + + +def test_repeated_case_names_not_allowed(order_with_repeated_case_names: TomteOrder): + # GIVEN an order with cases with the same name + + # WHEN validating the order + errors = validate_no_repeated_case_names(order_with_repeated_case_names) + + # THEN errors are returned + assert errors + + # THEN the errors are about the case names + assert isinstance(errors[0], RepeatedCaseNameError)