scout-browser 4.97.0__py3-none-any.whl → 4.99.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.
- scout/adapter/mongo/institute.py +42 -55
- scout/adapter/mongo/variant.py +19 -15
- scout/adapter/mongo/variant_loader.py +11 -11
- scout/build/individual.py +2 -0
- scout/build/variant/variant.py +8 -0
- scout/commands/download/ensembl.py +18 -2
- scout/commands/update/individual.py +2 -0
- scout/commands/update/panelapp.py +15 -2
- scout/constants/__init__.py +6 -7
- scout/constants/clnsig.py +2 -0
- scout/constants/file_types.py +12 -0
- scout/constants/igv_tracks.py +8 -6
- scout/constants/panels.py +3 -0
- scout/constants/variant_tags.py +6 -6
- scout/demo/643594.config.yaml +1 -0
- scout/load/panelapp.py +11 -5
- scout/models/case/case_loading_models.py +4 -0
- scout/parse/variant/clnsig.py +38 -0
- scout/parse/variant/genotype.py +4 -10
- scout/parse/variant/models.py +5 -11
- scout/parse/variant/rank_score.py +5 -13
- scout/parse/variant/variant.py +90 -111
- scout/server/app.py +33 -22
- scout/server/blueprints/alignviewers/controllers.py +29 -10
- scout/server/blueprints/alignviewers/templates/alignviewers/igv_viewer.html +41 -11
- scout/server/blueprints/cases/templates/cases/case.html +1 -1
- scout/server/blueprints/cases/templates/cases/utils.html +6 -6
- scout/server/blueprints/clinvar/controllers.py +29 -14
- scout/server/blueprints/clinvar/templates/clinvar/multistep_add_variant.html +13 -4
- scout/server/blueprints/clinvar/views.py +14 -2
- scout/server/blueprints/institutes/controllers.py +10 -2
- scout/server/blueprints/institutes/templates/overview/filters.html +14 -1
- scout/server/blueprints/login/controllers.py +112 -12
- scout/server/blueprints/login/views.py +38 -60
- scout/server/blueprints/public/templates/public/index.html +5 -1
- scout/server/blueprints/variant/controllers.py +1 -1
- scout/server/blueprints/variant/templates/variant/acmg.html +6 -2
- scout/server/blueprints/variant/templates/variant/components.html +19 -0
- scout/server/blueprints/variant/templates/variant/utils.html +3 -3
- scout/server/blueprints/variants/controllers.py +10 -1
- scout/server/blueprints/variants/templates/variants/components.html +28 -0
- scout/server/blueprints/variants/templates/variants/mei-variants.html +8 -6
- scout/server/blueprints/variants/templates/variants/sv-variants.html +9 -7
- scout/server/blueprints/variants/templates/variants/utils.html +8 -12
- scout/server/blueprints/variants/templates/variants/variants.html +4 -25
- scout/server/config.py +8 -0
- scout/server/utils.py +22 -5
- scout/utils/acmg.py +25 -26
- scout/utils/ensembl_biomart_clients.py +1 -1
- scout/utils/ensembl_rest_clients.py +25 -32
- scout/utils/hgvs.py +1 -1
- {scout_browser-4.97.0.dist-info → scout_browser-4.99.0.dist-info}/METADATA +10 -14
- {scout_browser-4.97.0.dist-info → scout_browser-4.99.0.dist-info}/RECORD +56 -56
- {scout_browser-4.97.0.dist-info → scout_browser-4.99.0.dist-info}/WHEEL +0 -0
- {scout_browser-4.97.0.dist-info → scout_browser-4.99.0.dist-info}/entry_points.txt +0 -0
- {scout_browser-4.97.0.dist-info → scout_browser-4.99.0.dist-info}/licenses/LICENSE +0 -0
scout/parse/variant/genotype.py
CHANGED
@@ -27,16 +27,10 @@ GENOTYPE_MAP = {0: "0", 1: "1", -1: "."}
|
|
27
27
|
LOG = logging.getLogger(__name__)
|
28
28
|
|
29
29
|
|
30
|
-
def parse_genotypes(
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
variant(cyvcf2.Variant)
|
35
|
-
individuals: List[dict]
|
36
|
-
individual_positions(dict)
|
37
|
-
Returns:
|
38
|
-
genotypes(list(dict)): A list of genotypes
|
39
|
-
"""
|
30
|
+
def parse_genotypes(
|
31
|
+
variant: cyvcf2.Variant, individuals: List[Dict], individual_positions: Dict
|
32
|
+
) -> List[Dict]:
|
33
|
+
"""Parse the genotype calls for a variant"""
|
40
34
|
genotypes = []
|
41
35
|
for ind in individuals:
|
42
36
|
pos = individual_positions[ind["individual_id"]]
|
scout/parse/variant/models.py
CHANGED
@@ -1,19 +1,13 @@
|
|
1
|
-
def parse_genetic_models(models_info, case_id):
|
1
|
+
def parse_genetic_models(models_info: str, case_id: str) -> list:
|
2
2
|
"""Parse the genetic models entry of a vcf
|
3
3
|
|
4
|
-
|
5
|
-
models_info(str): The raw vcf information
|
6
|
-
case_id(str)
|
7
|
-
|
8
|
-
Returns:
|
9
|
-
genetic_models(list)
|
10
|
-
|
4
|
+
Return inheritance patterns.
|
11
5
|
"""
|
12
6
|
genetic_models = []
|
13
7
|
if models_info:
|
14
8
|
for family_info in models_info.split(","):
|
15
|
-
|
16
|
-
if
|
17
|
-
genetic_models =
|
9
|
+
split_info = family_info.split(":")
|
10
|
+
if split_info[0] == case_id:
|
11
|
+
genetic_models = split_info[1].split("|")
|
18
12
|
|
19
13
|
return genetic_models
|
@@ -1,17 +1,9 @@
|
|
1
|
-
def parse_rank_score(rank_score_entry, case_id):
|
2
|
-
"""Parse the rank score
|
3
|
-
|
4
|
-
Args:
|
5
|
-
rank_score_entry(str): The raw rank score entry
|
6
|
-
case_id(str)
|
7
|
-
|
8
|
-
Returns:
|
9
|
-
rank_score(float)
|
10
|
-
"""
|
1
|
+
def parse_rank_score(rank_score_entry: str, case_id: str) -> float:
|
2
|
+
"""Parse the rank score from the raw rank score entry"""
|
11
3
|
rank_score = None
|
12
4
|
if rank_score_entry:
|
13
5
|
for family_info in rank_score_entry.split(","):
|
14
|
-
|
15
|
-
if case_id ==
|
16
|
-
rank_score = float(
|
6
|
+
split_info = family_info.split(":")
|
7
|
+
if case_id == split_info[0]:
|
8
|
+
rank_score = float(split_info[1])
|
17
9
|
return rank_score
|
scout/parse/variant/variant.py
CHANGED
@@ -3,13 +3,13 @@ from typing import Any, Dict, List, Optional
|
|
3
3
|
|
4
4
|
from cyvcf2 import Variant
|
5
5
|
|
6
|
-
from scout.constants import CHR_PATTERN
|
6
|
+
from scout.constants import CHR_PATTERN, DNA_SAMPLE_VARIANT_CATEGORIES
|
7
7
|
from scout.exceptions import VcfError
|
8
8
|
from scout.utils.convert import call_safe
|
9
9
|
from scout.utils.dict_utils import remove_nonetype
|
10
10
|
|
11
11
|
from .callers import parse_callers
|
12
|
-
from .clnsig import parse_clnsig
|
12
|
+
from .clnsig import parse_clnsig, parse_clnsig_onc
|
13
13
|
from .compound import parse_compounds
|
14
14
|
from .conservation import parse_conservations
|
15
15
|
from .coordinates import parse_coordinates
|
@@ -57,15 +57,39 @@ def parse_variant(
|
|
57
57
|
# Vep information
|
58
58
|
vep_header = vep_header or []
|
59
59
|
|
60
|
-
parsed_variant = {}
|
61
60
|
# Create the ID for the variant
|
62
61
|
case_id = case["_id"]
|
62
|
+
|
63
63
|
genmod_key = get_genmod_key(case)
|
64
64
|
chrom_match = CHR_PATTERN.match(variant.CHROM)
|
65
65
|
chrom = chrom_match.group(2)
|
66
66
|
|
67
|
+
### We always assume split and normalized vcfs!!!
|
68
|
+
if len(variant.ALT) > 1:
|
69
|
+
raise VcfError("Variants are only allowed to have one alternative")
|
70
|
+
|
67
71
|
# Builds a dictionary with the different ids that are used
|
68
|
-
|
72
|
+
parsed_variant = {
|
73
|
+
"case_id": case_id,
|
74
|
+
"variant_type": variant_type,
|
75
|
+
"reference": variant.REF,
|
76
|
+
"quality": variant.QUAL, # cyvcf2 will set QUAL to None if '.' in vcf
|
77
|
+
"filters": get_filters(variant),
|
78
|
+
"chromosome": chrom,
|
79
|
+
"compounds": parse_compounds(
|
80
|
+
compound_info=variant.INFO.get("Compounds"),
|
81
|
+
case_id=genmod_key,
|
82
|
+
variant_type=variant_type,
|
83
|
+
),
|
84
|
+
"rank_score": parse_rank_score(variant.INFO.get("RankScore", ""), genmod_key) or 0,
|
85
|
+
"genetic_models": parse_genetic_models(variant.INFO.get("GeneticModels"), genmod_key),
|
86
|
+
"str_swegen_mean": call_safe(float, variant.INFO.get("SweGenMean")),
|
87
|
+
"str_swegen_std": call_safe(float, variant.INFO.get("SweGenStd")),
|
88
|
+
"somatic_score": call_safe(int, variant.INFO.get("SOMATICSCORE")),
|
89
|
+
"custom": parse_custom_data(variant.INFO.get("SCOUT_CUSTOM")),
|
90
|
+
}
|
91
|
+
category = get_and_set_category(parsed_variant, variant, category)
|
92
|
+
alt = get_and_set_variant_alternative(parsed_variant, variant, category)
|
69
93
|
|
70
94
|
parsed_variant["ids"] = parse_ids(
|
71
95
|
chrom=chrom,
|
@@ -75,71 +99,17 @@ def parse_variant(
|
|
75
99
|
case_id=case_id,
|
76
100
|
variant_type=variant_type,
|
77
101
|
)
|
78
|
-
parsed_variant["case_id"] = case_id
|
79
|
-
# type can be 'clinical' or 'research'
|
80
|
-
parsed_variant["variant_type"] = variant_type
|
81
102
|
|
82
|
-
|
83
|
-
parsed_variant["category"] = category
|
103
|
+
set_coordinates(parsed_variant, variant, case, category)
|
84
104
|
|
85
|
-
|
86
|
-
parsed_variant["reference"] = variant.REF
|
105
|
+
parsed_variant["samples"] = get_samples(variant, individual_positions, case, category)
|
87
106
|
|
88
|
-
### We allways assume splitted and normalized vcfs!!!
|
89
|
-
if len(variant.ALT) > 1:
|
90
|
-
raise VcfError("Variants are only allowed to have one alternative")
|
91
|
-
parsed_variant["alternative"] = alt
|
92
|
-
|
93
|
-
# cyvcf2 will set QUAL to None if '.' in vcf
|
94
|
-
parsed_variant["quality"] = variant.QUAL
|
95
|
-
|
96
|
-
parsed_variant["filters"] = get_filters(variant)
|
97
|
-
|
98
|
-
# Add the dbsnp ids
|
99
107
|
set_dbsnp_id(parsed_variant, variant.ID)
|
100
108
|
|
101
109
|
# This is the id of other position in translocations
|
102
110
|
# (only for specific svs)
|
103
111
|
parsed_variant["mate_id"] = None
|
104
112
|
|
105
|
-
################# Position specific #################
|
106
|
-
parsed_variant["chromosome"] = chrom
|
107
|
-
|
108
|
-
coordinates = parse_coordinates(variant, category, case.get("genome_build"))
|
109
|
-
|
110
|
-
parsed_variant["cytoband_end"] = coordinates["cytoband_end"]
|
111
|
-
parsed_variant["cytoband_start"] = coordinates["cytoband_start"]
|
112
|
-
parsed_variant["end"] = coordinates["end"]
|
113
|
-
parsed_variant["end_chrom"] = coordinates["end_chrom"]
|
114
|
-
parsed_variant["length"] = coordinates["length"]
|
115
|
-
parsed_variant["mate_id"] = coordinates["mate_id"]
|
116
|
-
parsed_variant["position"] = coordinates["position"]
|
117
|
-
parsed_variant["sub_category"] = coordinates["sub_category"]
|
118
|
-
|
119
|
-
################# Add rank score #################
|
120
|
-
# The rank score is central for displaying variants in scout.
|
121
|
-
# Use RankScore for somatic variations also
|
122
|
-
|
123
|
-
rank_score = parse_rank_score(variant.INFO.get("RankScore", ""), genmod_key)
|
124
|
-
parsed_variant["rank_score"] = rank_score or 0
|
125
|
-
|
126
|
-
################# Add gt calls #################
|
127
|
-
parsed_variant["samples"] = get_samples(variant, individual_positions, case)
|
128
|
-
|
129
|
-
################# Add compound information #################
|
130
|
-
compounds = parse_compounds(
|
131
|
-
compound_info=variant.INFO.get("Compounds"),
|
132
|
-
case_id=genmod_key,
|
133
|
-
variant_type=variant_type,
|
134
|
-
)
|
135
|
-
|
136
|
-
parsed_variant["compounds"] = compounds
|
137
|
-
|
138
|
-
################# Add inheritance patterns #################
|
139
|
-
parsed_variant["genetic_models"] = parse_genetic_models(
|
140
|
-
variant.INFO.get("GeneticModels"), genmod_key
|
141
|
-
)
|
142
|
-
|
143
113
|
################# Add autozygosity calls if present #################
|
144
114
|
parsed_variant["azlength"] = call_safe(int, variant.INFO.get("AZLENGTH"))
|
145
115
|
parsed_variant["azqual"] = call_safe(float, variant.INFO.get("AZQUAL"))
|
@@ -148,8 +118,6 @@ def parse_variant(
|
|
148
118
|
set_str_info(variant, parsed_variant)
|
149
119
|
# STR source dict with display string, source type and entry id
|
150
120
|
set_str_source(parsed_variant, variant)
|
151
|
-
parsed_variant["str_swegen_mean"] = call_safe(float, variant.INFO.get("SweGenMean"))
|
152
|
-
parsed_variant["str_swegen_std"] = call_safe(float, variant.INFO.get("SweGenStd"))
|
153
121
|
|
154
122
|
# MEI variant info
|
155
123
|
set_mei_info(variant, parsed_variant)
|
@@ -157,18 +125,12 @@ def parse_variant(
|
|
157
125
|
# Add Fusion info
|
158
126
|
set_fusion_info(variant, parsed_variant)
|
159
127
|
|
160
|
-
################# Add somatic info ##################
|
161
|
-
parsed_variant["somatic_score"] = call_safe(int, variant.INFO.get("SOMATICSCORE"))
|
162
|
-
|
163
128
|
################# Add mitomap info, from HmtNote #################
|
164
129
|
set_mitomap_associated_diseases(parsed_variant, variant)
|
165
130
|
|
166
131
|
################# Add HmtVar variant id, from HmtNote #################
|
167
132
|
add_hmtvar(parsed_variant, variant)
|
168
133
|
|
169
|
-
### Add custom info
|
170
|
-
parsed_variant["custom"] = parse_custom_data(variant.INFO.get("SCOUT_CUSTOM"))
|
171
|
-
|
172
134
|
### Add gene and transcript information
|
173
135
|
if parsed_variant.get("category") == "fusion":
|
174
136
|
parsed_transcripts = add_gene_and_transcript_info_for_fusions(parsed_variant)
|
@@ -204,6 +166,25 @@ def parse_variant(
|
|
204
166
|
return parsed_variant
|
205
167
|
|
206
168
|
|
169
|
+
def set_coordinates(parsed_variant: dict, variant: dict, case: dict, category: str):
|
170
|
+
"""
|
171
|
+
Parse and set coordinate annotations
|
172
|
+
"""
|
173
|
+
coordinates = parse_coordinates(variant, category, case.get("genome_build"))
|
174
|
+
parsed_variant.update(
|
175
|
+
{
|
176
|
+
"cytoband_end": coordinates["cytoband_end"],
|
177
|
+
"cytoband_start": coordinates["cytoband_start"],
|
178
|
+
"end": coordinates["end"],
|
179
|
+
"end_chrom": coordinates["end_chrom"],
|
180
|
+
"length": coordinates["length"],
|
181
|
+
"mate_id": coordinates["mate_id"],
|
182
|
+
"position": coordinates["position"],
|
183
|
+
"sub_category": coordinates["sub_category"],
|
184
|
+
}
|
185
|
+
)
|
186
|
+
|
187
|
+
|
207
188
|
def set_mei_specific_annotations(parsed_variant: dict, variant: dict):
|
208
189
|
"""Add MEI specific annotations"""
|
209
190
|
if parsed_variant.get("category") in ["mei"]:
|
@@ -315,20 +296,15 @@ def get_genmod_key(case):
|
|
315
296
|
return case["_id"]
|
316
297
|
|
317
298
|
|
318
|
-
def
|
319
|
-
"""Get variant's ALT
|
320
|
-
|
321
|
-
Args:
|
322
|
-
variant(cyvcf2.Variant)
|
323
|
-
category(str)
|
324
|
-
Return:
|
325
|
-
alternative variant: Str
|
326
|
-
"""
|
299
|
+
def get_and_set_variant_alternative(parsed_variant: dict, variant: Variant, category: str) -> str:
|
300
|
+
"""Get and set variant's ALT as alternative"""
|
327
301
|
|
328
302
|
if variant.ALT:
|
329
|
-
|
303
|
+
alt = variant.ALT[0]
|
330
304
|
elif not variant.ALT and category == "str":
|
331
|
-
|
305
|
+
alt = "."
|
306
|
+
parsed_variant["alternative"] = alt
|
307
|
+
return alt
|
332
308
|
|
333
309
|
|
334
310
|
def set_mei_info(variant: Variant, parsed_variant: Dict[str, Any]):
|
@@ -378,42 +354,46 @@ def get_filters(variant):
|
|
378
354
|
return ["PASS"]
|
379
355
|
|
380
356
|
|
381
|
-
def get_samples(variant, individual_positions, case):
|
357
|
+
def get_samples(variant: Variant, individual_positions: dict, case: dict, category: str) -> List:
|
382
358
|
"""Get samples
|
383
359
|
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
Return:
|
389
|
-
variant filter
|
360
|
+
Add GT calls to individuals.
|
361
|
+
|
362
|
+
Do not add individuals if they are not wanted based on the analysis type,
|
363
|
+
eg a WTS only individual for a DNA SNV variant.
|
390
364
|
"""
|
391
|
-
|
392
|
-
|
365
|
+
invalid_sample_types: List[str] = []
|
366
|
+
if category in DNA_SAMPLE_VARIANT_CATEGORIES:
|
367
|
+
invalid_sample_types = ["wts"]
|
368
|
+
|
369
|
+
if individual_positions and bool(case["individuals"]):
|
370
|
+
individuals = [
|
371
|
+
ind
|
372
|
+
for ind in case["individuals"]
|
373
|
+
if ind.get("analysis_type") not in invalid_sample_types
|
374
|
+
]
|
375
|
+
return parse_genotypes(variant, individuals, individual_positions)
|
393
376
|
return []
|
394
377
|
|
395
378
|
|
396
|
-
def
|
397
|
-
"""
|
398
|
-
|
399
|
-
|
400
|
-
variant(cyvcf2.Variant)
|
401
|
-
parsed_variant(dict)
|
402
|
-
Return:
|
403
|
-
category(str)
|
379
|
+
def get_and_set_category(parsed_variant: dict, variant: Variant, category: str) -> str:
|
380
|
+
"""Set category of variant. Convenience return of category.
|
381
|
+
|
382
|
+
If category not set, use variant type, but convert types SNP or INDEL (or old MNP) to "snv".
|
404
383
|
"""
|
405
384
|
if category:
|
385
|
+
parsed_variant["category"] = category
|
406
386
|
return category
|
407
387
|
|
408
|
-
|
409
|
-
if
|
410
|
-
|
411
|
-
if
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
return
|
388
|
+
category = variant.var_type
|
389
|
+
if category in ["indel", "snp"]:
|
390
|
+
category = "snv"
|
391
|
+
if category == "mnp":
|
392
|
+
category = "snv"
|
393
|
+
LOG.warning("Category MNP found for variant. Setting to SNV.")
|
394
|
+
|
395
|
+
parsed_variant["category"] = category
|
396
|
+
return category
|
417
397
|
|
418
398
|
|
419
399
|
def set_dbsnp_id(parsed_variant, variant_id):
|
@@ -660,14 +640,13 @@ def set_clnsig(parsed_variant, variant, parsed_transcripts):
|
|
660
640
|
variant(cyvcf2.Variant)
|
661
641
|
parsed_transcripts(list)
|
662
642
|
"""
|
663
|
-
|
664
|
-
clnsig_predictions = []
|
665
|
-
if len(clnsig_predictions) == 0 and len(parsed_transcripts) > 0:
|
666
|
-
# Parse INFO fielf to collect clnsig info
|
667
|
-
clnsig_predictions = parse_clnsig(variant, transcripts=parsed_transcripts)
|
668
|
-
|
643
|
+
clnsig_predictions = parse_clnsig(variant, transcripts=parsed_transcripts)
|
669
644
|
parsed_variant["clnsig"] = clnsig_predictions
|
670
645
|
|
646
|
+
clnsig_onco_predictions = parse_clnsig_onc(variant)
|
647
|
+
if clnsig_onco_predictions:
|
648
|
+
parsed_variant["clnsig_onc"] = clnsig_onco_predictions
|
649
|
+
|
671
650
|
|
672
651
|
def set_rank_result(parsed_variant, variant, rank_results_header):
|
673
652
|
"""Set rank_result in parsed_variant
|
scout/server/app.py
CHANGED
@@ -149,15 +149,7 @@ def configure_extensions(app):
|
|
149
149
|
# setup rerunner service
|
150
150
|
extensions.rerunner.init_app(app)
|
151
151
|
|
152
|
-
|
153
|
-
LOG.info("LDAP login enabled")
|
154
|
-
# setup connection to server
|
155
|
-
extensions.ldap_manager.init_app(app)
|
156
|
-
|
157
|
-
if app.config.get("GOOGLE"):
|
158
|
-
LOG.info("Google login enabled")
|
159
|
-
# setup connection to google oauth2
|
160
|
-
configure_oauth_login(app)
|
152
|
+
set_login_system(app)
|
161
153
|
|
162
154
|
if app.config.get("CUSTOM_IGV_TRACKS") or app.config.get("CLOUD_IGV_TRACKS"):
|
163
155
|
LOG.info("Collecting IGV tracks from cloud or local resources")
|
@@ -172,6 +164,21 @@ def configure_extensions(app):
|
|
172
164
|
extensions.bionano_access.init_app(app)
|
173
165
|
|
174
166
|
|
167
|
+
def set_login_system(app):
|
168
|
+
"""Initialize login system: LDAP, Google OAuth, Keycloak. If none of these is set, then simple database user matching is used."""
|
169
|
+
if app.config.get("LDAP_HOST"):
|
170
|
+
LOG.info("LDAP login enabled")
|
171
|
+
extensions.ldap_manager.init_app(app)
|
172
|
+
|
173
|
+
if app.config.get("GOOGLE"):
|
174
|
+
LOG.info("Google login enabled")
|
175
|
+
configure_google_login(app)
|
176
|
+
|
177
|
+
if app.config.get("KEYCLOAK"):
|
178
|
+
LOG.info("keycloak login enabled")
|
179
|
+
configure_keycloak_login(app)
|
180
|
+
|
181
|
+
|
175
182
|
def register_blueprints(app):
|
176
183
|
"""Register Flask blueprints."""
|
177
184
|
app.register_blueprint(public.public_bp)
|
@@ -301,25 +308,29 @@ def register_tests(app):
|
|
301
308
|
return os.path.exists(path)
|
302
309
|
|
303
310
|
|
304
|
-
def
|
305
|
-
"""Register
|
306
|
-
|
307
|
-
google_conf = app.config["GOOGLE"]
|
308
|
-
discovery_url = google_conf.get("discovery_url")
|
309
|
-
client_id = google_conf.get("client_id")
|
310
|
-
client_secret = google_conf.get("client_secret")
|
311
|
-
|
311
|
+
def configure_oidc_login(app: Flask, provider_name: str, config_key: str):
|
312
|
+
"""Register an OIDC login client using config settings."""
|
313
|
+
provider_conf = app.config[config_key]
|
312
314
|
extensions.oauth_client.init_app(app)
|
313
|
-
|
314
315
|
extensions.oauth_client.register(
|
315
|
-
name=
|
316
|
-
server_metadata_url=discovery_url,
|
317
|
-
client_id=client_id,
|
318
|
-
client_secret=client_secret,
|
316
|
+
name=provider_name,
|
317
|
+
server_metadata_url=provider_conf.get("discovery_url"),
|
318
|
+
client_id=provider_conf.get("client_id"),
|
319
|
+
client_secret=provider_conf.get("client_secret"),
|
319
320
|
client_kwargs={"scope": "openid email profile"},
|
320
321
|
)
|
321
322
|
|
322
323
|
|
324
|
+
def configure_google_login(app):
|
325
|
+
"""Register the Google Oauth login client using config settings"""
|
326
|
+
configure_oidc_login(app, "google", "GOOGLE")
|
327
|
+
|
328
|
+
|
329
|
+
def configure_keycloak_login(app):
|
330
|
+
"""Register a Keycloak OIDC login client using config settings"""
|
331
|
+
configure_oidc_login(app, "keycloak", "KEYCLOAK")
|
332
|
+
|
333
|
+
|
323
334
|
def configure_email_logging(app):
|
324
335
|
"""Setup logging of error/exceptions to email."""
|
325
336
|
import logging
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
import logging
|
3
3
|
import os.path
|
4
|
-
from typing import Dict, Optional
|
4
|
+
from typing import Dict, List, Optional
|
5
5
|
|
6
6
|
from flask import flash, session
|
7
7
|
from flask_login import current_user
|
@@ -43,7 +43,9 @@ def set_session_tracks(display_obj: dict):
|
|
43
43
|
|
44
44
|
session_tracks = list(display_obj.get("reference_track", {}).values())
|
45
45
|
for key, track_items in display_obj.items():
|
46
|
-
if key not in ["tracks", "custom_tracks", "sample_tracks", "config_custom_tracks"]
|
46
|
+
if key not in ["tracks", "custom_tracks", "sample_tracks", "config_custom_tracks"] + list(
|
47
|
+
CASE_SPECIFIC_TRACKS.keys()
|
48
|
+
):
|
47
49
|
continue
|
48
50
|
for track_item in track_items:
|
49
51
|
session_tracks += list(track_item.values())
|
@@ -263,11 +265,25 @@ def make_locus_from_gene(variant_obj: Dict, case_obj: Dict, build: str) -> str:
|
|
263
265
|
return f"{chrom}:{locus_start}-{locus_end}"
|
264
266
|
|
265
267
|
|
266
|
-
def
|
267
|
-
"""Return a dict according to IGV track format.
|
268
|
+
def get_tracks(name_list: list, file_list: list) -> List[Dict]:
|
269
|
+
"""Return a list of dict according to IGV track format.
|
270
|
+
|
271
|
+
If an index can be found (e.g. for bam, cram files), use it explicitly.
|
272
|
+
If the format is one of those two alignment types, set it explicitly: it will need to be dynamically
|
273
|
+
filled into the igv_viewer html template script for igv.js.
|
274
|
+
"""
|
268
275
|
track_list = []
|
269
|
-
for track in file_list:
|
270
|
-
|
276
|
+
for name, track in zip(name_list, file_list):
|
277
|
+
if track == "missing":
|
278
|
+
continue
|
279
|
+
track_config = {"name": name, "url": track, "min": 0.0, "max": 30.0}
|
280
|
+
index = find_index(track)
|
281
|
+
if index:
|
282
|
+
track_config["indexURL"] = index
|
283
|
+
file_format_ending = track.split(".")[-1]
|
284
|
+
if file_format_ending in ["bam", "cram"]:
|
285
|
+
track_config["format"] = file_format_ending
|
286
|
+
track_list.append(track_config)
|
271
287
|
return track_list
|
272
288
|
|
273
289
|
|
@@ -338,17 +354,20 @@ def set_sample_tracks(display_obj: dict, case_groups: list, chromosome: str):
|
|
338
354
|
|
339
355
|
|
340
356
|
def set_case_specific_tracks(display_obj, case_obj):
|
341
|
-
"""Set up tracks from files that might be present
|
357
|
+
"""Set up tracks from files that might be present for the focus case samples,
|
358
|
+
not fetched for all samples in the case group.
|
342
359
|
(rhocall files, tiddit coverage files, upd regions and sites files)
|
343
360
|
Args:
|
344
361
|
display_obj(dict) dictionary containing all tracks info
|
345
362
|
form(dict) flask request form dictionary
|
346
363
|
"""
|
347
364
|
for track, label in CASE_SPECIFIC_TRACKS.items():
|
348
|
-
if case_obj.get(track)
|
365
|
+
if None in [case_obj.get(track), case_obj.get("sample_names")]:
|
349
366
|
continue
|
350
|
-
|
351
|
-
display_obj[track] =
|
367
|
+
|
368
|
+
display_obj[track] = get_tracks(
|
369
|
+
[f"{label} - {sample}" for sample in case_obj.get("sample_names")], case_obj.get(track)
|
370
|
+
)
|
352
371
|
|
353
372
|
|
354
373
|
def set_config_custom_tracks(display_obj: dict, build: str):
|
@@ -86,12 +86,12 @@
|
|
86
86
|
{% endif %}
|
87
87
|
},
|
88
88
|
{% endfor %}
|
89
|
-
{% for wtrack in
|
89
|
+
{% for wtrack in rhocall_wigs %}
|
90
90
|
{
|
91
91
|
type: "wig",
|
92
92
|
name: '{{ wtrack.name }}',
|
93
93
|
url: '{{ url_for("alignviewers.remote_static", file=wtrack.url) }}',
|
94
|
-
format: '
|
94
|
+
format: 'bw',
|
95
95
|
{# indexURL: '{{ url_for("alignviewers.remote_static", file=wtrack.url) }}', #}
|
96
96
|
color: "rgb(60, 37, 17)",
|
97
97
|
min: '{{ wtrack.min }}',
|
@@ -99,12 +99,12 @@
|
|
99
99
|
sourceType: 'file'
|
100
100
|
},
|
101
101
|
{% endfor %}
|
102
|
-
{% for btrack in
|
102
|
+
{% for btrack in rhocall_beds %}
|
103
103
|
{
|
104
|
-
type: "
|
104
|
+
type: "annotation",
|
105
105
|
name: '{{ btrack.name }}',
|
106
106
|
url: '{{ url_for("alignviewers.remote_static", file=btrack.url) }}',
|
107
|
-
format: '
|
107
|
+
format: 'bb',
|
108
108
|
color: "rgb(65, 31, 30)",
|
109
109
|
sourceType: 'file'
|
110
110
|
},
|
@@ -117,33 +117,63 @@
|
|
117
117
|
indexURL: "{{ url_for('alignviewers.remote_static', file=track.indexURL) }}",
|
118
118
|
sourceType: "file",
|
119
119
|
groupBy: "tag:HP",
|
120
|
-
colorBy: "basemod2:m",
|
121
120
|
showSoftClips: {{track.show_soft_clips | lower }},
|
122
121
|
format: "{{ track.format }}",
|
123
122
|
height: "{{track.height}}"
|
124
123
|
},
|
125
124
|
{% endfor %}
|
126
|
-
{% for ttrack in
|
125
|
+
{% for ttrack in tiddit_coverage_wigs %}
|
127
126
|
{
|
128
127
|
type: "wig",
|
128
|
+
format: 'bw',
|
129
129
|
name: '{{ ttrack.name }}',
|
130
130
|
url: '{{ url_for("alignviewers.remote_static", file=ttrack.url) }}',
|
131
131
|
color: "rgb(40, 0, 13)",
|
132
132
|
sourceType: 'file'
|
133
133
|
},
|
134
134
|
{% endfor %}
|
135
|
-
{% for
|
135
|
+
{% for ptrack in paraphase_alignments %}
|
136
136
|
{
|
137
|
-
type: "
|
137
|
+
type: "alignment",
|
138
|
+
name: "{{ ptrack.name }}",
|
139
|
+
url: '{{ url_for("alignviewers.remote_static", file=ptrack.url) }}',
|
140
|
+
format: "{{ ptrack.format }}",
|
141
|
+
groupBy: "tag:HP",
|
142
|
+
indexURL: '{{ url_for("alignviewers.remote_static", file=ptrack.indexURL) }}',
|
143
|
+
min: '{{ ptrack.min }}',
|
144
|
+
max: '{{ ptrack.max }}',
|
145
|
+
sourceType: 'file'
|
146
|
+
},
|
147
|
+
{% endfor %}
|
148
|
+
{% for atrack in assembly_alignments %}
|
149
|
+
{
|
150
|
+
type: "alignment",
|
151
|
+
name: "{{ atrack.name }}",
|
152
|
+
url: '{{ url_for("alignviewers.remote_static", file=atrack.url) }}',
|
153
|
+
format: "{{ atrack.format }}",
|
154
|
+
groupBy: "tag:HP",
|
155
|
+
height: 140,
|
156
|
+
showCoverage: false,
|
157
|
+
indexURL: '{{ url_for("alignviewers.remote_static", file=atrack.indexURL) }}',
|
158
|
+
min: '{{ atrack.min }}',
|
159
|
+
max: '{{ atrack.max }}',
|
160
|
+
sourceType: 'file'
|
161
|
+
},
|
162
|
+
{% endfor %}
|
163
|
+
{% for rtrack in upd_regions_beds %}
|
164
|
+
{
|
165
|
+
type: 'annotation',
|
166
|
+
format: 'bb',
|
138
167
|
name: '{{ rtrack.name }}',
|
139
168
|
url: '{{ url_for("alignviewers.remote_static", file=rtrack.url) }}',
|
140
169
|
color: "rgb(0, 204, 102)",
|
141
170
|
sourceType: 'file'
|
142
171
|
},
|
143
172
|
{% endfor %}
|
144
|
-
{% for strack in
|
173
|
+
{% for strack in upd_sites_beds %}
|
145
174
|
{
|
146
|
-
type: "
|
175
|
+
type: "annotation",
|
176
|
+
format: 'bb',
|
147
177
|
name: '{{ strack.name }}',
|
148
178
|
url: '{{ url_for("alignviewers.remote_static", file=strack.url) }}',
|
149
179
|
color: "rgb(25, 61, 4)",
|
@@ -295,7 +295,7 @@
|
|
295
295
|
{% if case.vcf_files.vcf_fusion_research %}
|
296
296
|
<a class="btn btn-dark btn-sm text-white" href="{{ url_for('variants.fusion_variants', institute_id=institute._id, case_name=case.display_name, variant_type='research') }}">Research fusion variants</a>
|
297
297
|
{% endif %}
|
298
|
-
{% if case.omics_files.fraser_research or case.omics_files.outrider_research %}
|
298
|
+
{% if case.omics_files and (case.omics_files.fraser_research or case.omics_files.outrider_research) %}
|
299
299
|
<a class="btn btn-dark btn-sm text-white" href="{{ url_for('omics_variants.outliers', institute_id=institute._id, case_name=case.display_name, variant_type='research') }}">Research WTS variants</a>
|
300
300
|
{% endif %}
|
301
301
|
</div>
|