Skip to content

Commit

Permalink
Trailblazer priority control samples (#3991)(patch)
Browse files Browse the repository at this point in the history
### Fixed

- Controls ran with slurm qos express have the priority in trailblazer corresponding to the priority in statusDB
  • Loading branch information
eliottBo authored Dec 5, 2024
1 parent 40f53da commit c624ab0
Show file tree
Hide file tree
Showing 9 changed files with 121 additions and 15 deletions.
9 changes: 7 additions & 2 deletions cg/apps/demultiplex/demultiplex_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from cg.apps.tb import TrailblazerAPI
from cg.constants.constants import FileFormat, Workflow
from cg.constants.demultiplexing import DemultiplexingDirsAndFiles
from cg.constants.priority import SlurmQos
from cg.constants.priority import SlurmQos, TrailblazerPriority
from cg.constants.tb import AnalysisType
from cg.exc import HousekeeperFileMissingError
from cg.io.controller import WriteFile
Expand Down Expand Up @@ -49,6 +49,11 @@ def slurm_quality_of_service(self) -> Literal[SlurmQos.HIGH, SlurmQos.LOW]:
"""Return SLURM quality of service."""
return SlurmQos.LOW if self.environment == "stage" else SlurmQos.HIGH

@property
def trailblazer_priority(self) -> Literal[TrailblazerPriority.HIGH, TrailblazerPriority.LOW]:
"""Return Trailblazer quality of service."""
return TrailblazerPriority.LOW if self.environment == "stage" else TrailblazerPriority.HIGH

def set_dry_run(self, dry_run: bool) -> None:
"""Set dry run."""
LOG.debug(f"DemultiplexingAPI: Set dry run to {dry_run}")
Expand Down Expand Up @@ -213,7 +218,7 @@ def add_to_trailblazer(
analysis_type=AnalysisType.OTHER,
config_path=sequencing_run.trailblazer_config_path.as_posix(),
out_dir=sequencing_run.trailblazer_config_path.parent.as_posix(),
slurm_quality_of_service=self.slurm_quality_of_service,
priority=self.trailblazer_priority,
email=self.mail,
workflow=Workflow.DEMULTIPLEX,
)
Expand Down
7 changes: 4 additions & 3 deletions cg/apps/tb/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from cg.apps.tb.models import AnalysesResponse, TrailblazerAnalysis
from cg.constants import Workflow
from cg.constants.constants import APIMethods, FileFormat, JobType, WorkflowManager
from cg.constants.priority import SlurmQos
from cg.constants.priority import TrailblazerPriority
from cg.constants.tb import AnalysisStatus
from cg.exc import (
AnalysisNotCompletedError,
Expand All @@ -21,6 +21,7 @@
)
from cg.io.controller import APIRequest, ReadStream


LOG = logging.getLogger(__name__)


Expand Down Expand Up @@ -112,7 +113,7 @@ def add_pending_analysis(
analysis_type: str,
config_path: str,
out_dir: str,
slurm_quality_of_service: SlurmQos,
priority: TrailblazerPriority,
email: str = None,
order_id: int | None = None,
workflow: Workflow = None,
Expand All @@ -128,7 +129,7 @@ def add_pending_analysis(
"config_path": config_path,
"order_id": order_id,
"out_dir": out_dir,
"priority": slurm_quality_of_service,
"priority": priority,
"workflow": workflow.upper(),
"ticket": ticket,
"workflow_manager": workflow_manager,
Expand Down
3 changes: 2 additions & 1 deletion cg/cli/workflow/raw_data/raw_data_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from cg.constants.constants import Workflow
from cg.constants.tb import AnalysisStatus, AnalysisType
from cg.exc import CaseNotFoundError
from cg.meta.workflow.utils.utils import MAP_TO_TRAILBLAZER_PRIORITY
from cg.store.models import Analysis, Case
from cg.store.store import Store

Expand Down Expand Up @@ -41,7 +42,7 @@ def _add_analysis_to_trailblazer(self, case: Case) -> None:
config_path="",
order_id=case.latest_order.id,
out_dir="",
slurm_quality_of_service=case.slurm_priority,
priority=MAP_TO_TRAILBLAZER_PRIORITY[case.priority],
workflow=Workflow.RAW_DATA,
ticket=case.latest_ticket,
)
Expand Down
7 changes: 7 additions & 0 deletions cg/constants/priority.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ class SlurmQos(StrEnum):
EXPRESS: str = "express"


class TrailblazerPriority(StrEnum):
LOW: str = "low"
NORMAL: str = "normal"
HIGH: str = "high"
EXPRESS: str = "express"


class PriorityTerms(StrEnum):
EXPRESS: str = "express"
PRIORITY: str = "priority"
Expand Down
14 changes: 10 additions & 4 deletions cg/meta/workflow/analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
WorkflowManager,
)
from cg.constants.gene_panel import GenePanelCombo, GenePanelMasterList
from cg.constants.priority import SlurmQos
from cg.constants.priority import SlurmQos, TrailblazerPriority
from cg.constants.scout import HGNC_ID, ScoutExportFileName
from cg.constants.sequencing import SeqLibraryPrepCategory
from cg.constants.tb import AnalysisStatus, AnalysisType
Expand All @@ -29,7 +29,7 @@
from cg.meta.archive.archive import SpringArchiveAPI
from cg.meta.meta import MetaAPI
from cg.meta.workflow.fastq import FastqHandler
from cg.meta.workflow.utils.utils import are_all_samples_control
from cg.meta.workflow.utils.utils import are_all_samples_control, MAP_TO_TRAILBLAZER_PRIORITY
from cg.models.analysis import AnalysisModel
from cg.models.cg_config import CGConfig
from cg.models.fastq import FastqFileMeta
Expand Down Expand Up @@ -137,6 +137,12 @@ def get_slurm_qos_for_case(self, case_id: str) -> str:
priority: int = case.priority or Priority.research
return Priority.priority_to_slurm_qos().get(priority)

def get_trailblazer_priority(self, case_id: str) -> int:
"""Get the priority for the case in Trailblazer."""
case: Case = self.status_db.get_case_by_internal_id(internal_id=case_id)
priority: int = case.priority
return MAP_TO_TRAILBLAZER_PRIORITY[priority]

def get_workflow_manager(self) -> str:
"""Get workflow manager for a given workflow."""
return WorkflowManager.Slurm.value
Expand Down Expand Up @@ -290,7 +296,7 @@ def add_pending_trailblazer_analysis(
email: str = environ_email()
order_id: int = self._get_order_id_from_case_id(case_id)
out_dir: str = self.get_job_ids_path(case_id).parent.as_posix()
slurm_quality_of_service: str = self.get_slurm_qos_for_case(case_id)
priority: TrailblazerPriority = self.get_trailblazer_priority(case_id)
ticket: str = self.status_db.get_latest_ticket_from_case(case_id)
workflow: Workflow = self.workflow
workflow_manager: str = self.get_workflow_manager()
Expand All @@ -302,7 +308,7 @@ def add_pending_trailblazer_analysis(
email=email,
order_id=order_id,
out_dir=out_dir,
slurm_quality_of_service=slurm_quality_of_service,
priority=priority,
ticket=ticket,
workflow=workflow,
workflow_manager=workflow_manager,
Expand Down
10 changes: 10 additions & 0 deletions cg/meta/workflow/utils/utils.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from cg.constants.constants import ControlOptions
from cg.constants.priority import TrailblazerPriority
from cg.store.models import Case


Expand All @@ -9,3 +10,12 @@ def are_all_samples_control(case: Case) -> bool:
sample.control in [ControlOptions.NEGATIVE, ControlOptions.POSITIVE]
for sample in case.samples
)


MAP_TO_TRAILBLAZER_PRIORITY: dict[int, TrailblazerPriority] = {
0: TrailblazerPriority.LOW,
1: TrailblazerPriority.NORMAL,
2: TrailblazerPriority.HIGH,
3: TrailblazerPriority.EXPRESS,
4: TrailblazerPriority.NORMAL,
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from cg.apps.tb.models import TrailblazerAnalysis
from cg.constants import Priority, Workflow
from cg.constants.tb import AnalysisType
from cg.meta.workflow.utils.utils import MAP_TO_TRAILBLAZER_PRIORITY
from cg.services.analysis_service.analysis_service import AnalysisService
from cg.services.deliver_files.deliver_files_service.error_handling import (
handle_no_delivery_files_error,
Expand Down Expand Up @@ -132,7 +133,7 @@ def _add_trailblazer_tracking(self, case: Case, job_id: int, dry_run: bool) -> N
config_path=self.rsync_service.trailblazer_config_path.as_posix(),
order_id=case.latest_order.id,
out_dir=self.rsync_service.log_dir.as_posix(),
slurm_quality_of_service=Priority.priority_to_slurm_qos().get(case.priority),
priority=MAP_TO_TRAILBLAZER_PRIORITY[case.priority],
workflow=Workflow.RSYNC,
ticket=case.latest_ticket,
)
Expand Down
13 changes: 11 additions & 2 deletions cg/services/deliver_files/rsync/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from cg.constants import Workflow
from cg.constants.constants import FileFormat
from cg.constants.delivery import INBOX_NAME
from cg.constants.priority import SlurmAccount, SlurmQos
from cg.constants.priority import SlurmAccount, SlurmQos, TrailblazerPriority
from cg.constants.tb import AnalysisType
from cg.exc import CgError
from cg.io.controller import WriteFile
Expand Down Expand Up @@ -52,6 +52,15 @@ def slurm_quality_of_service(self) -> str:
"""Return the slurm quality of service depending on the slurm account."""
return SlurmQos.HIGH if self.account == SlurmAccount.PRODUCTION else SlurmQos.LOW

@property
def trailblazer_priority(self) -> TrailblazerPriority:
"""Return the trailblazer priority depending on the slurm account."""
return (
TrailblazerPriority.HIGH
if self.account == SlurmAccount.PRODUCTION
else TrailblazerPriority.LOW
)

@property
def trailblazer_config_path(self) -> Path:
"""Return Path to trailblazer config."""
Expand Down Expand Up @@ -161,7 +170,7 @@ def add_to_trailblazer_api(
analysis_type=AnalysisType.OTHER,
config_path=self.trailblazer_config_path.as_posix(),
out_dir=self.log_dir.as_posix(),
slurm_quality_of_service=self.slurm_quality_of_service,
priority=self.trailblazer_priority,
email=self.mail_user,
workflow=Workflow.RSYNC,
ticket=ticket,
Expand Down
70 changes: 68 additions & 2 deletions tests/meta/workflow/test_analysis.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@
from cg.constants import GenePanelMasterList, Priority, SequencingRunDataAvailability
from cg.constants.archiving import ArchiveLocations
from cg.constants.constants import ControlOptions
from cg.constants.priority import SlurmQos
from cg.constants.priority import SlurmQos, TrailblazerPriority
from cg.constants.sequencing import Sequencers
from cg.exc import AnalysisNotReadyError
from cg.meta.archive.archive import SpringArchiveAPI
from cg.meta.workflow.analysis import AnalysisAPI
from cg.meta.workflow.mip import MipAnalysisAPI
from cg.meta.workflow.mip_dna import MipDNAAnalysisAPI
from cg.meta.workflow.prepare_fastq import PrepareFastqAPI
from cg.meta.workflow.utils.utils import are_all_samples_control
from cg.meta.workflow.utils.utils import are_all_samples_control, MAP_TO_TRAILBLAZER_PRIORITY
from cg.models.fastq import FastqFileMeta
from cg.store.models import Case, Sample, IlluminaSequencingRun
from cg.store.store import Store
Expand Down Expand Up @@ -55,6 +55,39 @@ def test_get_slurm_qos_for_case(
assert slurm_qos is expected_slurm_qos


@pytest.mark.parametrize(
"priority,expected_trailblazer_priority",
[
(Priority.clinical_trials, TrailblazerPriority.NORMAL),
(Priority.research, TrailblazerPriority.LOW),
(Priority.standard, TrailblazerPriority.NORMAL),
(Priority.priority, TrailblazerPriority.HIGH),
(Priority.express, TrailblazerPriority.EXPRESS),
],
)
def test_get_trailblazer_priority(
case_id: str,
priority,
expected_trailblazer_priority,
mip_analysis_api: MipDNAAnalysisAPI,
analysis_store: Store,
):
"""Test get Trailblazer priority from the case priority"""

# GIVEN a store with a case with a specific priority
mip_analysis_api.status_db = analysis_store
case: Case = analysis_store.get_case_by_internal_id(case_id)
case.priority = priority

# WHEN getting the trailblazer priority for the case
trailblazer_priority: TrailblazerPriority = mip_analysis_api.get_trailblazer_priority(
case_id=case_id
)

# THEN the expected trailblazer priority should be returned
assert trailblazer_priority is expected_trailblazer_priority


def test_gene_panels_not_part_of_master_list(customer_id: str):
"""Test get only broad non-specific gene panels and custom gene panel list if a supplied gene panels is not part of master list."""
# GIVEN a customer who is a collaborator on the master list
Expand Down Expand Up @@ -624,3 +657,36 @@ def test_case_with_controls_get_correct_slurmqos(

# THEN the result should match the expected QOS
assert qos == expected_qos


@pytest.mark.parametrize(
"priority, expected_trailblazer_priority",
[
(Priority.clinical_trials, TrailblazerPriority.NORMAL),
(Priority.research, TrailblazerPriority.LOW),
(Priority.standard, TrailblazerPriority.NORMAL),
(Priority.priority, TrailblazerPriority.HIGH),
(Priority.express, TrailblazerPriority.EXPRESS),
],
)
def test_get_trailblazer_priority(
case_id: str,
priority: Priority,
expected_trailblazer_priority: TrailblazerPriority,
mip_analysis_api: MipDNAAnalysisAPI,
analysis_store: Store,
):
"""Test get Trailblazer priority from the case priority"""

# GIVEN a store with a case with a specific priority
mip_analysis_api.status_db = analysis_store
case: Case = analysis_store.get_case_by_internal_id(case_id)
case.priority = priority

# WHEN getting the trailblazer priority for the priority
trailblazer_priority: TrailblazerPriority = mip_analysis_api.get_trailblazer_priority(
case_id=case_id
)

# THEN the expected trailblazer priority should be returned
assert trailblazer_priority is expected_trailblazer_priority

0 comments on commit c624ab0

Please sign in to comment.