cg 76.0.0__py3-none-any.whl → 83.14.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cg/__init__.py +1 -1
- cg/apps/housekeeper/hk.py +18 -1
- cg/apps/tb/api.py +42 -5
- cg/cli/transfer.py +13 -2
- cg/cli/upload/mutacc.py +16 -3
- cg/cli/upload/scout.py +2 -2
- cg/cli/upload/utils.py +10 -1
- cg/cli/workflow/balsamic/base.py +86 -172
- cg/cli/workflow/balsamic/options.py +3 -48
- cg/cli/workflow/balsamic/umi.py +210 -15
- cg/cli/workflow/microsalt/base.py +4 -2
- cg/cli/workflow/mip_dna/base.py +1 -1
- cg/cli/workflow/nallo/base.py +73 -23
- cg/cli/workflow/nf_analysis.py +5 -207
- cg/cli/workflow/raredisease/base.py +41 -54
- cg/cli/workflow/rnafusion/base.py +38 -8
- cg/cli/workflow/taxprofiler/base.py +31 -18
- cg/cli/workflow/tomte/base.py +83 -10
- cg/constants/constants.py +25 -30
- cg/constants/devices.py +6 -1
- cg/constants/gene_panel.py +3 -1
- cg/constants/housekeeper_tags.py +28 -28
- cg/constants/lims.py +4 -0
- cg/constants/nf_analysis.py +0 -1
- cg/constants/observations.py +21 -5
- cg/constants/orderforms.py +3 -3
- cg/constants/pacbio.py +1 -0
- cg/constants/priority.py +1 -1
- cg/constants/report.py +1 -0
- cg/constants/scout.py +12 -9
- cg/constants/sequencing.py +2 -2
- cg/constants/tb.py +5 -5
- cg/exc.py +27 -5
- cg/meta/compress/compress.py +7 -2
- cg/meta/delivery_report/balsamic.py +3 -1
- cg/meta/delivery_report/delivery_report_api.py +4 -3
- cg/meta/delivery_report/nallo.py +11 -11
- cg/meta/delivery_report/raredisease.py +7 -3
- cg/meta/delivery_report/templates/macros/data_analysis/qc_metrics/balsamic_qc_metrics.html +1 -0
- cg/meta/delivery_report/templates/macros/ticket_system.html +1 -1
- cg/meta/observations/balsamic_observations_api.py +110 -14
- cg/meta/observations/mip_dna_observations_api.py +1 -1
- cg/meta/observations/nallo_observations_api.py +1 -1
- cg/meta/observations/observations_api.py +23 -32
- cg/meta/observations/raredisease_observations_api.py +1 -1
- cg/meta/tar/tar.py +5 -2
- cg/meta/transfer/lims.py +32 -3
- cg/meta/upload/balsamic/balsamic.py +1 -8
- cg/meta/upload/coverage.py +5 -5
- cg/meta/upload/raredisease/raredisease.py +3 -0
- cg/meta/upload/scout/hk_tags.py +1 -0
- cg/meta/upload/scout/nallo_config_builder.py +31 -7
- cg/meta/workflow/balsamic.py +70 -36
- cg/meta/workflow/fastq.py +8 -0
- cg/meta/workflow/microsalt/quality_controller/models.py +0 -2
- cg/meta/workflow/microsalt/quality_controller/quality_controller.py +8 -16
- cg/meta/workflow/microsalt/quality_controller/result_logger.py +3 -6
- cg/meta/workflow/microsalt/quality_controller/utils.py +2 -45
- cg/meta/workflow/nallo.py +21 -99
- cg/meta/workflow/nf_analysis.py +12 -263
- cg/meta/workflow/raredisease.py +3 -112
- cg/meta/workflow/rnafusion.py +2 -34
- cg/meta/workflow/taxprofiler.py +2 -38
- cg/meta/workflow/tomte.py +2 -42
- cg/models/balsamic/config.py +0 -24
- cg/models/balsamic/metrics.py +5 -3
- cg/models/cg_config.py +39 -16
- cg/models/deliverables/metric_deliverables.py +1 -1
- cg/models/delivery_report/metadata.py +2 -1
- cg/models/nallo/nallo.py +14 -64
- cg/models/nf_analysis.py +1 -41
- cg/models/raredisease/raredisease.py +1 -63
- cg/models/rnafusion/rnafusion.py +0 -26
- cg/models/scout/scout_load_config.py +5 -2
- cg/models/taxprofiler/taxprofiler.py +0 -42
- cg/models/tomte/tomte.py +0 -69
- cg/resources/nallo_bundle_filenames.yaml +292 -22
- cg/resources/raredisease_bundle_filenames.yaml +11 -1
- cg/resources/taxprofiler_bundle_filenames.yaml +20 -0
- cg/server/admin.py +106 -25
- cg/server/app.py +15 -4
- cg/server/endpoints/sequencing_run/dtos.py +21 -3
- cg/server/endpoints/sequencing_run/pacbio_sequencing_run.py +29 -10
- cg/server/endpoints/sequencing_run/pacbio_smrt_cell_metrics.py +20 -0
- cg/services/analysis_starter/{service.py → analysis_starter.py} +11 -9
- cg/services/analysis_starter/configurator/abstract_model.py +8 -0
- cg/services/analysis_starter/configurator/configurator.py +1 -1
- cg/services/analysis_starter/configurator/extensions/nallo.py +27 -0
- cg/services/analysis_starter/configurator/extensions/{abstract.py → pipeline_extension.py} +1 -1
- cg/services/analysis_starter/configurator/extensions/raredisease.py +3 -1
- cg/services/analysis_starter/configurator/extensions/tomte_extension.py +28 -0
- cg/services/analysis_starter/configurator/file_creators/balsamic_config.py +240 -0
- cg/services/analysis_starter/configurator/file_creators/gene_panel.py +10 -5
- cg/services/analysis_starter/configurator/file_creators/nextflow/params_file/abstract.py +2 -1
- cg/services/analysis_starter/configurator/file_creators/nextflow/params_file/models.py +40 -1
- cg/services/analysis_starter/configurator/file_creators/nextflow/params_file/nallo.py +37 -0
- cg/services/analysis_starter/configurator/file_creators/nextflow/params_file/raredisease.py +8 -5
- cg/services/analysis_starter/configurator/file_creators/nextflow/params_file/tomte_params_file_creator.py +64 -0
- cg/services/analysis_starter/configurator/file_creators/nextflow/sample_sheet/creator.py +1 -1
- cg/services/analysis_starter/configurator/file_creators/nextflow/sample_sheet/nallo_sample_sheet_creator.py +65 -0
- cg/services/analysis_starter/configurator/file_creators/nextflow/sample_sheet/protocol.py +12 -0
- cg/services/analysis_starter/configurator/file_creators/nextflow/sample_sheet/{raredisease.py → raredisease_sample_sheet_creator.py} +2 -2
- cg/services/analysis_starter/configurator/file_creators/nextflow/sample_sheet/{rnafusion.py → rnafusion_sample_sheet_creator.py} +2 -2
- cg/services/analysis_starter/configurator/file_creators/nextflow/sample_sheet/{taxprofiler.py → taxprofiler_sample_sheet_creator.py} +2 -2
- cg/services/analysis_starter/configurator/file_creators/nextflow/sample_sheet/tomte_sample_sheet_creator.py +36 -0
- cg/services/analysis_starter/configurator/implementations/balsamic.py +68 -0
- cg/services/analysis_starter/configurator/implementations/nextflow.py +22 -5
- cg/services/analysis_starter/configurator/models/balsamic.py +152 -0
- cg/services/analysis_starter/configurator/models/mip_dna.py +6 -8
- cg/services/analysis_starter/configurator/models/nextflow.py +9 -0
- cg/services/analysis_starter/constants.py +2 -0
- cg/services/analysis_starter/factories/configurator_factory.py +131 -51
- cg/services/analysis_starter/factories/starter_factory.py +36 -7
- cg/services/analysis_starter/input_fetcher/implementations/bam_fetcher.py +57 -0
- cg/services/analysis_starter/input_fetcher/implementations/fastq_fetcher.py +3 -3
- cg/services/analysis_starter/submitters/seqera_platform/{client.py → seqera_platform_client.py} +19 -3
- cg/services/analysis_starter/submitters/seqera_platform/seqera_platform_submitter.py +73 -0
- cg/services/analysis_starter/submitters/submitter.py +1 -1
- cg/services/analysis_starter/submitters/subprocess/submitter.py +2 -1
- cg/services/analysis_starter/tracker/implementations/balsamic.py +22 -0
- cg/services/analysis_starter/tracker/implementations/microsalt.py +4 -4
- cg/services/analysis_starter/tracker/implementations/mip_dna.py +4 -1
- cg/services/analysis_starter/tracker/implementations/{nextflow.py → nextflow_tracker.py} +6 -4
- cg/services/analysis_starter/tracker/tracker.py +19 -15
- cg/services/deliver_files/factory.py +1 -1
- cg/services/delivery_message/messages/__init__.py +24 -14
- cg/services/delivery_message/messages/{microsalt_mwr_message.py → microsalt_message.py} +1 -1
- cg/services/delivery_message/utils.py +4 -40
- cg/services/illumina/backup/backup_service.py +29 -7
- cg/services/orders/validation/constants.py +3 -0
- cg/services/orders/validation/index_sequences.py +558 -0
- cg/services/orders/validation/order_types/microsalt/models/sample.py +2 -3
- cg/services/run_devices/pacbio/data_storage_service/pacbio_store_service.py +39 -18
- cg/services/run_devices/pacbio/data_transfer_service/data_transfer_service.py +8 -2
- cg/services/run_devices/pacbio/data_transfer_service/dto.py +9 -3
- cg/services/run_devices/pacbio/data_transfer_service/utils.py +14 -7
- cg/services/run_devices/pacbio/metrics_parser/models.py +1 -0
- cg/services/run_devices/pacbio/sequencing_runs_service.py +35 -7
- cg/services/sequencing_qc_service/quality_checks/checks.py +18 -16
- cg/services/sequencing_qc_service/quality_checks/utils.py +82 -18
- cg/services/sequencing_qc_service/sequencing_qc_service.py +12 -10
- cg/store/crud/create.py +73 -42
- cg/store/crud/read.py +73 -7
- cg/store/crud/update.py +14 -3
- cg/store/models.py +98 -35
- cg/store/store.py +8 -1
- {cg-76.0.0.dist-info → cg-83.14.0.dist-info}/METADATA +1 -1
- {cg-76.0.0.dist-info → cg-83.14.0.dist-info}/RECORD +150 -138
- cg/services/analysis_starter/submitters/seqera_platform/submitter.py +0 -39
- cg/services/delivery_message/messages/microsalt_mwx_message.py +0 -18
- {cg-76.0.0.dist-info → cg-83.14.0.dist-info}/WHEEL +0 -0
- {cg-76.0.0.dist-info → cg-83.14.0.dist-info}/entry_points.txt +0 -0
cg/constants/nf_analysis.py
CHANGED
|
@@ -17,7 +17,6 @@ class NfTowerStatus(StrEnum):
|
|
|
17
17
|
|
|
18
18
|
NALLO_GENERAL_METRIC_CONDITIONS: dict[str, dict[str, Any]] = {
|
|
19
19
|
"median_coverage": {"norm": "gt", "threshold": 20},
|
|
20
|
-
"predicted_sex_sex_check": {"norm": "eq", "threshold": None},
|
|
21
20
|
}
|
|
22
21
|
|
|
23
22
|
NALLO_PARENT_PEDDY_METRIC_CONDITION: dict[str, dict[str, Any]] = {
|
cg/constants/observations.py
CHANGED
|
@@ -14,10 +14,14 @@ LOQUSDB_SUPPORTED_WORKFLOWS = [
|
|
|
14
14
|
]
|
|
15
15
|
LOQUSDB_RARE_DISEASE_CUSTOMERS = [CustomerId.CUST002, CustomerId.CUST003, CustomerId.CUST004]
|
|
16
16
|
LOQUSDB_CANCER_CUSTOMERS = [
|
|
17
|
+
CustomerId.CUST002,
|
|
18
|
+
CustomerId.CUST087,
|
|
17
19
|
CustomerId.CUST110,
|
|
18
20
|
CustomerId.CUST127,
|
|
19
21
|
CustomerId.CUST143,
|
|
20
22
|
CustomerId.CUST147,
|
|
23
|
+
CustomerId.CUST175,
|
|
24
|
+
CustomerId.CUST185,
|
|
21
25
|
]
|
|
22
26
|
LOQUSDB_LONG_READ_CUSTOMERS = [
|
|
23
27
|
CustomerId.CUST002,
|
|
@@ -35,9 +39,18 @@ LOQUSDB_RARE_DISEASE_SEQUENCING_METHODS = [
|
|
|
35
39
|
LOQUSDB_CANCER_SEQUENCING_METHODS = [
|
|
36
40
|
CancerAnalysisType.TUMOR_WGS,
|
|
37
41
|
CancerAnalysisType.TUMOR_NORMAL_WGS,
|
|
42
|
+
CancerAnalysisType.TUMOR_PANEL,
|
|
38
43
|
]
|
|
39
44
|
|
|
40
45
|
|
|
46
|
+
class BalsamicObservationPanel(StrEnum):
|
|
47
|
+
"""Group of panels which have associated LoqusDB instances."""
|
|
48
|
+
|
|
49
|
+
EXOME = "Twist Exome Comprehensive"
|
|
50
|
+
LYMPHOID = "GMSlymphoid"
|
|
51
|
+
MYELOID = "GMSmyeloid"
|
|
52
|
+
|
|
53
|
+
|
|
41
54
|
class BalsamicLoadParameters(Enum):
|
|
42
55
|
"""Cancer Loqusdb load command parameters."""
|
|
43
56
|
|
|
@@ -57,11 +70,14 @@ class BalsamicObservationsAnalysisTag(StrEnum):
|
|
|
57
70
|
class LoqusdbInstance(StrEnum):
|
|
58
71
|
"""Observations instances."""
|
|
59
72
|
|
|
60
|
-
LWP
|
|
61
|
-
WGS
|
|
62
|
-
WES
|
|
63
|
-
SOMATIC
|
|
64
|
-
TUMOR
|
|
73
|
+
LWP = "loqusdb-lwp"
|
|
74
|
+
WGS = "loqusdb"
|
|
75
|
+
WES = "loqusdb-wes"
|
|
76
|
+
SOMATIC = "loqusdb-somatic"
|
|
77
|
+
TUMOR = "loqusdb-tumor"
|
|
78
|
+
SOMATIC_LYMPHOID = "loqusdb-somatic-lymphoid"
|
|
79
|
+
SOMATIC_MYELOID = "loqusdb-somatic-myeloid"
|
|
80
|
+
SOMATIC_EXOME = "loqusdb-somatic-exome"
|
|
65
81
|
|
|
66
82
|
|
|
67
83
|
class MipDNALoadParameters(Enum):
|
cg/constants/orderforms.py
CHANGED
|
@@ -31,11 +31,11 @@ class Orderform(StrEnum):
|
|
|
31
31
|
"""Returns the current version of the given order form."""
|
|
32
32
|
current_order_form_versions = {
|
|
33
33
|
Orderform.MIP_DNA: "35",
|
|
34
|
-
Orderform.RML: "
|
|
35
|
-
Orderform.MICROSALT: "
|
|
34
|
+
Orderform.RML: "21",
|
|
35
|
+
Orderform.MICROSALT: "12",
|
|
36
36
|
Orderform.NALLO: "1",
|
|
37
37
|
Orderform.SARS_COV_2: "10",
|
|
38
|
-
Orderform.MICROBIAL_FASTQ: "
|
|
38
|
+
Orderform.MICROBIAL_FASTQ: "3",
|
|
39
39
|
Orderform.PACBIO_LONG_READ: "2",
|
|
40
40
|
}
|
|
41
41
|
return current_order_form_versions[order_form]
|
cg/constants/pacbio.py
CHANGED
|
@@ -64,6 +64,7 @@ class SmrtLinkDatabasesAliases:
|
|
|
64
64
|
BIO_SAMPLE_NAME: str = "bioSampleName"
|
|
65
65
|
CELL_ID: str = "cellId"
|
|
66
66
|
CELL_INDEX: str = "cellIndex"
|
|
67
|
+
INSTRUMENT_NAME: str = "instrumentName"
|
|
67
68
|
MOVIE_NAME: str = "metadataContextId"
|
|
68
69
|
PATH: str = "path"
|
|
69
70
|
RUN_COMPLETED_AT = "createdAt"
|
cg/constants/priority.py
CHANGED
|
@@ -34,7 +34,7 @@ class Priority(IntEnum):
|
|
|
34
34
|
express = 4
|
|
35
35
|
|
|
36
36
|
@classmethod
|
|
37
|
-
def priority_to_slurm_qos(cls) -> dict[
|
|
37
|
+
def priority_to_slurm_qos(cls) -> dict["Priority", SlurmQos]:
|
|
38
38
|
return {
|
|
39
39
|
Priority.research: SlurmQos.LOW,
|
|
40
40
|
Priority.standard: SlurmQos.NORMAL,
|
cg/constants/report.py
CHANGED
|
@@ -209,6 +209,7 @@ REQUIRED_SAMPLE_METADATA_NALLO_FIELDS: list[str] = _REQUIRED_SAMPLE_METADATA_FIE
|
|
|
209
209
|
_REQUIRED_SAMPLE_METADATA_BALSAMIC_FIELDS: list[str] = _REQUIRED_SAMPLE_METADATA_FIELDS + [
|
|
210
210
|
"mean_insert_size",
|
|
211
211
|
"fold_80",
|
|
212
|
+
"predicted_sex",
|
|
212
213
|
]
|
|
213
214
|
|
|
214
215
|
REQUIRED_SAMPLE_METADATA_BALSAMIC_TARGETED_FIELDS: list[str] = (
|
cg/constants/scout.py
CHANGED
|
@@ -12,14 +12,14 @@ class GenomeBuild(StrEnum):
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
class ScoutExportFileName(StrEnum):
|
|
15
|
-
MANAGED_VARIANTS
|
|
16
|
-
PANELS
|
|
17
|
-
PANELS_TSV
|
|
15
|
+
MANAGED_VARIANTS = f"managed_variants{FileExtensions.VCF}"
|
|
16
|
+
PANELS = f"gene_panels{FileExtensions.BED}"
|
|
17
|
+
PANELS_TSV = f"gene_panels{FileExtensions.TSV}"
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
class UploadTrack(StrEnum):
|
|
21
|
-
RARE_DISEASE
|
|
22
|
-
CANCER
|
|
21
|
+
RARE_DISEASE = "rare"
|
|
22
|
+
CANCER = "cancer"
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
class ScoutCustomCaseReportTags(StrEnum):
|
|
@@ -66,12 +66,12 @@ NALLO_CASE_TAGS = dict(
|
|
|
66
66
|
multiqc={"multiqc-html"},
|
|
67
67
|
peddy_check={"ped-check", "peddy"},
|
|
68
68
|
peddy_ped={"ped", "peddy"},
|
|
69
|
-
|
|
70
|
-
vcf_snv_research={"vcf-snv-research"},
|
|
69
|
+
somalier_samples={"somalier", "relate-samples"},
|
|
71
70
|
vcf_snv={"vcf-snv-clinical"},
|
|
72
|
-
|
|
73
|
-
vcf_sv={"vcf-sv-clinical"},
|
|
71
|
+
vcf_snv_research={"vcf-snv-research"},
|
|
74
72
|
vcf_str={"vcf-str"},
|
|
73
|
+
vcf_sv={"vcf-sv-clinical"},
|
|
74
|
+
vcf_sv_research={"vcf-sv-research"},
|
|
75
75
|
)
|
|
76
76
|
|
|
77
77
|
MIP_CASE_TAGS: dict[str, set[str]] = dict(
|
|
@@ -138,9 +138,12 @@ RAREDISEASE_SAMPLE_TAGS: dict[str, set[str]] = dict(
|
|
|
138
138
|
NALLO_SAMPLE_TAGS: dict[str, set[str]] = dict(
|
|
139
139
|
alignment_path={AlignmentFileTag.BAM, "haplotags"},
|
|
140
140
|
assembly_alignment_path={AlignmentFileTag.BAM, "assembly"},
|
|
141
|
+
chromograph_autozyg={"chromograph", "autozyg"},
|
|
142
|
+
chromograph_coverage={"chromograph", "tcov"},
|
|
141
143
|
d4_file={"coverage", "d4"},
|
|
142
144
|
hificnv_coverage={"hificnv", "bigwig"},
|
|
143
145
|
paraphase_alignment_path={AlignmentFileTag.BAM, NalloAnalysisTag.PARAPHASE},
|
|
146
|
+
phase_blocks={"whatshap", "gtf"},
|
|
144
147
|
reviewer_alignment={"repeats", "spanning", "bam"},
|
|
145
148
|
reviewer_alignment_index={"repeats", "spanning", "bam-index"},
|
|
146
149
|
reviewer_vcf={"repeats", "sorted", "vcf"},
|
cg/constants/sequencing.py
CHANGED
|
@@ -60,8 +60,8 @@ class RecordType(StrEnum):
|
|
|
60
60
|
|
|
61
61
|
|
|
62
62
|
class SequencingPlatform(StrEnum):
|
|
63
|
-
ILLUMINA
|
|
64
|
-
OXFORD_NANOPORE
|
|
63
|
+
ILLUMINA = "ILLUMINA"
|
|
64
|
+
OXFORD_NANOPORE = "OXFORD_NANOPORE"
|
|
65
65
|
|
|
66
66
|
|
|
67
67
|
class SeqLibraryPrepCategory(StrEnum):
|
cg/constants/tb.py
CHANGED
|
@@ -15,8 +15,8 @@ class AnalysisStatus:
|
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
class AnalysisType(StrEnum):
|
|
18
|
-
OTHER
|
|
19
|
-
TGS
|
|
20
|
-
WES
|
|
21
|
-
WGS
|
|
22
|
-
WTS
|
|
18
|
+
OTHER = "other"
|
|
19
|
+
TGS = SeqLibraryPrepCategory.TARGETED_GENOME_SEQUENCING
|
|
20
|
+
WES = SeqLibraryPrepCategory.WHOLE_EXOME_SEQUENCING
|
|
21
|
+
WGS = SeqLibraryPrepCategory.WHOLE_GENOME_SEQUENCING
|
|
22
|
+
WTS = SeqLibraryPrepCategory.WHOLE_TRANSCRIPTOME_SEQUENCING
|
cg/exc.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
2
|
+
Cg Exceptions.
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
|
|
@@ -36,6 +36,12 @@ class AnalysisNotReadyError(CgError):
|
|
|
36
36
|
"""
|
|
37
37
|
|
|
38
38
|
|
|
39
|
+
class AnalysisAlreadyCompletedError(CgError):
|
|
40
|
+
"""
|
|
41
|
+
Exception raised when the latest analysis is already completed.
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
|
|
39
45
|
class AnalysisNotCompletedError(CgError):
|
|
40
46
|
"""
|
|
41
47
|
Exception raised when an analysis has not completed.
|
|
@@ -144,6 +150,14 @@ class IlluminaRunAlreadyBackedUpError(CgError):
|
|
|
144
150
|
"""Raised when a flow cell is already backed-up."""
|
|
145
151
|
|
|
146
152
|
|
|
153
|
+
class PacbioSequencingRunAlreadyExistsError(CgError):
|
|
154
|
+
"""Raised when a PacBio sequencing run already exists."""
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
class PacbioSequencingRunNotFoundError(CgError):
|
|
158
|
+
"""Raised when a PacBio sequencing run is not found."""
|
|
159
|
+
|
|
160
|
+
|
|
147
161
|
class HousekeeperFileMissingError(CgError):
|
|
148
162
|
"""
|
|
149
163
|
Exception raised when a file is missing in Housekeeper.
|
|
@@ -210,10 +224,6 @@ class RunParametersError(CgError):
|
|
|
210
224
|
"""Raised when something is wrong with the run parameters file."""
|
|
211
225
|
|
|
212
226
|
|
|
213
|
-
class NfSampleSheetError(CgError):
|
|
214
|
-
"""Raised when something is wrong with the sample sheet."""
|
|
215
|
-
|
|
216
|
-
|
|
217
227
|
class SampleSheetContentError(CgError):
|
|
218
228
|
"""Raised when something is wrong with the sample sheet content."""
|
|
219
229
|
|
|
@@ -334,5 +344,17 @@ class Chanjo2ResponseError(Chanjo2APIClientError):
|
|
|
334
344
|
"""Exception raised when the response from Chanjo2 API client fails validation."""
|
|
335
345
|
|
|
336
346
|
|
|
347
|
+
class CaseNotConfiguredError(CgError):
|
|
348
|
+
"""Exception raised when a case is being run without a configuration."""
|
|
349
|
+
|
|
350
|
+
|
|
337
351
|
class MissingConfigFilesError(CgError):
|
|
338
352
|
"""Exception raised when a case is being run with missing configuration files."""
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
class SeqeraError(CgError):
|
|
356
|
+
"""Exception raised when receiving an unexpected response from Seqera platform"""
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
class ApplicationDoesNotHaveHiFiYieldError(CgError):
|
|
360
|
+
"""Exception raised when application does not have HiFi yield set."""
|
cg/meta/compress/compress.py
CHANGED
|
@@ -96,8 +96,13 @@ class CompressAPI:
|
|
|
96
96
|
def decompress_case(self, case: Case) -> None:
|
|
97
97
|
"""Decompresses all Spring files tied to the case.
|
|
98
98
|
Raises:
|
|
99
|
-
|
|
100
|
-
|
|
99
|
+
DecompressionCouldNotStartError if no sample could start decompressing.
|
|
100
|
+
"""
|
|
101
|
+
success = False
|
|
102
|
+
for sample in case.samples:
|
|
103
|
+
if self.decompress_spring(sample.internal_id):
|
|
104
|
+
success = True
|
|
105
|
+
if not success:
|
|
101
106
|
raise DecompressionCouldNotStartError(
|
|
102
107
|
f"No sample could be decompressed for {case.internal_id}"
|
|
103
108
|
)
|
|
@@ -89,6 +89,7 @@ class BalsamicDeliveryReportAPI(DeliveryReportAPI):
|
|
|
89
89
|
million_read_pairs=million_read_pairs,
|
|
90
90
|
pct_250x=sample_metrics.pct_target_bases_250x if sample_metrics else None,
|
|
91
91
|
pct_500x=sample_metrics.pct_target_bases_500x if sample_metrics else None,
|
|
92
|
+
predicted_sex=sample_metrics.compare_predicted_to_given_sex if sample_metrics else None,
|
|
92
93
|
)
|
|
93
94
|
|
|
94
95
|
@staticmethod
|
|
@@ -103,13 +104,14 @@ class BalsamicDeliveryReportAPI(DeliveryReportAPI):
|
|
|
103
104
|
fold_80=sample_metrics.fold_80_base_penalty if sample_metrics else None,
|
|
104
105
|
initial_qc=passed_initial_qc,
|
|
105
106
|
mean_insert_size=sample_metrics.mean_insert_size if sample_metrics else None,
|
|
106
|
-
median_coverage=sample_metrics.
|
|
107
|
+
median_coverage=sample_metrics.median_target_coverage if sample_metrics else None,
|
|
107
108
|
million_read_pairs=million_read_pairs,
|
|
108
109
|
pct_15x=sample_metrics.pct_15x if sample_metrics else None,
|
|
109
110
|
pct_60x=sample_metrics.pct_60x if sample_metrics else None,
|
|
110
111
|
pct_reads_improper_pairs=(
|
|
111
112
|
sample_metrics.pct_pf_reads_improper_pairs if sample_metrics else None
|
|
112
113
|
),
|
|
114
|
+
predicted_sex=sample_metrics.compare_predicted_to_given_sex if sample_metrics else None,
|
|
113
115
|
)
|
|
114
116
|
|
|
115
117
|
def is_report_accredited(
|
|
@@ -23,6 +23,7 @@ from cg.meta.delivery.delivery import DeliveryAPI
|
|
|
23
23
|
from cg.meta.delivery_report.data_validators import get_empty_report_data, get_missing_report_data
|
|
24
24
|
from cg.meta.workflow.analysis import AnalysisAPI
|
|
25
25
|
from cg.models.analysis import AnalysisModel
|
|
26
|
+
from cg.models.delivery.delivery import DeliveryFile
|
|
26
27
|
from cg.models.delivery_report.metadata import SampleMetadataModel
|
|
27
28
|
from cg.models.delivery_report.report import (
|
|
28
29
|
CaseModel,
|
|
@@ -250,14 +251,14 @@ class DeliveryReportAPI:
|
|
|
250
251
|
for case_sample in case_samples:
|
|
251
252
|
sample: Sample = case_sample.sample
|
|
252
253
|
lims_sample: dict[str, Any] = self.lims_api.sample(sample.internal_id)
|
|
253
|
-
delivered_files: list[
|
|
254
|
+
delivered_files: list[DeliveryFile] | None = (
|
|
254
255
|
self.delivery_api.get_analysis_sample_delivery_files_by_sample(
|
|
255
256
|
case=case, sample=sample
|
|
256
257
|
)
|
|
257
258
|
if self.delivery_api.is_analysis_delivery(case.data_delivery)
|
|
258
259
|
else None
|
|
259
260
|
)
|
|
260
|
-
delivered_fastq_files: list[
|
|
261
|
+
delivered_fastq_files: list[DeliveryFile] | None = (
|
|
261
262
|
self.delivery_api.get_fastq_delivery_files_by_sample(case=case, sample=sample)
|
|
262
263
|
if self.delivery_api.is_fastq_delivery(case.data_delivery)
|
|
263
264
|
else None
|
|
@@ -332,7 +333,7 @@ class DeliveryReportAPI:
|
|
|
332
333
|
|
|
333
334
|
def get_case_analysis_data(self, case: Case, analysis: Analysis) -> DataAnalysisModel:
|
|
334
335
|
"""Return workflow attributes used for data analysis."""
|
|
335
|
-
delivered_files: list[
|
|
336
|
+
delivered_files: list[DeliveryFile] | None = (
|
|
336
337
|
self.delivery_api.get_analysis_case_delivery_files(case)
|
|
337
338
|
if self.delivery_api.is_analysis_delivery(case.data_delivery)
|
|
338
339
|
else None
|
cg/meta/delivery_report/nallo.py
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"""Nallo Delivery Report API."""
|
|
2
2
|
|
|
3
|
+
from typing import cast
|
|
4
|
+
|
|
3
5
|
from cg.clients.chanjo2.models import CoverageMetrics
|
|
4
|
-
from cg.constants.housekeeper_tags import AnalysisTag
|
|
6
|
+
from cg.constants.housekeeper_tags import AnalysisTag
|
|
5
7
|
from cg.constants.report import (
|
|
6
8
|
REQUIRED_APPLICATION_FIELDS,
|
|
7
9
|
REQUIRED_CASE_FIELDS,
|
|
@@ -13,8 +15,6 @@ from cg.constants.report import (
|
|
|
13
15
|
REQUIRED_SAMPLE_NALLO_FIELDS,
|
|
14
16
|
REQUIRED_SAMPLE_TIMESTAMP_FIELDS,
|
|
15
17
|
)
|
|
16
|
-
from cg.constants.scout import ScoutUploadKey
|
|
17
|
-
from cg.constants.subject import PlinkSex
|
|
18
18
|
from cg.meta.delivery_report.data_validators import get_million_read_pairs
|
|
19
19
|
from cg.meta.delivery_report.delivery_report_api import DeliveryReportAPI
|
|
20
20
|
from cg.meta.workflow.nallo import NalloAnalysisAPI
|
|
@@ -36,7 +36,9 @@ class NalloDeliveryReportAPI(DeliveryReportAPI):
|
|
|
36
36
|
self, case: Case, sample: Sample, analysis_metadata: NextflowAnalysis
|
|
37
37
|
) -> NalloSampleMetadataModel:
|
|
38
38
|
"""Return Nallo sample metadata to include in the report."""
|
|
39
|
-
sample_metrics: NalloQCMetrics =
|
|
39
|
+
sample_metrics: NalloQCMetrics = cast(
|
|
40
|
+
NalloQCMetrics, analysis_metadata.sample_metrics[sample.internal_id]
|
|
41
|
+
)
|
|
40
42
|
gene_ids: list[int] = self.analysis_api.get_gene_ids_from_scout(case.panels)
|
|
41
43
|
coverage_metrics: CoverageMetrics | None = self.analysis_api.get_sample_coverage(
|
|
42
44
|
case_id=case.internal_id, sample_id=sample.internal_id, gene_ids=gene_ids
|
|
@@ -50,7 +52,7 @@ class NalloDeliveryReportAPI(DeliveryReportAPI):
|
|
|
50
52
|
median_coverage=sample_metrics.median_coverage,
|
|
51
53
|
million_read_pairs=get_million_read_pairs(sample.reads),
|
|
52
54
|
pct_10x=coverage_metrics.coverage_completeness_percent if coverage_metrics else None,
|
|
53
|
-
sex=sample_metrics.
|
|
55
|
+
sex=sample_metrics.predicted_sex,
|
|
54
56
|
)
|
|
55
57
|
|
|
56
58
|
def is_report_accredited(
|
|
@@ -65,23 +67,21 @@ class NalloDeliveryReportAPI(DeliveryReportAPI):
|
|
|
65
67
|
def get_scout_variants_files(self, case_id: str) -> ScoutVariantsFiles:
|
|
66
68
|
"""Return Nallo files that will be uploaded to Scout."""
|
|
67
69
|
return ScoutVariantsFiles(
|
|
68
|
-
snv_vcf=self.housekeeper_api.
|
|
70
|
+
snv_vcf=self.housekeeper_api.get_latest_file_strict(
|
|
69
71
|
bundle=case_id,
|
|
70
72
|
tags=[
|
|
71
73
|
AnalysisTag.VCF_SNV_CLINICAL,
|
|
72
74
|
],
|
|
73
75
|
).full_path,
|
|
74
|
-
sv_vcf=self.housekeeper_api.
|
|
76
|
+
sv_vcf=self.housekeeper_api.get_latest_file_strict(
|
|
75
77
|
bundle=case_id,
|
|
76
78
|
tags=[
|
|
77
79
|
AnalysisTag.VCF_SV_CLINICAL,
|
|
78
80
|
],
|
|
79
81
|
).full_path,
|
|
80
|
-
vcf_str=self.housekeeper_api.
|
|
82
|
+
vcf_str=self.housekeeper_api.get_latest_file_strict(
|
|
81
83
|
bundle=case_id,
|
|
82
|
-
tags=[
|
|
83
|
-
AnalysisTag.VCF_STR,
|
|
84
|
-
],
|
|
84
|
+
tags=[AnalysisTag.VCF_STR],
|
|
85
85
|
).full_path,
|
|
86
86
|
)
|
|
87
87
|
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
"""Raredisease Delivery Report API."""
|
|
2
2
|
|
|
3
|
+
from housekeeper.store.models import File
|
|
4
|
+
|
|
3
5
|
from cg.clients.chanjo2.models import CoverageMetrics
|
|
6
|
+
from cg.constants.housekeeper_tags import AnalysisTag
|
|
4
7
|
from cg.constants.report import (
|
|
5
8
|
REQUIRED_APPLICATION_FIELDS,
|
|
6
9
|
REQUIRED_CASE_FIELDS,
|
|
@@ -43,7 +46,7 @@ class RarediseaseDeliveryReportAPI(DeliveryReportAPI):
|
|
|
43
46
|
)
|
|
44
47
|
return RarediseaseSampleMetadataModel(
|
|
45
48
|
bait_set=self.lims_api.capture_kit(sample.internal_id),
|
|
46
|
-
duplicates=sample_metrics.
|
|
49
|
+
duplicates=round(sample_metrics.percent_duplication * 100, 2),
|
|
47
50
|
initial_qc=self.lims_api.has_sample_passed_initial_qc(sample.internal_id),
|
|
48
51
|
mapped_reads=round((sample_metrics.mapped_reads / sample_metrics.total_reads) * 100, 2),
|
|
49
52
|
mean_target_coverage=coverage_metrics.mean_coverage if coverage_metrics else None,
|
|
@@ -69,9 +72,10 @@ class RarediseaseDeliveryReportAPI(DeliveryReportAPI):
|
|
|
69
72
|
sv_vcf: str | None = self.get_scout_uploaded_file_from_hk(
|
|
70
73
|
case_id=case_id, scout_key=ScoutUploadKey.VCF_SV
|
|
71
74
|
)
|
|
72
|
-
|
|
73
|
-
|
|
75
|
+
vcf_str_file: File | None = self.housekeeper_api.get_latest_file(
|
|
76
|
+
bundle=case_id, tags=[AnalysisTag.VCF_STR, case_id]
|
|
74
77
|
)
|
|
78
|
+
vcf_str: str | None = vcf_str_file.full_path if vcf_str_file else None
|
|
75
79
|
smn_tsv: str | None = self.get_scout_uploaded_file_from_hk(
|
|
76
80
|
case_id=case_id, scout_key=ScoutUploadKey.SMN_TSV
|
|
77
81
|
)
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
{% macro balsamic_qc_metrics(samples, analysis_type) %}
|
|
4
4
|
{% set
|
|
5
5
|
metrics = [
|
|
6
|
+
{"name": "Kön", "key": "predicted_sex", "description": "Kön beräknat genom bioinformatisk analys."},
|
|
6
7
|
{"name": "Läspar [M]", "key": "million_read_pairs", "description": "Antal sekvenseringsläsningar i miljoner läspar."},
|
|
7
8
|
{"name": "Duplikat [%]", "key": "duplicates", "description": "Sekvenseringsläsningar som är i duplikat och därmed ej unika sekvenser. Hög mängd duplikat kan tyda på dålig komplexitet av sekvenserat bibliotek eller djup sekvensering."},
|
|
8
9
|
{"name": "Medelfragmentlängd [baspar]", "key": "mean_insert_size", "description": "Medelstorlek av provbiblioteken som laddats på sekvenseringsinstrument."},
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<div class="container">
|
|
3
3
|
<div class="alert alert-info" role="alert">
|
|
4
4
|
All kommunikation gällande ordern, såsom tillägg, avvikelser eller ev undantag i metoden från
|
|
5
|
-
Clinical Genomics, finns tillgänglig i
|
|
5
|
+
Clinical Genomics, finns tillgänglig i Freshdesk och är länkad till ärendets ticket:
|
|
6
6
|
<a class="alert-link">#{{ ticket_id }}</a>. En stängd ticket kan närsomhelst öppnas upp igen för frågor.
|
|
7
7
|
</div>
|
|
8
8
|
</div>
|
|
@@ -12,19 +12,27 @@ from cg.constants.observations import (
|
|
|
12
12
|
LOQUSDB_CANCER_SEQUENCING_METHODS,
|
|
13
13
|
LOQUSDB_ID,
|
|
14
14
|
BalsamicLoadParameters,
|
|
15
|
+
BalsamicObservationPanel,
|
|
15
16
|
BalsamicObservationsAnalysisTag,
|
|
16
17
|
LoqusdbInstance,
|
|
17
18
|
)
|
|
18
|
-
from cg.
|
|
19
|
+
from cg.constants.sequencing import SeqLibraryPrepCategory
|
|
20
|
+
from cg.exc import CaseNotFoundError, LoqusdbDeleteCaseError, LoqusdbDuplicateRecordError
|
|
19
21
|
from cg.meta.observations.observations_api import ObservationsAPI
|
|
20
22
|
from cg.meta.workflow.balsamic import BalsamicAnalysisAPI
|
|
21
23
|
from cg.models.cg_config import CGConfig
|
|
22
24
|
from cg.models.observations.input_files import BalsamicObservationsInputFiles
|
|
23
|
-
from cg.store.models import Case
|
|
25
|
+
from cg.store.models import BedVersion, Case, Sample
|
|
24
26
|
from cg.utils.dict import get_full_path_dictionary
|
|
25
27
|
|
|
26
28
|
LOG = logging.getLogger(__name__)
|
|
27
29
|
|
|
30
|
+
PANEL_TO_LOQUSDB_INSTANCE_MAP: dict = {
|
|
31
|
+
BalsamicObservationPanel.LYMPHOID: LoqusdbInstance.SOMATIC_LYMPHOID,
|
|
32
|
+
BalsamicObservationPanel.MYELOID: LoqusdbInstance.SOMATIC_MYELOID,
|
|
33
|
+
BalsamicObservationPanel.EXOME: LoqusdbInstance.SOMATIC_EXOME,
|
|
34
|
+
}
|
|
35
|
+
|
|
28
36
|
|
|
29
37
|
class BalsamicObservationsAPI(ObservationsAPI):
|
|
30
38
|
"""API to manage Balsamic observations."""
|
|
@@ -32,6 +40,7 @@ class BalsamicObservationsAPI(ObservationsAPI):
|
|
|
32
40
|
def __init__(self, config: CGConfig):
|
|
33
41
|
self.analysis_api = BalsamicAnalysisAPI(config)
|
|
34
42
|
super().__init__(config=config, analysis_api=self.analysis_api)
|
|
43
|
+
self.lims_api = config.lims_api
|
|
35
44
|
self.loqusdb_somatic_api: LoqusdbAPI = self.get_loqusdb_api(LoqusdbInstance.SOMATIC)
|
|
36
45
|
self.loqusdb_tumor_api: LoqusdbAPI = self.get_loqusdb_api(LoqusdbInstance.TUMOR)
|
|
37
46
|
|
|
@@ -45,43 +54,122 @@ class BalsamicObservationsAPI(ObservationsAPI):
|
|
|
45
54
|
"""Return sequencing methods that are eligible for cancer Loqusdb uploads."""
|
|
46
55
|
return LOQUSDB_CANCER_SEQUENCING_METHODS
|
|
47
56
|
|
|
48
|
-
def is_analysis_type_eligible_for_observations_upload(self,
|
|
57
|
+
def is_analysis_type_eligible_for_observations_upload(self, case: Case) -> bool:
|
|
49
58
|
"""Return whether the cancer analysis type is eligible for cancer Loqusdb uploads."""
|
|
50
|
-
if self.analysis_api.is_analysis_normal_only(
|
|
51
|
-
LOG.error(
|
|
59
|
+
if self.analysis_api.is_analysis_normal_only(case.internal_id):
|
|
60
|
+
LOG.error(
|
|
61
|
+
f"Normal only analysis {case.internal_id} is not supported for Loqusdb uploads"
|
|
62
|
+
)
|
|
63
|
+
return False
|
|
64
|
+
prep_category: str = case.samples[0].prep_category
|
|
65
|
+
if prep_category in [
|
|
66
|
+
SeqLibraryPrepCategory.TARGETED_GENOME_SEQUENCING,
|
|
67
|
+
SeqLibraryPrepCategory.WHOLE_EXOME_SEQUENCING,
|
|
68
|
+
] and self._is_paired_analysis(case):
|
|
69
|
+
LOG.error(
|
|
70
|
+
f"Paired analysis {case.internal_id} is not supported for TGS Loqusdb uploads"
|
|
71
|
+
)
|
|
52
72
|
return False
|
|
53
73
|
return True
|
|
54
74
|
|
|
75
|
+
@staticmethod
|
|
76
|
+
def _is_paired_analysis(case: Case) -> bool:
|
|
77
|
+
return len(case.samples) > 1
|
|
78
|
+
|
|
55
79
|
def is_case_eligible_for_observations_upload(self, case: Case) -> bool:
|
|
56
80
|
"""Return whether a cancer case is eligible for observations upload."""
|
|
57
81
|
return all(
|
|
58
82
|
[
|
|
59
83
|
self.is_customer_eligible_for_observations_upload(case.customer.internal_id),
|
|
60
|
-
self.
|
|
61
|
-
self.
|
|
62
|
-
self.
|
|
84
|
+
self.is_analysis_type_eligible_for_observations_upload(case),
|
|
85
|
+
self.is_sample_source_type_not_ffpe(case.internal_id),
|
|
86
|
+
self.is_panel_allowed_for_observations_upload(case),
|
|
63
87
|
]
|
|
64
88
|
)
|
|
65
89
|
|
|
90
|
+
def is_panel_allowed_for_observations_upload(self, case: Case) -> bool:
|
|
91
|
+
"""
|
|
92
|
+
True if WGS.
|
|
93
|
+
If TGS/WES, True for panels with LoqusDB instances.
|
|
94
|
+
This assumes that all samples in the case have the same prep-category.
|
|
95
|
+
"""
|
|
96
|
+
if self._is_panel_upload(case):
|
|
97
|
+
sample: Sample = case.samples[0]
|
|
98
|
+
panel_short_name: str | None = self.lims_api.capture_kit(sample.internal_id)
|
|
99
|
+
bed_version: BedVersion | None = self.store.get_bed_version_by_short_name(
|
|
100
|
+
panel_short_name
|
|
101
|
+
)
|
|
102
|
+
if not bed_version:
|
|
103
|
+
LOG.warning(
|
|
104
|
+
f"No bed version found for LIMS panel {panel_short_name} for sample "
|
|
105
|
+
f"{sample.internal_id} in case {case.internal_id}"
|
|
106
|
+
)
|
|
107
|
+
return False
|
|
108
|
+
if bed_version.bed.name not in list(BalsamicObservationPanel):
|
|
109
|
+
return False
|
|
110
|
+
return True
|
|
111
|
+
|
|
66
112
|
def load_observations(self, case: Case) -> None:
|
|
67
113
|
"""
|
|
68
|
-
|
|
114
|
+
Upload observation counts to Loqusdb for a Balsamic case.
|
|
69
115
|
|
|
70
116
|
Raises:
|
|
71
117
|
LoqusdbDuplicateRecordError: If case has already been uploaded.
|
|
72
118
|
"""
|
|
73
|
-
|
|
119
|
+
if self._is_panel_upload(case):
|
|
120
|
+
self._upload_panel_case(case)
|
|
121
|
+
else:
|
|
122
|
+
self._upload_wgs_case(case)
|
|
123
|
+
|
|
124
|
+
@staticmethod
|
|
125
|
+
def _is_panel_upload(case: Case) -> bool:
|
|
126
|
+
sample: Sample = case.samples[0]
|
|
127
|
+
return sample.prep_category in [
|
|
128
|
+
SeqLibraryPrepCategory.TARGETED_GENOME_SEQUENCING,
|
|
129
|
+
SeqLibraryPrepCategory.WHOLE_EXOME_SEQUENCING,
|
|
130
|
+
]
|
|
131
|
+
|
|
132
|
+
def _upload_panel_case(self, case: Case) -> None:
|
|
133
|
+
"""
|
|
134
|
+
Uploads the case to one of the somatic panel LoqusDB instances. The case is known to have
|
|
135
|
+
a panel with a known LoqusDB instance.
|
|
136
|
+
"""
|
|
137
|
+
loqusdb_api: LoqusdbAPI = self._get_panel_loqusdb_api(case)
|
|
138
|
+
if self.is_duplicate(case=case, loqusdb_api=loqusdb_api):
|
|
139
|
+
LOG.error(f"Case {case.internal_id} has already been uploaded to Loqusdb")
|
|
140
|
+
raise LoqusdbDuplicateRecordError
|
|
141
|
+
input_files: BalsamicObservationsInputFiles = self.get_observations_input_files(case)
|
|
142
|
+
loqusdb_api.load(
|
|
143
|
+
case_id=case.internal_id,
|
|
144
|
+
snv_vcf_path=input_files.snv_vcf_path,
|
|
145
|
+
)
|
|
146
|
+
loqusdb_id: str = str(loqusdb_api.get_case(case_id=case.internal_id)[LOQUSDB_ID])
|
|
147
|
+
self.update_statusdb_loqusdb_id(samples=case.samples, loqusdb_id=loqusdb_id)
|
|
148
|
+
|
|
149
|
+
def _get_panel_loqusdb_api(self, case: Case) -> LoqusdbAPI:
|
|
150
|
+
sample: Sample = case.samples[0]
|
|
151
|
+
bed_short_name: str = self.lims_api.capture_kit(sample.internal_id)
|
|
152
|
+
bed_version: BedVersion = self.store.get_bed_version_by_short_name_strict(bed_short_name)
|
|
153
|
+
panel: str = bed_version.bed.name
|
|
154
|
+
loqusdb_instance: LoqusdbInstance = PANEL_TO_LOQUSDB_INSTANCE_MAP[
|
|
155
|
+
BalsamicObservationPanel(panel)
|
|
156
|
+
]
|
|
157
|
+
return self.get_loqusdb_api(loqusdb_instance)
|
|
158
|
+
|
|
159
|
+
def _upload_wgs_case(self, case: Case) -> None:
|
|
160
|
+
loqusdb_upload_apis: list[LoqusdbAPI] = [
|
|
161
|
+
self.loqusdb_somatic_api,
|
|
162
|
+
self.loqusdb_tumor_api,
|
|
163
|
+
]
|
|
74
164
|
for loqusdb_api in loqusdb_upload_apis:
|
|
75
165
|
if self.is_duplicate(case=case, loqusdb_api=loqusdb_api):
|
|
76
166
|
LOG.error(f"Case {case.internal_id} has already been uploaded to Loqusdb")
|
|
77
167
|
raise LoqusdbDuplicateRecordError
|
|
78
|
-
|
|
79
168
|
input_files: BalsamicObservationsInputFiles = self.get_observations_input_files(case)
|
|
80
169
|
for loqusdb_api in loqusdb_upload_apis:
|
|
81
170
|
self.load_cancer_observations(
|
|
82
171
|
case=case, input_files=input_files, loqusdb_api=loqusdb_api
|
|
83
172
|
)
|
|
84
|
-
|
|
85
173
|
# Update Statusdb with a germline Loqusdb ID
|
|
86
174
|
loqusdb_id: str = str(self.loqusdb_tumor_api.get_case(case_id=case.internal_id)[LOQUSDB_ID])
|
|
87
175
|
self.update_statusdb_loqusdb_id(samples=case.samples, loqusdb_id=loqusdb_id)
|
|
@@ -137,12 +225,12 @@ class BalsamicObservationsAPI(ObservationsAPI):
|
|
|
137
225
|
version=hk_version.id, tags=[BalsamicObservationsAnalysisTag.SV_VCF]
|
|
138
226
|
).first(),
|
|
139
227
|
}
|
|
140
|
-
return BalsamicObservationsInputFiles(
|
|
228
|
+
return BalsamicObservationsInputFiles.model_validate(get_full_path_dictionary(input_files))
|
|
141
229
|
|
|
142
230
|
def delete_case(self, case_id: str) -> None:
|
|
143
231
|
"""Delete cancer case observations from Loqusdb."""
|
|
144
232
|
case: Case = self.store.get_case_by_internal_id(internal_id=case_id)
|
|
145
|
-
loqusdb_apis: list[LoqusdbAPI] =
|
|
233
|
+
loqusdb_apis: list[LoqusdbAPI] = self._get_relevant_loqusdb_apis(case)
|
|
146
234
|
for loqusdb_api in loqusdb_apis:
|
|
147
235
|
if not loqusdb_api.get_case(case_id):
|
|
148
236
|
LOG.error(f"Case {case_id} could not be found in Loqusdb. Skipping case deletion.")
|
|
@@ -151,3 +239,11 @@ class BalsamicObservationsAPI(ObservationsAPI):
|
|
|
151
239
|
loqusdb_api.delete_case(case_id)
|
|
152
240
|
self.update_statusdb_loqusdb_id(samples=case.samples, loqusdb_id=None)
|
|
153
241
|
LOG.info(f"Removed observations for case {case_id} from Loqusdb")
|
|
242
|
+
|
|
243
|
+
def _get_relevant_loqusdb_apis(self, case: Case) -> list[LoqusdbAPI]:
|
|
244
|
+
if not self._is_panel_upload(case):
|
|
245
|
+
return [self.loqusdb_somatic_api, self.loqusdb_tumor_api]
|
|
246
|
+
try:
|
|
247
|
+
return [self._get_panel_loqusdb_api(case)]
|
|
248
|
+
except ValueError as error:
|
|
249
|
+
raise LoqusdbDeleteCaseError from error
|
|
@@ -58,7 +58,7 @@ class MipDNAObservationsAPI(ObservationsAPI):
|
|
|
58
58
|
self.is_customer_eligible_for_observations_upload(case.customer.internal_id),
|
|
59
59
|
self.is_sequencing_method_eligible_for_observations_upload(case.internal_id),
|
|
60
60
|
self.is_sample_type_eligible_for_observations_upload(case),
|
|
61
|
-
self.
|
|
61
|
+
self.is_sample_source_type_not_ffpe(case.internal_id),
|
|
62
62
|
]
|
|
63
63
|
)
|
|
64
64
|
|
|
@@ -59,7 +59,7 @@ class NalloObservationsAPI(ObservationsAPI):
|
|
|
59
59
|
self.is_customer_eligible_for_observations_upload(case.customer.internal_id),
|
|
60
60
|
self.is_sequencing_method_eligible_for_observations_upload(case.internal_id),
|
|
61
61
|
self.is_sample_type_eligible_for_observations_upload(case),
|
|
62
|
-
self.
|
|
62
|
+
self.is_sample_source_type_not_ffpe(case.internal_id),
|
|
63
63
|
]
|
|
64
64
|
)
|
|
65
65
|
|