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

(Improve order flow) Patch repeated case sample name rule #3941

Merged
merged 4 commits into from
Nov 15, 2024
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
4 changes: 2 additions & 2 deletions cg/services/order_validation_service/models/case.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@ def enumerated_new_samples(self):
return samples

@property
def enumerated_existing_samples(self) -> list[tuple[int, Sample]]:
samples: list[tuple[int, Sample]] = []
def enumerated_existing_samples(self) -> list[tuple[int, ExistingSample]]:
samples: list[tuple[int, ExistingSample]] = []
for sample_index, sample in self.enumerated_samples:
if not sample.is_new:
samples.append((sample_index, sample))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from cg.services.order_validation_service.models.discriminators import has_internal_id
from cg.services.order_validation_service.models.existing_case import ExistingCase
from cg.services.order_validation_service.models.order import Order
from cg.store.models import Sample

NewCaseType = Annotated[Case, Tag("new")]
ExistingCaseType = Annotated[ExistingCase, Tag("existing")]
Expand All @@ -14,7 +15,7 @@ class OrderWithCases(Order):
cases: list[Annotated[NewCaseType | ExistingCaseType, Discriminator(has_internal_id)]]

@property
def enumerated_cases(self) -> enumerate[Case]:
def enumerated_cases(self) -> enumerate[Case | ExistingCase]:
return enumerate(self.cases)

@property
Expand All @@ -32,3 +33,11 @@ def enumerated_existing_cases(self) -> list[tuple[int, Case]]:
if not case.is_new:
cases.append((case_index, case))
return cases

@property
def enumerated_new_samples(self) -> list[tuple[int, int, Sample]]:
return [
(case_index, sample_index, sample)
for case_index, case in self.enumerated_new_cases
for sample_index, sample in case.enumerated_new_samples
]
19 changes: 10 additions & 9 deletions cg/services/order_validation_service/rules/case_sample/rules.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,13 @@
)
from cg.services.order_validation_service.rules.case_sample.utils import (
get_counter_container_names,
get_existing_sample_names,
get_father_case_errors,
get_father_sex_errors,
get_invalid_panels,
get_mother_case_errors,
get_mother_sex_errors,
get_occupied_well_errors,
get_repeated_sample_name_errors,
get_well_sample_map,
is_concentration_missing,
is_container_name_missing,
Expand Down Expand Up @@ -249,15 +249,16 @@ def validate_wells_contain_at_most_one_sample(


def validate_sample_names_not_repeated(
order: OrderWithCases, **kwargs
order: OrderWithCases, store: Store, **kwargs
) -> list[SampleNameRepeatedError]:
errors: list[SampleNameRepeatedError] = []
for index, case in order.enumerated_new_cases:
case_errors: list[SampleNameRepeatedError] = get_repeated_sample_name_errors(
case=case, case_index=index
)
errors.extend(case_errors)
return errors
old_sample_names: set[str] = get_existing_sample_names(order=order, status_db=store)
new_samples: list[tuple[int, int, Sample]] = order.enumerated_new_samples
sample_name_counter = Counter([sample.name for _, _, sample in new_samples])
return [
SampleNameRepeatedError(case_index=case_index, sample_index=sample_index)
for case_index, sample_index, sample in new_samples
if sample_name_counter.get(sample.name) > 1 or sample.name in old_sample_names
]


def validate_fathers_are_male(order: OrderWithCases, **kwargs) -> list[InvalidFatherSexError]:
Expand Down
35 changes: 14 additions & 21 deletions cg/services/order_validation_service/rules/case_sample/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,12 @@
InvalidMotherSexError,
MotherNotInCaseError,
OccupiedWellError,
SampleNameRepeatedError,
SubjectIdSameAsCaseNameError,
)
from cg.services.order_validation_service.models.aliases import (
CaseContainingRelatives,
SampleWithRelatives,
)
from cg.services.order_validation_service.models.case import Case
from cg.services.order_validation_service.models.order_with_cases import OrderWithCases
from cg.services.order_validation_service.models.sample import Sample
from cg.services.order_validation_service.rules.utils import (
Expand Down Expand Up @@ -102,25 +100,6 @@ def get_repeated_case_name_errors(order: OrderWithCases) -> list[RepeatedCaseNam
return [RepeatedCaseNameError(case_index=case_index) for case_index in case_indices]


def get_indices_for_repeated_sample_names(case: Case) -> list[int]:
counter = Counter([sample.name for sample in case.samples])
indices: list[int] = []

for index, sample in case.enumerated_new_samples:
if counter.get(sample.name) > 1:
indices.append(index)

return indices


def get_repeated_sample_name_errors(case: Case, case_index: int) -> list[SampleNameRepeatedError]:
sample_indices: list[int] = get_indices_for_repeated_sample_names(case)
return [
SampleNameRepeatedError(sample_index=sample_index, case_index=case_index)
for sample_index in sample_indices
]


def get_father_sex_errors(
case: CaseContainingRelatives, case_index: int
) -> list[InvalidFatherSexError]:
Expand Down Expand Up @@ -307,3 +286,17 @@ def get_counter_container_names(order: OrderWithCases) -> Counter:
for sample_index, sample in case.enumerated_new_samples
)
return counter


def get_existing_sample_names(order: OrderWithCases, status_db: Store) -> set[str]:
existing_sample_names: set[str] = set()
for case in order.cases:
if case.is_new:
for sample_index, sample in case.enumerated_existing_samples:
db_sample = status_db.get_sample_by_internal_id(sample.internal_id)
existing_sample_names.add(db_sample.name)
else:
db_case = status_db.get_case_by_internal_id(case.internal_id)
for sample in db_case.samples:
existing_sample_names.add(sample.name)
return existing_sample_names
Original file line number Diff line number Diff line change
Expand Up @@ -269,12 +269,14 @@ def test_multiple_samples_in_well_not_allowed(order_with_samples_in_same_well: O
assert isinstance(errors[0], OccupiedWellError)


def test_repeated_sample_names_not_allowed(order_with_repeated_sample_names: OrderWithCases):
def test_repeated_sample_names_not_allowed(
order_with_repeated_sample_names: OrderWithCases, base_store: Store
):
# GIVEN an order with samples in a case with the same name

# WHEN validating the order
errors: list[SampleNameRepeatedError] = validate_sample_names_not_repeated(
order_with_repeated_sample_names
order=order_with_repeated_sample_names, store=base_store
)

# THEN errors are returned
Expand Down
Loading