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

add(delivery service factory) #3647

Merged
merged 41 commits into from
Aug 29, 2024
Merged
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
4dd94c8
initial commit
ChrOertlin Aug 21, 2024
c09db2f
fixes
ChrOertlin Aug 21, 2024
f449da4
fix
ChrOertlin Aug 21, 2024
33456ef
add test
ChrOertlin Aug 21, 2024
f175540
fix test
ChrOertlin Aug 22, 2024
511ed7f
add exists assertion
ChrOertlin Aug 22, 2024
9063fc8
add func call
ChrOertlin Aug 22, 2024
8a64d3b
initial commit
ChrOertlin Aug 22, 2024
83d6c9f
Update tests/fixture_plugins/delivery_fixtures/delivery_files_models_…
ChrOertlin Aug 22, 2024
bacaa11
Update tests/fixture_plugins/delivery_fixtures/delivery_files_models_…
ChrOertlin Aug 22, 2024
0533071
Apply suggestions from code review
ChrOertlin Aug 22, 2024
3fbc9ef
review
ChrOertlin Aug 22, 2024
4eca28e
Merge branch 'add-file-mover' into add-file-reformatters
ChrOertlin Aug 22, 2024
f83f694
conflicsts
ChrOertlin Aug 22, 2024
652938e
refactor logic
ChrOertlin Aug 26, 2024
1aad85b
fix formatter
ChrOertlin Aug 26, 2024
6cabd68
fix concatenation
ChrOertlin Aug 26, 2024
56d7ec1
tests
ChrOertlin Aug 26, 2024
2845d05
add tests for formatters
ChrOertlin Aug 27, 2024
481c518
add test
ChrOertlin Aug 27, 2024
293ee09
review comments
ChrOertlin Aug 27, 2024
273f19f
setup factory
ChrOertlin Aug 27, 2024
981395f
add supporting functions
ChrOertlin Aug 27, 2024
db99c05
make super call
ChrOertlin Aug 27, 2024
e187522
Merge branch 'add-file-reformatters' into add-delivery-services
ChrOertlin Aug 27, 2024
6ab6d5e
changes
ChrOertlin Aug 27, 2024
93056cd
simplifiy formatter
ChrOertlin Aug 27, 2024
c13ce34
add docstring
ChrOertlin Aug 27, 2024
5d1ddb9
solve imports
ChrOertlin Aug 27, 2024
ebcf010
Merge branch 'add-file-reformatters' into add-delivery-services
ChrOertlin Aug 27, 2024
25e8e15
add factory
ChrOertlin Aug 27, 2024
e20ccb0
add builder test
ChrOertlin Aug 27, 2024
b1e1e2b
merge conflicts
ChrOertlin Aug 29, 2024
e490438
cleanup
ChrOertlin Aug 29, 2024
c751d50
fix doc
ChrOertlin Aug 29, 2024
2bda145
cleanup
ChrOertlin Aug 29, 2024
da1416d
Apply suggestions from code review
ChrOertlin Aug 29, 2024
befb44b
typehints
ChrOertlin Aug 29, 2024
a63e099
merge
ChrOertlin Aug 29, 2024
6f8c117
rename class
ChrOertlin Aug 29, 2024
40f7ac3
missing typehints
ChrOertlin Aug 29, 2024
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
Empty file.
Original file line number Diff line number Diff line change
@@ -1,30 +1,24 @@
from abc import abstractmethod, ABC

from pathlib import Path
from cg.services.file_delivery.fetch_file_service.fetch_delivery_files_service import (
FetchDeliveryFilesService,
)

from cg.services.file_delivery.fetch_file_service.models import DeliveryFiles
from cg.services.file_delivery.file_formatter_service.delivery_file_formatting_service import (
DeliveryFileFormattingService,
)

from cg.services.file_delivery.move_files_service.move_delivery_files_service import (
MoveDeliveryFilesService,
)


