Repository: Clinical-Genomics/cg
Issue category: Feature Request

Issue:
One Case Per Sample for FASTQ 
<!--
Filled in by the Product Owner or the Stakeholders.
-->

As a user assigned to the pipeline FASTQ,
I want one case per sample,
So that the order-view in Trailblazer would tell me if a sample failed on amount of reads.

# Acceptance Criteria
<!--
Filled in by the Product Owner or the Stakeholders.
-->


- [ ] For each sample, generate one case in any ticket that includes an app-tag related to FASTQ delivery.

...

# Notes
<!--
Filled in by the Product Owner or the Stakeholders.
-->

- Additional information.
- Dependencies.
- Related user stories.

# Implementation plan
<!--
This section will be populated by the developers during their technical refinements.
-->

- [ ] Work item 1
- [ ] Work item 2



Hints:
@karlnyr, This is a start at least  
As a user of the trailblazer order view I would like to have FASTQ order stratified on a sample level and an indication whether samples did not reach the required amount of reads. @RasmusBurge-CG Is this right?

Added to refinement 19-06-2024
> As a user of the trailblazer order view I would like to have FASTQ order stratified on a sample level and an indication whether samples did not reach the required amount of reads. @RasmusBurge-CG Is this right?
> 
> Added to refinement 19-06-2024

I can just add that I would prefer having one case per sample than having the order summary logic depending on the workflow. Having one case per sample was also how I interpreted Rasmus' wishes

> > As a user of the trailblazer order view I would like to have FASTQ order stratified on a sample level and an indication whether samples did not reach the required amount of reads. @RasmusBurge-CG Is this right?
> > Added to refinement 19-06-2024
> 
> I can just add that I would prefer having one case per sample than having the order summary logic depending on the workflow. Having one case per sample was also how I interpreted Rasmus' wishes

Hi, yes @ChrOertlin, 'user of trailblazer' is a more accurate description. In my mind it does not need to be on a sample level, it could still be at case level(now that case and sample are somewhat equal, if we make the changes for Fastq). 

I guess one only needs multiple samples in a case when we are running trios. Do you agree, @islean? This might be a good discussion for a larger audience.

