diff --git a/cg/services/order_validation_service/models/errors.py b/cg/services/order_validation_service/models/errors.py index 2bbec19056..e6297f9fa3 100644 --- a/cg/services/order_validation_service/models/errors.py +++ b/cg/services/order_validation_service/models/errors.py @@ -53,3 +53,8 @@ class OrderNameRequiredError(OrderError): class OccupiedWellError(CaseSampleError): field: str = "well_position" message: str = "Well is already occupied" + + +class ReusedSampleNameError(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 8142989ed6..1f136f9414 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,9 +1,13 @@ -from cg.services.order_validation_service.models.errors import OccupiedWellError +from cg.services.order_validation_service.models.errors import ( + OccupiedWellError, + ReusedSampleNameError, +) from cg.services.order_validation_service.workflows.tomte.models.order import TomteOrder from cg.services.order_validation_service.workflows.tomte.validation.inter_field.utils import ( _get_errors, _get_excess_samples, _get_plate_samples, + get_duplicate_sample_name_errors, ) @@ -11,3 +15,11 @@ def validate_wells_contain_at_most_one_sample(order: TomteOrder) -> list[Occupie samples_with_cases = _get_plate_samples(order) samples = _get_excess_samples(samples_with_cases) return _get_errors(samples) + + +def validate_unique_sample_names_in_cases(order: TomteOrder) -> list[ReusedSampleNameError]: + errors: list[ReusedSampleNameError] = [] + for case in order.cases: + case_errors = get_duplicate_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 c056dc4ba3..501a20ebd3 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 @@ -1,5 +1,9 @@ +from collections import Counter from cg.models.orders.sample_base import ContainerEnum -from cg.services.order_validation_service.models.errors import OccupiedWellError +from cg.services.order_validation_service.models.errors import ( + OccupiedWellError, + ReusedSampleNameError, +) from cg.services.order_validation_service.workflows.tomte.models.case import TomteCase from cg.services.order_validation_service.workflows.tomte.models.order import TomteOrder from cg.services.order_validation_service.workflows.tomte.models.sample import ( @@ -47,3 +51,14 @@ def _get_sample_well_map(plate_samples_with_cases: list[tuple[TomteSample, Tomte sample_well_map[sample.well_position] = [] sample_well_map[sample.well_position].append((sample, case)) return sample_well_map + + +def get_duplicate_sample_names(case: TomteCase) -> list[str]: + sample_names = [sample.name for sample in case.samples] + count = Counter(sample_names) + return list({name for name, freq in count.items() if freq > 1}) + + +def get_duplicate_sample_name_errors(case: TomteCase) -> list[ReusedSampleNameError]: + sample_names = get_duplicate_sample_names(case) + return [ReusedSampleNameError(sample_name=name, case_name=case.name) for name in sample_names]