Skip to content

Commit ca48177

Browse files
authored
Merge branch 'master' into add-nallo-loqusdb-upload
2 parents 5b53a38 + 298513c commit ca48177

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+1297
-156
lines changed

.bumpversion.cfg

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[bumpversion]
2-
current_version = 69.5.3
2+
current_version = 69.8.1
33
commit = True
44
tag = True
55
tag_name = v{new_version}

.github/workflows/bumpversion.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ jobs:
1010
name: Bump version and push tags to master
1111
steps:
1212
- name: Bump version
13-
uses: Clinical-Genomics/bump2version-ci@v3
13+
uses: Clinical-Genomics/bump2version-ci@2.0.3
1414
env:
1515
BUMPVERSION_TOKEN: ${{ secrets.BUMPVERSION_TOKEN }}
1616
BUMPVERSION_AUTHOR: ${{ secrets.BUMPVERSION_AUTHOR }}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
"""add Raredisease ordertype
2+
3+
Revision ID: 039dbdf8af01
4+
Revises: 6362cfd4c61f
5+
Create Date: 2025-04-25 13:57:47.960893
6+
7+
"""
8+
9+
import sqlalchemy as sa
10+
from sqlalchemy.orm import declarative_base
11+
12+
from alembic import op
13+
14+
# revision identifiers, used by Alembic.
15+
revision = "039dbdf8af01"
16+
down_revision = "6362cfd4c61f"
17+
branch_labels = None
18+
depends_on = None
19+
20+
Base = declarative_base()
21+
22+
23+
old_order_types = [
24+
"BALSAMIC",
25+
"BALSAMIC_UMI",
26+
"FASTQ",
27+
"FLUFFY",
28+
"METAGENOME",
29+
"MICROBIAL_FASTQ",
30+
"MICROSALT",
31+
"MIP_DNA",
32+
"MIP_RNA",
33+
"PACBIO_LONG_READ",
34+
"NALLO",
35+
"RML",
36+
"RNAFUSION",
37+
"SARS_COV_2",
38+
"TAXPROFILER",
39+
"TOMTE",
40+
]
41+
42+
new_order_types = old_order_types.copy()
43+
new_order_types.append("RAREDISEASE")
44+
new_order_types.sort()
45+
46+
47+
class OrderTypeApplication(Base):
48+
"""Maps an order type to its allowed applications"""
49+
50+
__tablename__ = "order_type_application"
51+
order_type = sa.Column(sa.types.Enum(*new_order_types), primary_key=True)
52+
53+
54+
def upgrade():
55+
op.alter_column(
56+
table_name="order_type_application",
57+
column_name="order_type",
58+
existing_type=sa.Enum(*old_order_types),
59+
type_=sa.Enum(*new_order_types),
60+
nullable=False,
61+
)
62+
63+
64+
def downgrade():
65+
# Remove incompatible entries
66+
bind = op.get_bind()
67+
session = sa.orm.Session(bind=bind)
68+
69+
for link in session.query(OrderTypeApplication).filter(
70+
OrderTypeApplication.order_type == "RAREDISEASE"
71+
):
72+
session.delete(link)
73+
74+
session.commit()
75+
76+
# Modify the column type back to the old enum
77+
op.alter_column(
78+
table_name="order_type_application",
79+
column_name="order_type",
80+
existing_type=sa.Enum(*new_order_types),
81+
type_=sa.Enum(*old_order_types),
82+
nullable=False,
83+
)

cg/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
__title__ = "cg"
2-
__version__ = "69.5.3"
2+
__version__ = "69.8.1"

cg/constants/constants.py

+16-16
Original file line numberDiff line numberDiff line change
@@ -165,22 +165,22 @@ class SampleType(StrEnum):
165165

166166