Note: the changes are only suggested for pipeline FASTQ. (but I'm tempted to say that this would be a good change for all pipelines. However,  I think that trios should be in the same case)



Ok, this is more of an trailblazer issue, I am transferring the issue there.
## Suggestion by devs:
When order is placed for FASTQ orders, create one case per sample

## Considerations @ChrOertlin will look into these
### get_cases_to_analyse should behave the same way. 

Needs testing - although no change expected as it works for other cases as well. Mostly depends on how the OrderSubmitter stores the case data

### get_analyses_to_upload should behave the same way.

Needs testing - although no change expected as it works for other cases as well. Mostly depends on how the OrderSubmitter stores the case data

### See if file-structure on caesar remains the same.

Since fastq is a sample level file it will be formatted as ticket/sample/file just as it is now. No change expected. Will be confirmed with a test.

### Finding "pair" samples when canceling them. Ask AZ if the change would cause issues for the lab

No issues, it would be an improvement.

According to AZ it can become a confusion that multiple fastq samples are stored under a single case if they have no relationship that just the order they are in. Especially, when just one sample needs to be cancelled the question could arise whether the whole order would need to be cancelled. If a sample would have it's own case this ambiguity would not exist. 


According to KN this would be very nice!
## Technical refinement

Fastq samples and cases are added to the database [here](https://github.com/Clinical-Genomics/cg/blob/75cc99e1a5ea3f59d047c2078b4eb5ffec1b74bd/cg/services/orders/store_order_services/store_fastq_order_service.py#L84). This function needs to be refactored so that each sample has one case.

Patch:
diff --git a/cg/services/orders/storing/implementations/fastq_order_service.py b/cg/services/orders/storing/implementations/fastq_order_service.py
index ea6df4656b..ba3b149237 100644
--- a/cg/services/orders/storing/implementations/fastq_order_service.py
+++ b/cg/services/orders/storing/implementations/fastq_order_service.py
@@ -45,16 +45,18 @@ def store_order_data_in_status_db(self, order: FastqOrder) -> list[Sample]:
         The stored data objects are:
         - Order
         - Samples
-        - One Case containing all samples
-        - For each Sample, a relationship between the sample and the Case
+        - One Case per sample
+        - For each Sample, a relationship between the sample and its Case
         - For each non-tumour WGS Sample, a MAF Case and a relationship between the Sample and the
         MAF Case
         """
         db_order: Order = self._create_db_order(order=order)
-        db_case: Case = self._create_db_case(order=order, db_order=db_order)
         new_samples = []
         with self.status_db.session.no_autoflush:
             for sample in order.samples:
+                db_case: Case = self._create_db_case_for_sample(
+                    sample=sample, customer=db_order.customer, order=order
+                )
                 db_sample: Sample = self._create_db_sample(
                     sample=sample,
                     order_name=order.name,
@@ -65,10 +67,10 @@ def store_order_data_in_status_db(self, order: FastqOrder) -> list[Sample]:
                 case_sample: CaseSample = self.status_db.relate_sample(
                     case=db_case, sample=db_sample, status=StatusEnum.unknown
                 )
-                self.status_db.add_multiple_items_to_store([db_sample, case_sample])
+                self.status_db.add_multiple_items_to_store([db_sample, case_sample, db_case])
                 new_samples.append(db_sample)
-        db_order.cases.append(db_case)
-        self.status_db.add_multiple_items_to_store([db_order, db_case])
+                db_order.cases.append(db_case)
+        self.status_db.add_item_to_store(db_order)
         self.status_db.commit_to_store()
         return new_samples
 
@@ -80,17 +82,24 @@ def _create_db_order(self, order: FastqOrder) -> Order:
         )
         return self.status_db.add_order(customer=customer, ticket_id=ticket_id)
 
-    def _create_db_case(self, order: FastqOrder, db_order: Order) -> Case:
+    def _create_db_case_for_sample(
+        self,
+        sample: FastqSample,
+        customer: Customer,
+        order: FastqOrder,
+    ) -> Case:
         """Return a Case database object."""
-        priority: str = order.samples[0].priority
+        ticket_id = str(order._generated_ticket_id)
+        case_name: str = f"{sample.name}-{ticket_id}"
+        priority: str = sample.priority
         case: Case = self.status_db.add_case(
             data_analysis=ORDER_TYPE_WORKFLOW_MAP[order.order_type],
             data_delivery=DataDelivery(order.delivery_type),
-            name=str(db_order.ticket_id),
+            name=case_name,
             priority=priority,
-            ticket=str(db_order.ticket_id),
+            ticket=ticket_id,
         )
-        case.customer = db_order.customer
+        case.customer = customer
         return case
 
     def _create_db_sample(
diff --git a/cg/services/orders/storing/implementations/microbial_fastq_order_service.py b/cg/services/orders/storing/implementations/microbial_fastq_order_service.py
index 69dd9cdfdd..38a1c41358 100644
--- a/cg/services/orders/storing/implementations/microbial_fastq_order_service.py
+++ b/cg/services/orders/storing/implementations/microbial_fastq_order_service.py
@@ -78,13 +78,14 @@ def _create_db_case_for_sample(
         self, sample: MicrobialFastqSample, customer: Customer, order: MicrobialFastqOrder
     ) -> Case:
         """Return a Case database object for a MicrobialFastqSample."""
-        case_name: str = f"{sample.name}-case"
+        ticket_id = str(order._generated_ticket_id)
+        case_name: str = f"{sample.name}-{ticket_id}"
         case: Case = self.status_db.add_case(
             data_analysis=ORDER_TYPE_WORKFLOW_MAP[order.order_type],
             data_delivery=DataDelivery(order.delivery_type),
             name=case_name,
             priority=sample.priority,
-            ticket=str(order._generated_ticket_id),
+            ticket=ticket_id,
         )
         case.customer = customer
         return case


Test patch:
diff --git a/tests/services/orders/store_service/test_fastq_order_service.py b/tests/services/orders/store_service/test_fastq_order_service.py
index d42b1cd186..5281d338ee 100644
--- a/tests/services/orders/store_service/test_fastq_order_service.py
+++ b/tests/services/orders/store_service/test_fastq_order_service.py
@@ -9,7 +9,7 @@
 from cg.services.orders.storing.constants import MAF_ORDER_ID
 from cg.services.orders.storing.implementations.fastq_order_service import StoreFastqOrderService
 from cg.services.orders.validation.workflows.fastq.models.order import FastqOrder
-from cg.store.models import Application, Case, Order, Sample
+from cg.store.models import Application, Case, CaseSample, Order, Sample
 from cg.store.store import Store
 from tests.store_helpers import StoreHelpers
 
@@ -38,12 +38,14 @@ def test_store_order_data_in_status_db(
     db_samples: list[Sample] = store_to_submit_and_validate_orders._get_query(table=Sample).all()
     assert set(new_samples) == set(db_samples)
 
-    # THEN it should create one case for the analysis and one MAF case
+    # THEN it should create one case per sample and one MAF case
     cases: list[Case] = store_to_submit_and_validate_orders._get_query(table=Case).all()
-    assert len(cases) == 2
-    assert len(db_samples[0].links) == 2
+    assert len(cases) == 3
+    links: list[CaseSample] = store_to_submit_and_validate_orders._get_query(table=CaseSample).all()
+    assert len(links) == 3
     assert cases[0].data_analysis == Workflow.MIP_DNA
     assert cases[1].data_analysis == Workflow.RAW_DATA
+    assert cases[2].data_analysis == Workflow.RAW_DATA
 
     # THEN the analysis case has allowed data deliveries
     assert cases[1].data_delivery in [DataDelivery.FASTQ, DataDelivery.NO_DELIVERY]