class DeliverFilesService(ABC):
class DeliverFilesService:
"""
Abstract class that encapsulates the logic required for delivering files to the customer.

Deliver files to the customer inbox on hasta.
1. Get the files to deliver from Housekeeper based on workflow and data delivery
2. Create a delivery folder structure in the customer folder on Hasta and move the files there
3. Reformatting of output / renaming of files
4. Start a Rsync job to upload the files to Caesar
5. Track the status of the Rsync job in Trailblazer
"""

@abstractmethod
def __init__(
self,
delivery_file_manager_service: FetchDeliveryFilesService,
Expand All @@ -35,7 +29,10 @@ def __init__(
self.file_mover = move_file_service
self.file_formatter = file_formatter_service

@abstractmethod
def deliver_files_for_case(self, case_id: str) -> None:
def deliver_files_for_case(self, case_id: str, delivery_base_path: Path) -> None:
"""Deliver the files to the customer folder."""
pass
delivery_files: DeliveryFiles = self.file_manager.get_files_to_deliver(case_id)
moved_files: DeliveryFiles = self.file_mover.move_files(
delivery_files=delivery_files, delivery_base_path=delivery_base_path
)
self.file_formatter.format_files(moved_files)
ChrOertlin marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
"""Module for the factory of the deliver files service."""

from cg.apps.housekeeper.hk import HousekeeperAPI
from cg.constants import Workflow, DataDelivery
from cg.services.fastq_concatenation_service.fastq_concatenation_service import (
FastqConcatenationService,
)
from cg.services.file_delivery.deliver_files_service.deliver_files_service import (
DeliverFilesService,
)
from cg.services.file_delivery.fetch_delivery_files_tags.fetch_delivery_file_tags_service import (
FetchDeliveryFileTagsService,
)
from cg.services.file_delivery.fetch_delivery_files_tags.fetch_sample_and_case_delivery_file_tags_service import (
FetchSampleAndCaseDeliveryFileTagsService,
)
from cg.services.file_delivery.fetch_file_service.fetch_analysis_files_service import (
FetchAnalysisDeliveryFilesService,
)
from cg.services.file_delivery.fetch_file_service.fetch_delivery_files_service import (
FetchDeliveryFilesService,
)
from cg.services.file_delivery.fetch_file_service.fetch_fastq_analysis_files_service import (
FetchFastqAndAnalysisDeliveryFilesService,
)
from cg.services.file_delivery.fetch_file_service.fetch_fastq_files_service import (
FetchFastqDeliveryFilesService,
)
from cg.services.file_delivery.file_formatter_service.delivery_file_formatter import (
DeliveryFileFormatter,
)

from cg.services.file_delivery.file_formatter_service.delivery_file_formatting_service import (
DeliveryFileFormattingService,
)
from cg.services.file_delivery.file_formatter_service.utils.case_file_formatter import (
CaseFileFormatter,
)
from cg.services.file_delivery.file_formatter_service.utils.sample_file_concatenation_formatter import (
SampleFileConcatenationFormatter,
)
from cg.services.file_delivery.file_formatter_service.utils.sample_file_formatter import (
SampleFileFormatter,
)
from cg.services.file_delivery.move_files_service.move_delivery_files_service import (
MoveDeliveryFilesService,
)
from cg.store.store import Store


class DeliveryServiceBuilder:
"""Class to build the delivery services based on workflow and delivery type."""

def __init__(self, store: Store, hk_api: HousekeeperAPI):
self.store = store
self.hk_api = hk_api

@staticmethod
def _get_file_tag_fetcher(delivery_type: DataDelivery) -> FetchDeliveryFileTagsService:
"""Get the file tag fetcher based on the delivery type."""
service_map = {
DataDelivery.FASTQ: FetchSampleAndCaseDeliveryFileTagsService,
DataDelivery.ANALYSIS_FILES: FetchSampleAndCaseDeliveryFileTagsService,
DataDelivery.FASTQ_ANALYSIS: FetchSampleAndCaseDeliveryFileTagsService,
}
return service_map[delivery_type]()

def _get_file_fetcher(self, delivery_type: DataDelivery) -> FetchDeliveryFilesService:
"""Get the file fetcher based on the delivery type."""
service_map = {
DataDelivery.FASTQ: FetchFastqDeliveryFilesService,
DataDelivery.ANALYSIS_FILES: FetchAnalysisDeliveryFilesService,
DataDelivery.FASTQ_ANALYSIS: FetchFastqAndAnalysisDeliveryFilesService,
}
file_tag_fetcher = self._get_file_tag_fetcher(delivery_type)
return service_map[delivery_type](
status_db=self.store,
hk_api=self.hk_api,
tags_fetcher=file_tag_fetcher,
)

@staticmethod
def _get_sample_file_formatter(
workflow: Workflow,
) -> SampleFileFormatter | SampleFileConcatenationFormatter:
"""Get the file formatter service based on the workflow."""
if workflow in [Workflow.MICROSALT, Workflow.MUTANT]:
return SampleFileConcatenationFormatter(FastqConcatenationService())
ChrOertlin marked this conversation as resolved.
Show resolved Hide resolved
return SampleFileFormatter()

def build_delivery_service(
self, workflow: Workflow, delivery_type: DataDelivery
) -> DeliverFilesService:
"""Build a delivery service based on the workflow and delivery type."""
file_fetcher = self._get_file_fetcher(delivery_type)
sample_file_formatter = self._get_sample_file_formatter(workflow)
file_formatter: DeliveryFileFormattingService = DeliveryFileFormatter(
case_file_formatter=CaseFileFormatter(), sample_file_formatter=sample_file_formatter
)
return DeliverFilesService(
delivery_file_manager_service=file_fetcher,
move_file_service=MoveDeliveryFilesService(),
ChrOertlin marked this conversation as resolved.
Show resolved Hide resolved
file_formatter_service=file_formatter,
)
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
from cg.apps.housekeeper.hk import HousekeeperAPI
from cg.services.file_delivery.fetch_delivery_files_tags.fetch_delivery_file_tags_service import (
FetchDeliveryFileTagsService,
)
from cg.services.file_delivery.fetch_delivery_files_tags.fetch_sample_and_case_delivery_file_tags_service import (
FetchSampleAndCaseDeliveryFileTagsService,
)
Expand All @@ -17,30 +20,33 @@


class FetchFastqAndAnalysisDeliveryFilesService(FetchDeliveryFilesService):
def __init__(self, status_db: Store, hk_api: HousekeeperAPI):

def __init__(
self, status_db: Store, hk_api: HousekeeperAPI, tags_fetcher: FetchDeliveryFileTagsService
):
self.status_db = status_db
self.hk_api = hk_api
self.tags_fetcher = tags_fetcher

def get_files_to_deliver(self, case_id: str) -> DeliveryFiles:
case: Case = self.status_db.get_case_by_internal_id(internal_id=case_id)
fetch_fastq_service = FetchFastqDeliveryFilesService(
self.status_db,
self.hk_api,
tags_fetcher=FetchSampleAndCaseDeliveryFileTagsService(),
)
fetch_analysis_service = FetchAnalysisDeliveryFilesService(
self.status_db,
self.hk_api,
tags_fetcher=FetchSampleAndCaseDeliveryFileTagsService(),
)

fastq_files: DeliveryFiles = fetch_fastq_service.get_files_to_deliver(case_id)
analysis_files: DeliveryFiles = fetch_analysis_service.get_files_to_deliver(case_id)
case = self._get_case(case_id)
fastq_files = self._fetch_files(FetchFastqDeliveryFilesService, case_id)
analysis_files = self._fetch_files(FetchAnalysisDeliveryFilesService, case_id)
ChrOertlin marked this conversation as resolved.
Show resolved Hide resolved
delivery_data = DeliveryMetaData(
customer_internal_id=case.customer.internal_id, ticket_id=case.latest_ticket
)

return DeliveryFiles(
delivery_data=delivery_data,
case_files=analysis_files.case_files,
sample_files=analysis_files.sample_files + fastq_files.sample_files,
)

def _get_case(self, case_id: str) -> Case:
"""Fetch the case from the database."""
return self.status_db.get_case_by_internal_id(internal_id=case_id)

def _fetch_files(self, service_class: type, case_id: str) -> DeliveryFiles:
"""Fetch files using the provided service class."""
service = service_class(self.status_db, self.hk_api, tags_fetcher=self.tags_fetcher)
return service.get_files_to_deliver(case_id)
ChrOertlin marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import pytest
from unittest.mock import MagicMock, patch

from cg.constants import Workflow, DataDelivery
from cg.services.file_delivery.deliver_files_service.deliver_files_service import (
DeliverFilesService,
)
from cg.services.file_delivery.deliver_files_service.deliver_files_service_factory import (
DeliveryServiceBuilder,
)
from cg.services.file_delivery.fetch_delivery_files_tags.fetch_sample_and_case_delivery_file_tags_service import (
FetchSampleAndCaseDeliveryFileTagsService,
)
from cg.services.file_delivery.fetch_file_service.fetch_analysis_files_service import (
FetchAnalysisDeliveryFilesService,
)
from cg.services.file_delivery.fetch_file_service.fetch_fastq_analysis_files_service import (
FetchFastqAndAnalysisDeliveryFilesService,
)
from cg.services.file_delivery.fetch_file_service.fetch_fastq_files_service import (
FetchFastqDeliveryFilesService,
)
from cg.services.file_delivery.file_formatter_service.utils.sample_file_concatenation_formatter import (
SampleFileConcatenationFormatter,
)
from cg.services.file_delivery.file_formatter_service.utils.sample_file_formatter import (
SampleFileFormatter,
)
from cg.services.file_delivery.move_files_service.move_delivery_files_service import (
MoveDeliveryFilesService,
)


@pytest.mark.parametrize(
"workflow,delivery_type,expected_tag_fetcher,expected_file_fetcher,expected_file_mover,expected_sample_file_formatter",
[
(
Workflow.MICROSALT,
DataDelivery.FASTQ,
FetchSampleAndCaseDeliveryFileTagsService,
FetchFastqDeliveryFilesService,
MoveDeliveryFilesService,
SampleFileConcatenationFormatter,
),
(
Workflow.MUTANT,
DataDelivery.ANALYSIS_FILES,
FetchSampleAndCaseDeliveryFileTagsService,
FetchAnalysisDeliveryFilesService,
MoveDeliveryFilesService,
SampleFileConcatenationFormatter,
),
(
Workflow.BALSAMIC,
DataDelivery.FASTQ_ANALYSIS,
FetchSampleAndCaseDeliveryFileTagsService,
FetchFastqAndAnalysisDeliveryFilesService,
MoveDeliveryFilesService,
SampleFileFormatter,
),
],
)
def test_build_delivery_service(
workflow,
delivery_type,
expected_tag_fetcher,
expected_file_fetcher,
expected_file_mover,
expected_sample_file_formatter,
):
# GIVEN a delivery service builder with mocked store and hk_api
store_mock = MagicMock()
hk_api_mock = MagicMock()
builder = DeliveryServiceBuilder(store=store_mock, hk_api=hk_api_mock)

# WHEN building a delivery service
delivery_service: DeliverFilesService = builder.build_delivery_service(
workflow=workflow, delivery_type=delivery_type
)

# THEN the correct file formatter and file fetcher services are used
assert isinstance(delivery_service.file_manager.tags_fetcher, expected_tag_fetcher)
assert isinstance(delivery_service.file_manager, expected_file_fetcher)
assert isinstance(delivery_service.file_mover, expected_file_mover)
assert isinstance(
delivery_service.file_formatter.sample_file_formatter, expected_sample_file_formatter
)
Loading