Skip to content

Commit

Permalink
Refactor and add customer filtering (#3739) (patch)
Browse files Browse the repository at this point in the history
### Fixed

- Naming of sample and pool filtering based on customers
  • Loading branch information
islean authored Sep 19, 2024
1 parent 188e283 commit f41fafd
Show file tree
Hide file tree
Showing 9 changed files with 72 additions and 36 deletions.
2 changes: 1 addition & 1 deletion cg/meta/upload/scout/uploadscoutapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,7 @@ def create_rna_dna_collection(self, rna_sample: Sample) -> RNADNACollection:

collaborators: set[Customer] = rna_sample.customer.collaborators
subject_id_samples: list[Sample] = (
self.status_db.get_samples_by_customer_id_list_and_subject_id_and_is_tumour(
self.status_db.get_samples_by_customer_ids_and_subject_id_and_is_tumour(
customer_ids=[customer.id for customer in collaborators],
subject_id=rna_sample.subject_id,
is_tumour=rna_sample.is_tumour,
Expand Down
11 changes: 6 additions & 5 deletions cg/server/endpoints/samples.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
from http import HTTPStatus

from flask import Blueprint, abort, g, jsonify, request

from cg.server.dto.samples.collaborator_samples_request import CollaboratorSamplesRequest
from cg.server.dto.samples.collaborator_samples_request import CollaboratorSamplesRequest
from cg.server.dto.samples.collaborator_samples_request import (
CollaboratorSamplesRequest,
)
from cg.server.dto.samples.samples_response import SamplesResponse
from cg.server.endpoints.utils import before_request
from cg.server.ext import sample_service
from cg.server.ext import db, sample_service
from cg.store.models import Customer, Sample
from cg.server.ext import db

SAMPLES_BLUEPRINT = Blueprint("samples", __name__, url_prefix="/api/v1")
SAMPLES_BLUEPRINT.before_request(before_request)
Expand Down Expand Up @@ -57,7 +58,7 @@ def get_samples():
customers: list[Customer] | None = (
None if g.current_user.is_admin else g.current_user.customers
)
samples: list[Sample] = db.get_samples_by_customer_id_and_pattern(
samples: list[Sample] = db.get_samples_by_customers_and_pattern(
pattern=request.args.get("enquiry"), customers=customers
)
limit = int(request.args.get("limit", 50))
Expand Down
59 changes: 38 additions & 21 deletions cg/store/crud/read.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,19 @@
from cg.constants.constants import CaseActions, CustomerId, PrepCategory, SampleType
from cg.exc import CaseNotFoundError, CgError, OrderNotFoundError, SampleNotFoundError
from cg.server.dto.orders.orders_request import OrdersRequest
from cg.server.dto.samples.collaborator_samples_request import CollaboratorSamplesRequest
from cg.server.dto.samples.collaborator_samples_request import (
CollaboratorSamplesRequest,
)
from cg.store.base import BaseHandler
from cg.store.exc import EntryNotFoundError
from cg.store.filters.status_analysis_filters import AnalysisFilter, apply_analysis_filter
from cg.store.filters.status_application_filters import ApplicationFilter, apply_application_filter
from cg.store.filters.status_analysis_filters import (
AnalysisFilter,
apply_analysis_filter,
)
from cg.store.filters.status_application_filters import (
ApplicationFilter,
apply_application_filter,
)
from cg.store.filters.status_application_limitations_filters import (
ApplicationLimitationsFilter,
apply_application_limitations_filter,
Expand All @@ -25,14 +33,23 @@
apply_application_versions_filter,
)
from cg.store.filters.status_bed_filters import BedFilter, apply_bed_filter
from cg.store.filters.status_bed_version_filters import BedVersionFilter, apply_bed_version_filter
from cg.store.filters.status_bed_version_filters import (
BedVersionFilter,
apply_bed_version_filter,
)
from cg.store.filters.status_case_filters import CaseFilter, apply_case_filter
from cg.store.filters.status_case_sample_filters import CaseSampleFilter, apply_case_sample_filter
from cg.store.filters.status_case_sample_filters import (
CaseSampleFilter,
apply_case_sample_filter,
)
from cg.store.filters.status_collaboration_filters import (
CollaborationFilter,
apply_collaboration_filter,
)
from cg.store.filters.status_customer_filters import CustomerFilter, apply_customer_filter
from cg.store.filters.status_customer_filters import (
CustomerFilter,
apply_customer_filter,
)
from cg.store.filters.status_illumina_flow_cell_filters import (
IlluminaFlowCellFilter,
apply_illumina_flow_cell_filters,
Expand All @@ -47,10 +64,13 @@
)
from cg.store.filters.status_invoice_filters import InvoiceFilter, apply_invoice_filter
from cg.store.filters.status_order_filters import OrderFilter, apply_order_filters
from cg.store.filters.status_organism_filters import OrganismFilter, apply_organism_filter
from cg.store.filters.status_organism_filters import (
OrganismFilter,
apply_organism_filter,
)
from cg.store.filters.status_pacbio_smrt_cell_filters import (
apply_pac_bio_smrt_cell_filters,
PacBioSMRTCellFilter,
apply_pac_bio_smrt_cell_filters,
)
from cg.store.filters.status_panel_filters import PanelFilter, apply_panel_filter
from cg.store.filters.status_pool_filters import PoolFilter, apply_pool_filter
Expand All @@ -73,12 +93,12 @@
Invoice,
Order,
Organism,
PacBioSMRTCell,
Panel,
Pool,
Sample,
SampleRunMetrics,
User,
PacBioSMRTCell,
)

LOG = logging.getLogger(__name__)
Expand Down Expand Up @@ -540,13 +560,12 @@ def new_invoice_id(self) -> int:
ids = [inv.id for inv in query]
return max(ids) + 1 if ids else 0

def get_pools_by_customer_id(self, *, customers: list[Customer] | None = None) -> list[Pool]:
"""Return all the pools for a customer."""
customer_ids = [customer.id for customer in customers]
def get_pools_by_customers(self, *, customers: list[Customer] | None = None) -> list[Pool]:
"""Return all the pools for a list of customers."""
return apply_pool_filter(
pools=self._get_query(table=Pool),
customer_ids=customer_ids,
filter_functions=[PoolFilter.BY_CUSTOMER_ID],
customers=customers,
filter_functions=[PoolFilter.BY_CUSTOMERS],
).all()

def get_pools_by_name_enquiry(self, *, name_enquiry: str = None) -> list[Pool]:
Expand Down Expand Up @@ -580,7 +599,7 @@ def get_pools_to_render(
self, customers: list[Customer] | None = None, enquiry: str = None
) -> list[Pool]:
pools: list[Pool] = (
self.get_pools_by_customer_id(customers=customers) if customers else self.get_pools()
self.get_pools_by_customers(customers=customers) if customers else self.get_pools()
)
if enquiry:
pools: list[Pool] = list(
Expand All @@ -603,24 +622,22 @@ def get_ready_made_library_expected_reads(self, case_id: str) -> int:
)
return application.expected_reads

def get_samples_by_customer_id_and_pattern(
def get_samples_by_customers_and_pattern(
self, *, customers: list[Customer] | None = None, pattern: str = None
) -> list[Sample]:
"""Get samples by customer and sample internal id or sample name pattern."""
samples: Query = self._get_query(table=Sample)
customer_entry_ids: list[int] = []
filter_functions: list[SampleFilter] = []
if customers:
if not isinstance(customers, list):
customers = list(customers)
customer_entry_ids = [customer.id for customer in customers]
filter_functions.append(SampleFilter.BY_CUSTOMER_ENTRY_IDS)
filter_functions.append(SampleFilter.BY_CUSTOMERS)
if pattern:
filter_functions.extend([SampleFilter.BY_INTERNAL_ID_OR_NAME_SEARCH])
filter_functions.append(SampleFilter.ORDER_BY_CREATED_AT_DESC)
return apply_sample_filter(
samples=samples,
customer_entry_ids=customer_entry_ids,
customers=customers,
search_pattern=pattern,
filter_functions=filter_functions,
).all()
Expand Down Expand Up @@ -667,7 +684,7 @@ def get_samples_by_customer_and_subject_id(
customer_internal_id=customer_internal_id, subject_id=subject_id
).all()

def get_samples_by_customer_id_list_and_subject_id_and_is_tumour(
def get_samples_by_customer_ids_and_subject_id_and_is_tumour(
self, customer_ids: list[int], subject_id: str, is_tumour: bool
) -> list[Sample]:
"""Return a list of samples matching a list of customers with given subject id and is a tumour or not."""
Expand Down
13 changes: 11 additions & 2 deletions cg/store/filters/status_pool_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from cg.store.models import Customer, Pool


def filter_pools_by_customer_id(pools: Query, customer_ids: list[int], **kwargs) -> Query:
def filter_pools_by_customer_ids(pools: Query, customer_ids: list[int], **kwargs) -> Query:
"""Return pools by customer id."""
return pools.filter(Pool.customer_id.in_(customer_ids))

Expand Down Expand Up @@ -71,13 +71,20 @@ def filter_pools_by_customer(pools: Query, customer: Customer, **kwargs) -> Quer
return pools.filter(Pool.customer == customer)


def filter_pools_by_customers(pools: Query, customers: list[Customer], **kwargs) -> Query:
"""Return pools by customers."""
customer_ids = [customer.id for customer in customers]
return pools.filter(Pool.customer_id.in_(customer_ids))


def apply_pool_filter(
filter_functions: list[Callable],
pools: Query,
invoice_id: int | None = None,
entry_id: int | None = None,
name: str | None = None,
customer_ids: list[int] | None = None,
customers: list[Customer] | None = None,
name_enquiry: str | None = None,
order_enquiry: str | None = None,
customer: Customer | None = None,
Expand All @@ -94,6 +101,7 @@ def apply_pool_filter(
name_enquiry=name_enquiry,
order_enquiry=order_enquiry,
customer=customer,
customers=customers,
)
return pools

Expand All @@ -110,7 +118,8 @@ class PoolFilter(Enum):
BY_INVOICE_ID: Callable = filter_pools_by_invoice_id
WITHOUT_INVOICE_ID: Callable = filter_pools_without_invoice_id
DO_INVOICE: Callable = filter_pools_do_invoice
BY_CUSTOMER_ID: Callable = filter_pools_by_customer_id
BY_CUSTOMER_IDS: Callable = filter_pools_by_customer_ids
BY_NAME_ENQUIRY: Callable = filter_pools_by_name_enquiry
BY_ORDER_ENQUIRY: Callable = filter_pools_by_order_enquiry
BY_CUSTOMER: Callable = filter_pools_by_customer
BY_CUSTOMERS: Callable = filter_pools_by_customers
9 changes: 9 additions & 0 deletions cg/store/filters/status_sample_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,12 @@ def filter_samples_by_customer(samples: Query, customer: Customer, **kwargs) ->
return samples.filter(Sample.customer == customer)


def filter_samples_by_customers(samples: Query, customers: list[Customer], **kwargs) -> Query:
"""Return samples by customers."""
customer_ids = [customer.id for customer in customers]
return samples.filter(Sample.customer_id.in_(customer_ids))


def order_samples_by_created_at_desc(samples: Query, **kwargs) -> Query:
"""Return samples ordered by created_at descending."""
return samples.order_by(Sample.created_at.desc())
Expand Down Expand Up @@ -179,6 +185,7 @@ def apply_sample_filter(
subject_id: str | None = None,
name: str | None = None,
customer: Customer | None = None,
customers: list[Customer] | None = None,
name_pattern: str | None = None,
internal_id_pattern: str | None = None,
search_pattern: str | None = None,
Expand All @@ -200,6 +207,7 @@ def apply_sample_filter(
subject_id=subject_id,
name=name,
customer=customer,
customers=customers,
name_pattern=name_pattern,
internal_id_pattern=internal_id_pattern,
search_pattern=search_pattern,
Expand All @@ -214,6 +222,7 @@ class SampleFilter(Enum):
"""Define Sample filter functions."""

BY_CUSTOMER: Callable = filter_samples_by_customer
BY_CUSTOMERS: Callable = filter_samples_by_customers
BY_CUSTOMER_ENTRY_IDS: Callable = filter_samples_by_entry_customer_ids
BY_ENTRY_ID: Callable = filter_samples_by_entry_id
BY_IDENTIFIER_NAME_AND_VALUE: Callable = filter_samples_by_identifier_name_and_value
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def test_validate_pacbio_order_reused_sample_name(
# GIVEN a PacBio order with a reused sample name
status_db: Store = validate_pacbio_order_service.status_db
customer = status_db.get_customer_by_internal_id(pacbio_order.customer)
old_sample_name: str = status_db.get_samples_by_customer_id_and_pattern(customers=[customer])[
old_sample_name: str = status_db.get_samples_by_customers_and_pattern(customers=[customer])[
0
].name
pacbio_order.samples[0].name = old_sample_name
Expand Down
2 changes: 1 addition & 1 deletion tests/store/crud/read/test_read.py
Original file line number Diff line number Diff line change
Expand Up @@ -950,7 +950,7 @@ def test_get_pools_by_customer_id(store_with_multiple_pools_for_customer: Store)
# GIVEN a database with two pools

# WHEN getting pools by customer id
pools: list[Pool] = store_with_multiple_pools_for_customer.get_pools_by_customer_id(
pools: list[Pool] = store_with_multiple_pools_for_customer.get_pools_by_customers(
customers=store_with_multiple_pools_for_customer.get_customers()
)

Expand Down
6 changes: 3 additions & 3 deletions tests/store/crud/read/test_read_sample.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ def test_get_samples_by_customer_id_list_and_subject_id_and_is_tumour(
)

# WHEN fetching the samples by customer ID list, subject ID, and tumour status
samples = store_with_samples_customer_id_and_subject_id_and_tumour_status.get_samples_by_customer_id_list_and_subject_id_and_is_tumour(
samples = store_with_samples_customer_id_and_subject_id_and_tumour_status.get_samples_by_customer_ids_and_subject_id_and_is_tumour(
customer_ids=customer_ids, subject_id=subject_id, is_tumour=is_tumour
)

Expand Down Expand Up @@ -170,7 +170,7 @@ def test_get_samples_by_customer_id_list_and_subject_id_and_is_tumour_with_non_e

# WHEN fetching the samples by customer ID list, subject ID, and tumour status
non_existing_customer_id = [3]
samples = store_with_samples_customer_id_and_subject_id_and_tumour_status.get_samples_by_customer_id_list_and_subject_id_and_is_tumour(
samples = store_with_samples_customer_id_and_subject_id_and_tumour_status.get_samples_by_customer_ids_and_subject_id_and_is_tumour(
customer_ids=non_existing_customer_id, subject_id="test_subject", is_tumour=True
)

Expand Down Expand Up @@ -590,7 +590,7 @@ def test_get_samples_by_customer_id_and_pattern_with_collaboration(

# WHEN getting the samples for a customer
samples: list[Sample] = (
store_with_samples_for_multiple_customers.get_samples_by_customer_id_and_pattern(
store_with_samples_for_multiple_customers.get_samples_by_customers_and_pattern(
customers=customer,
pattern="sample",
)
Expand Down
4 changes: 2 additions & 2 deletions tests/store/filters/test_status_pool_filters.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from sqlalchemy.orm import Query

from cg.store.filters.status_pool_filters import (
filter_pools_by_customer_id,
filter_pools_by_customer_ids,
filter_pools_by_invoice_id,
filter_pools_by_name_enquiry,
filter_pools_by_order_enquiry,
Expand Down Expand Up @@ -263,7 +263,7 @@ def test_filter_pools_by_customer_id(
# GIVEN a store with two pools of with the same customer

# WHEN getting pools with customer id
pools: Query = filter_pools_by_customer_id(
pools: Query = filter_pools_by_customer_ids(
pools=store_with_a_pool_with_and_without_attributes._get_query(table=Pool),
customer_ids=[1],
)
Expand Down

0 comments on commit f41fafd

Please sign in to comment.