167167
class DataDelivery(StrEnum):
168-
ANALYSIS_FILES: str = "analysis"
169-
ANALYSIS_SCOUT: str = "analysis-scout"
170-
BAM: str = "bam"
171-
FASTQ: str = "fastq"
172-
FASTQ_SCOUT: str = "fastq-scout"
173-
FASTQ_QC: str = "fastq_qc"
174-
FASTQ_ANALYSIS: str = "fastq-analysis"
175-
FASTQ_QC_ANALYSIS: str = "fastq_qc-analysis"
176-
FASTQ_ANALYSIS_SCOUT: str = "fastq-analysis-scout"
177-
NIPT_VIEWER: str = "nipt-viewer"
178-
NO_DELIVERY: str = "no-delivery"
179-
RAW_DATA_ANALYSIS: str = "raw_data-analysis"
180-
RAW_DATA_ANALYSIS_SCOUT: str = "raw_data-analysis-scout"
181-
RAW_DATA_SCOUT: str = "raw_data-scout"
182-
SCOUT: str = "scout"
183-
STATINA: str = "statina"
168+
ANALYSIS_FILES = "analysis"
169+
ANALYSIS_SCOUT = "analysis-scout"
170+
BAM = "bam"
171+
FASTQ = "fastq"
172+
FASTQ_SCOUT = "fastq-scout"
173+
FASTQ_QC = "fastq_qc"
174+
FASTQ_ANALYSIS = "fastq-analysis"
175+
FASTQ_QC_ANALYSIS = "fastq_qc-analysis"
176+
FASTQ_ANALYSIS_SCOUT = "fastq-analysis-scout"
177+
NIPT_VIEWER = "nipt-viewer"
178+
NO_DELIVERY = "no-delivery"
179+
RAW_DATA_ANALYSIS = "raw_data-analysis"
180+
RAW_DATA_ANALYSIS_SCOUT = "raw_data-analysis-scout"
181+
RAW_DATA_SCOUT = "raw_data-scout"
182+
SCOUT = "scout"
183+
STATINA = "statina"
184184

185185

186186
class HastaSlurmPartitions(StrEnum):

cg/constants/priority.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,11 @@ class PriorityTerms(StrEnum):
2727

2828

2929
class Priority(IntEnum):
30-
research: int = 0
31-
standard: int = 1
32-
clinical_trials: int = 2
33-
priority: int = 3
34-
express: int = 4
30+
research = 0
31+
standard = 1
32+
clinical_trials = 2
33+
priority = 3
34+
express = 4
3535

3636
@classmethod
3737
def priority_to_slurm_qos(cls) -> dict[int, str]:

cg/constants/scout.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ class ScoutUploadKey(StrEnum):
139139
NALLO_SAMPLE_TAGS: dict[str, set[str]] = dict(
140140
alignment_path={AlignmentFileTag.BAM, "haplotags"},
141141
assembly_alignment_path={AlignmentFileTag.BAM, "assembly"},
142-
d4_file={"mosdepth_d4"},
142+
d4_file={"coverage", "d4"},
143143
hificnv_coverage={"hificnv", "bigwig"},
144144
paraphase_alignment_path={AlignmentFileTag.BAM, NalloAnalysisTag.PARAPHASE},
145145
minor_allele_frequency_wig={"hificnv", "bigwig", "maf"},

cg/meta/orders/utils.py

+61-2
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,15 @@
44
from cg.constants.priority import Priority
55
from cg.models.orders.constants import OrderType
66
from cg.services.orders.constants import ORDER_TYPE_WORKFLOW_MAP
7+
from cg.services.orders.validation.models.existing_sample import ExistingSample
78
from cg.services.orders.validation.models.order import Order
89
from cg.services.orders.validation.models.order_with_cases import OrderWithCases
10+
from cg.services.orders.validation.models.order_with_samples import OrderWithSamples
11+
from cg.services.orders.validation.models.sample import Sample as ValidationSample
12+
from cg.store.models import Application, Sample
13+
from cg.store.store import Store
914

10-
DUE_TIME_BY_PRIORITY: dict[Priority, timedelta.days] = {
15+
DUE_TIME_BY_PRIORITY: dict[Priority, timedelta] = {
1116
Priority.express: timedelta(days=7),
1217
Priority.priority: timedelta(days=14),
1318
Priority.standard: timedelta(days=21),
@@ -21,7 +26,23 @@ def contains_existing_data(order: OrderWithCases) -> bool:
2126
return any(not case.is_new or case.enumerated_existing_samples for case in order.cases)
2227

2328

24-
def get_ticket_tags(order: Order, order_type: OrderType) -> list[str]:
29+
def contains_external_data(order: Order, status_db: Store) -> bool:
30+
"""Check if any existing or new sample from the given order is external."""
31+
existing_samples: list[Sample] = get_existing_samples(order=order, status_db=status_db)
32+
new_samples: list[ValidationSample] = get_new_samples(order=order)
33+
34+
if any([sample.is_external for sample in existing_samples]):
35+
return True
36+
37+
for sample in new_samples:
38+
application: Application | None = status_db.get_application_by_tag(sample.application)
39+
if application and application.is_external:
40+
return True
41+
42+
return False
43+
44+
45+
def get_ticket_tags(order: Order, order_type: OrderType, status_db: Store) -> list[str]:
2546
"""Generate ticket tags based on the order and order type"""
2647

2748
tags: list[str] = [ORDER_TYPE_WORKFLOW_MAP[order_type]]
@@ -30,6 +51,9 @@ def get_ticket_tags(order: Order, order_type: OrderType) -> list[str]:
3051
if contains_existing_data(order):
3152
tags.append("existing-data")
3253

54+
if contains_external_data(order=order, status_db=status_db):
55+
tags.append("external-data")
56+
3357
return tags
3458

3559

@@ -54,3 +78,38 @@ def get_due_by_date(priority: Priority) -> date:
5478
"""Get the ticket due by date based on the order priority."""
5579
due_by: datetime = datetime.now() + DUE_TIME_BY_PRIORITY[priority]
5680
return due_by.date()
81+
82+
83+
def get_existing_samples(order: Order, status_db: Store) -> list[Sample]:
84+
existing_samples: list[Sample] = []
85+
86+
if isinstance(order, OrderWithCases):
87+
existing_samples.extend(
88+
[
89+
sample
90+
for (_, case) in order.enumerated_existing_cases
91+
for sample in status_db.get_samples_by_case_id(case.internal_id)
92+
]
93+
)
94+
95+
existing_samples.extend(
96+
[
97+
sample
98+
for (_, case) in order.enumerated_new_cases
99+
for (_, existing_sample) in case.enumerated_existing_samples
100+
if (sample := status_db.get_sample_by_internal_id(existing_sample.internal_id))
101+
]
102+
)
103+
104+
return existing_samples
105+
106+
107+
def get_new_samples(order: Order) -> list[ValidationSample]:
108+
new_samples: list[ValidationSample] = []
109+
110+
if isinstance(order, OrderWithCases):
111+
new_samples.extend([sample for (_, _, sample) in order.enumerated_new_samples])
112+
elif isinstance(order, OrderWithSamples):
113+
new_samples.extend(order.samples)
114+
115+
return new_samples

cg/meta/workflow/nf_analysis.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -352,9 +352,15 @@ def create_params_file(self, case_id: str, dry_run: bool) -> None:
352352
).model_dump()
353353
LOG.debug("Adding parameters from the pipeline config file if it exist")
354354

355-
workflow_parameters: dict = built_workflow_parameters | (
355+
yaml_params: dict = (
356356
read_yaml(self.params) if hasattr(self, "params") and self.params else {}
357357
)
358+
359+
# Check for duplicate keys
360+
duplicate_keys = set(built_workflow_parameters.keys()) & set(yaml_params.keys())
361+
if duplicate_keys:
362+
raise ValueError(f"Duplicate parameter keys found: {duplicate_keys}")
363+
workflow_parameters: dict = built_workflow_parameters | (yaml_params)
358364
replaced_workflow_parameters: dict = self.replace_values_in_params_file(
359365
workflow_parameters=workflow_parameters
360366
)

cg/meta/workflow/raredisease.py

+6-24
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,15 @@
1313
CoveragePostResponse,
1414
CoverageSample,
1515
)
16-
from cg.constants import DEFAULT_CAPTURE_KIT, Workflow
16+
from cg.constants import Workflow
1717
from cg.constants.constants import GenomeVersion
1818
from cg.constants.nf_analysis import (
1919
RAREDISEASE_COVERAGE_FILE_TAGS,
2020
RAREDISEASE_COVERAGE_INTERVAL_TYPE,
2121
RAREDISEASE_COVERAGE_THRESHOLD,
22-
RAREDISEASE_PARENT_PEDDY_METRIC_CONDITION,
23-
RAREDISEASE_METRIC_CONDITIONS_WGS,
2422
RAREDISEASE_METRIC_CONDITIONS_WES,
23+
RAREDISEASE_METRIC_CONDITIONS_WGS,
24+
RAREDISEASE_PARENT_PEDDY_METRIC_CONDITION,
2525
)
2626
from cg.constants.scout import RAREDISEASE_CASE_TAGS, ScoutExportFileName
2727
from cg.constants.sequencing import SeqLibraryPrepCategory
@@ -33,9 +33,9 @@
3333
from cg.models.deliverables.metric_deliverables import MetricsBase, MultiqcDataJson
3434
from cg.models.raredisease.raredisease import (
3535
RarediseaseParameters,
36+
RarediseaseQCMetrics,
3637
RarediseaseSampleSheetEntry,
3738
RarediseaseSampleSheetHeaders,
38-
RarediseaseQCMetrics,
3939
)
4040
from cg.resources import RAREDISEASE_BUNDLE_FILENAMES_PATH
4141
from cg.store.models import CaseSample, Sample
@@ -94,30 +94,13 @@ def get_sample_sheet_content_per_sample(self, case_sample: CaseSample) -> list[l
9494

9595
@property
9696
def is_gene_panel_required(self) -> bool:
97-
"""Return True if a gene panel is needs to be created using the information in StatusDB and exporting it from Scout."""
97+
"""Return True if a gene panel needs to be created using the information in StatusDB and exporting it from Scout."""
9898
return True
9999

100-
def get_target_bed(self, case_id: str, analysis_type: str) -> str:
101-
"""
102-
Return the target bed file from LIMS and use default capture kit for WHOLE_GENOME_SEQUENCING.
103-
"""
104-
target_bed_file: str = self.get_target_bed_from_lims(case_id=case_id)
105-
if not target_bed_file:
106-
if analysis_type == AnalysisType.WGS:
107-
return DEFAULT_CAPTURE_KIT
108-
raise ValueError("No capture kit was found in LIMS")
109-
return target_bed_file
110-
111-
def get_germlinecnvcaller_flag(self, analysis_type: str) -> bool:
112-
if analysis_type == AnalysisType.WGS:
113-
return True
114-
return False
115-
116100
def get_built_workflow_parameters(self, case_id: str) -> RarediseaseParameters:
117101
"""Return parameters."""
118102
analysis_type: AnalysisType = self.get_data_analysis_type(case_id=case_id)
119-
target_bed_file: str = self.get_target_bed(case_id=case_id, analysis_type=analysis_type)
120-
skip_germlinecnvcaller = self.get_germlinecnvcaller_flag(analysis_type=analysis_type)
103+
target_bed_file: str = self.get_target_bed_from_lims(case_id=case_id) or ""
121104
outdir = self.get_case_path(case_id=case_id)
122105

123106
return RarediseaseParameters(
@@ -126,7 +109,6 @@ def get_built_workflow_parameters(self, case_id: str) -> RarediseaseParameters:
126109
analysis_type=analysis_type,
127110
target_bed_file=target_bed_file,
128111
save_mapped_as_cram=True,
129-
skip_germlinecnvcaller=skip_germlinecnvcaller,
130112
vcfanno_extra_resources=f"{outdir}/{ScoutExportFileName.MANAGED_VARIANTS}",
131113
vep_filters_scout_fmt=f"{outdir}/{ScoutExportFileName.PANELS}",
132114
)

cg/models/orders/constants.py

+15-14
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,23 @@
44

55

66
class OrderType(StrEnum):
7-
BALSAMIC: str = Workflow.BALSAMIC
8-
BALSAMIC_UMI: str = Workflow.BALSAMIC_UMI
9-
FASTQ: str = "fastq"
10-
FLUFFY: str = Workflow.FLUFFY
11-
METAGENOME: str = "metagenome"
12-
MICROBIAL_FASTQ: str = "microbial-fastq"
13-
MICROSALT: str = Workflow.MICROSALT
14-
MIP_DNA: str = Workflow.MIP_DNA
15-
MIP_RNA: str = Workflow.MIP_RNA
7+
BALSAMIC = Workflow.BALSAMIC
8+
BALSAMIC_UMI = Workflow.BALSAMIC_UMI
9+
FASTQ = "fastq"
10+
FLUFFY = Workflow.FLUFFY
11+
METAGENOME = "metagenome"
12+
MICROBIAL_FASTQ = "microbial-fastq"
13+
MICROSALT = Workflow.MICROSALT
14+
MIP_DNA = Workflow.MIP_DNA
15+
MIP_RNA = Workflow.MIP_RNA
1616
NALLO = Workflow.NALLO
1717
PACBIO_LONG_READ = "pacbio-long-read"
18-
RML: str = "rml"
19-
RNAFUSION: str = Workflow.RNAFUSION
20-
SARS_COV_2: str = "sars-cov-2"
21-
TAXPROFILER: str = Workflow.TAXPROFILER
22-
TOMTE: str = Workflow.TOMTE
18+
RML = "rml"
19+
RAREDISEASE = Workflow.RAREDISEASE
20+
RNAFUSION = Workflow.RNAFUSION
21+
SARS_COV_2 = "sars-cov-2"
22+
TAXPROFILER = Workflow.TAXPROFILER
23+
TOMTE = Workflow.TOMTE
2324

2425

2526
class ExcelSampleAliases(StrEnum):

cg/models/raredisease/raredisease.py

-1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,5 @@ class RarediseaseParameters(WorkflowParameters):
6767
target_bed_file: str
6868
analysis_type: str
6969
save_mapped_as_cram: bool
70-
skip_germlinecnvcaller: bool
7170
vcfanno_extra_resources: str
7271
vep_filters_scout_fmt: str

0 commit comments

Comments
 (0)