scout-browser 4.90.1__py3-none-any.whl → 4.91__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/__version__.py +1 -1
- scout/adapter/mongo/case.py +27 -38
- scout/commands/export/variant.py +14 -4
- scout/commands/load/panel.py +2 -1
- scout/commands/update/panelapp.py +11 -3
- scout/commands/view/case.py +2 -2
- scout/constants/__init__.py +1 -2
- scout/constants/acmg.py +15 -15
- scout/constants/case_tags.py +0 -46
- scout/constants/clnsig.py +2 -1
- scout/constants/gene_tags.py +0 -6
- scout/constants/panels.py +16 -0
- scout/constants/variants_export.py +2 -0
- scout/demo/__init__.py +4 -1
- scout/demo/panelapp_panel.json +463 -0
- scout/demo/panelapp_panels_reduced.json +37 -0
- scout/load/panel.py +3 -142
- scout/load/panelapp.py +138 -0
- scout/models/case/case_loading_models.py +5 -4
- scout/parse/panel.py +3 -117
- scout/parse/panelapp.py +112 -0
- scout/parse/variant/clnsig.py +26 -21
- scout/parse/variant/genotype.py +6 -5
- scout/server/blueprints/alignviewers/controllers.py +7 -5
- scout/server/blueprints/alignviewers/templates/alignviewers/utils.html +1 -1
- scout/server/blueprints/cases/templates/cases/case_sma.html +49 -42
- scout/server/blueprints/cases/templates/cases/collapsible_actionbar.html +27 -12
- scout/server/blueprints/cases/views.py +18 -7
- scout/server/blueprints/clinvar/templates/clinvar/clinvar_submissions.html +7 -7
- scout/server/blueprints/clinvar/templates/clinvar/multistep_add_variant.html +2 -2
- scout/server/blueprints/dashboard/controllers.py +128 -165
- scout/server/blueprints/dashboard/forms.py +3 -13
- scout/server/blueprints/dashboard/templates/dashboard/dashboard_general.html +17 -22
- scout/server/blueprints/institutes/forms.py +1 -2
- scout/server/blueprints/institutes/templates/overview/cases.html +2 -133
- scout/server/blueprints/institutes/templates/overview/utils.html +135 -0
- scout/server/blueprints/omics_variants/templates/omics_variants/outliers.html +5 -0
- scout/server/blueprints/panels/templates/panels/panel.html +5 -1
- scout/server/blueprints/panels/templates/panels/panel_pdf_simple.html +5 -1
- scout/server/blueprints/variant/controllers.py +6 -1
- scout/server/blueprints/variant/templates/variant/buttons.html +11 -10
- scout/server/blueprints/variant/templates/variant/components.html +63 -44
- scout/server/blueprints/variant/templates/variant/str-variant-reviewer.html +1 -1
- scout/server/blueprints/variant/templates/variant/utils.html +38 -10
- scout/server/blueprints/variant/templates/variant/variant.html +1 -1
- scout/server/blueprints/variants/controllers.py +9 -4
- scout/server/blueprints/variants/templates/variants/cancer-sv-variants.html +9 -5
- scout/server/blueprints/variants/templates/variants/cancer-variants.html +6 -17
- scout/server/blueprints/variants/templates/variants/str-variants.html +2 -2
- scout/server/blueprints/variants/templates/variants/sv-variants.html +8 -1
- scout/server/blueprints/variants/templates/variants/utils.html +14 -0
- scout/server/extensions/__init__.py +2 -0
- scout/server/extensions/panelapp_extension.py +75 -0
- scout/server/links.py +19 -1
- scout/server/utils.py +25 -33
- {scout_browser-4.90.1.dist-info → scout_browser-4.91.dist-info}/METADATA +1 -1
- {scout_browser-4.90.1.dist-info → scout_browser-4.91.dist-info}/RECORD +61 -57
- {scout_browser-4.90.1.dist-info → scout_browser-4.91.dist-info}/WHEEL +1 -1
- scout/demo/panelapp_test_panel.json +0 -79
- {scout_browser-4.90.1.dist-info → scout_browser-4.91.dist-info}/LICENSE +0 -0
- {scout_browser-4.90.1.dist-info → scout_browser-4.91.dist-info}/entry_points.txt +0 -0
- {scout_browser-4.90.1.dist-info → scout_browser-4.91.dist-info}/top_level.txt +0 -0
@@ -66,51 +66,70 @@
|
|
66
66
|
{% endmacro %}
|
67
67
|
|
68
68
|
{% macro alignments(institute, case, variant, current_user, config, igv_tracks, has_rna_tracks=False) %}
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
{
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
{
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
69
|
+
<ul class="list-group">
|
70
|
+
<li class="list-group-item d-flex justify-content-between">
|
71
|
+
{% if variant.is_mitochondrial and not case.mt_bams %}
|
72
|
+
<span data-bs-toggle="tooltip" title="Alignment file(s) missing">
|
73
|
+
<a href="{{url_for('alignviewers.igv', institute_id=institute['_id'], case_name=case['display_name'], variant_id=variant['_id'])}}"
|
74
|
+
target="_blank"
|
75
|
+
class="btn btn-secondary btn-sm text-white disabled" aria-disabled="true"><span class="fa fa-times-circle fa-fw me-1"></span>IGV mtDNA
|
76
|
+
</a>
|
77
|
+
</span>
|
78
|
+
{% elif not case.bam_files and not variant.is_mitochondrial %}
|
79
|
+
<span data-bs-toggle="tooltip" title="Alignment file(s) missing">
|
80
|
+
<a href="{{url_for('alignviewers.igv', institute_id=institute['_id'], case_name=case['display_name'], variant_id=variant['_id'])}}"
|
81
|
+
target="_blank"
|
82
|
+
class="btn btn-secondary btn-sm text-white disabled"
|
83
|
+
aria-disabled="true"><span class="fa fa-times-circle fa-fw me-1"></span>IGV gDNA
|
84
|
+
</a>
|
85
|
+
</span>
|
86
|
+
{% else %}
|
87
|
+
<span>
|
88
|
+
<a href="{{url_for('alignviewers.igv', institute_id=institute['_id'], case_name=case['display_name'], variant_id=variant['_id'])}}"
|
89
|
+
target="_blank"
|
90
|
+
class="btn btn-secondary btn-sm text-white"><span class="fa fa-magnifying-glass fa-fw me-1"></span>
|
91
|
+
IGV {% if variant.is_mitochondrial %}mt{% else %}g{% endif %}DNA
|
92
|
+
</a>
|
93
|
+
</span>
|
94
|
+
{% endif %}
|
95
|
+
{% if variant.category == "str" %}
|
96
|
+
<div class="ms-1">
|
97
|
+
{{ reviewer_button(case,variant,case_groups,institute._id) }}
|
98
|
+
</div>
|
99
|
+
{% endif %}
|
100
|
+
{% if has_rna_tracks %}
|
101
|
+
<div class="ms-1">
|
102
|
+
{{ splice_junctions_button(institute._id, case, variant._id) }}
|
103
|
+
</div>
|
104
|
+
{% endif %}
|
105
|
+
{% if variant.alamut_link %}
|
106
|
+
<div class="ms-1">
|
107
|
+
<a href="{{ variant.alamut_link }}" class="btn btn-sm btn-secondary" target="_blank" data-bs-toggle="tooltip" title="Alamut Visual (Plus) - Open variant - with genome g. coordinate">Alamut g.</a>
|
108
|
+
</div>
|
109
|
+
{% endif %}
|
110
|
+
{% for gene in variant.genes %}
|
111
|
+
<div class="d-flex flex-wrap ms-1">
|
112
|
+
{% if gene.alamut_link %}
|
113
|
+
<a href="{{ gene.alamut_link }}" class="btn btn-sm btn-secondary text-white" rel="noopener" referrerpolicy="no-referrer" target="_blank" data-bs-toggle="tooltip" title="Alamut Visual (Plus) - Open variant - with gene transcript c. coordinate">Alamut {{ gene.common.hgnc_symbol if gene.common else gene.hgnc_id }} c.</a>
|
114
|
+
{% endif %}
|
115
|
+
</div>
|
116
|
+
{% endfor %}
|
117
|
+
{% if config.chanjo_report %}
|
118
|
+
<div class="d-flex flex-wrap ms-1">
|
91
119
|
{% for gene in variant.genes %}
|
92
|
-
<
|
93
|
-
|
94
|
-
|
95
|
-
{% endif %}
|
96
|
-
</div>
|
120
|
+
<a class="btn btn-sm btn-secondary text-white" rel="noopener noreferrer" target="_blank" href="{{ url_for('report.gene', gene_id=gene.hgnc_id, sample_id=variant.samples|map(attribute='sample_id')|list) }}">
|
121
|
+
Gene coverage: {{ gene.common.hgnc_symbol if gene.common else gene.hgnc_id }}
|
122
|
+
</a>
|
97
123
|
{% endfor %}
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
</li>
|
108
|
-
<li class="list-group-item">
|
109
|
-
<div>
|
110
|
-
{{ igv_track_selection(igv_tracks, current_user) }}
|
111
|
-
</div>
|
112
|
-
</li>
|
113
|
-
</ul>
|
124
|
+
</div>
|
125
|
+
{% endif %}
|
126
|
+
</li>
|
127
|
+
<li class="list-group-item">
|
128
|
+
<div>
|
129
|
+
{{ igv_track_selection(igv_tracks, current_user) }}
|
130
|
+
</div>
|
131
|
+
</li>
|
132
|
+
</ul>
|
114
133
|
{% endmacro %}
|
115
134
|
|
116
135
|
{% macro acmg_form(institute, case, variant, ACMG_OPTIONS, selected=None) %}
|
@@ -224,7 +243,7 @@
|
|
224
243
|
<div class="card-body mt-1 ms-3 mb-1" style="padding: 0;">
|
225
244
|
Matching tiered:
|
226
245
|
{% for tier, tiered_info in matching_tiered.items() %}
|
227
|
-
<a style="padding: 0;" class="btn btn-{{tiered_info.label}} btn-xs" data-bs-toggle="collapse" href="#collapse_{{tier}}"
|
246
|
+
<a style="padding: 0;" class="btn btn-{{tiered_info.label}} btn-xs" data-bs-toggle="collapse" href="#collapse_{{tier}}" aria-expanded="false" aria-controls="collapseExample">
|
228
247
|
{{tier}} ({{tiered_info.links|length}}x)
|
229
248
|
</a>
|
230
249
|
<div class="collapse" id="collapse_{{tier}}">
|
@@ -2,7 +2,7 @@
|
|
2
2
|
{% from "utils.html" import comments_table, pedigree_panel %}
|
3
3
|
{% from "variants/components.html" import frequency_cell_general %}
|
4
4
|
{% from "variants/utils.html" import cell_rank, dismiss_variants_block, filter_form_footer, update_stash_filter_button_status, pagination_footer, pagination_hidden_div, str_filters %}
|
5
|
-
{% from "variant/buttons.html" import
|
5
|
+
{% from "variant/buttons.html" import reviewer_button%}
|
6
6
|
|
7
7
|
{% block title %}
|
8
8
|
{{ super() }} - {{ institute.display_name }} - {{ case.display_name }} - STR variants
|
@@ -92,7 +92,7 @@
|
|
92
92
|
<div id="accordion">
|
93
93
|
<div class="row">
|
94
94
|
<div class="col-4 d-flex align-items-center">
|
95
|
-
<span><a data-bs-toggle="collapse" data-bs-parent="#accordion" href="#track_settings" class="text-secondary"
|
95
|
+
<span><a data-bs-toggle="collapse" data-bs-parent="#accordion" href="#track_settings" class="text-secondary"><i class="fas fa-cogs text-secondary me-1"></i>Settings IGV DNA</a></span>
|
96
96
|
</div>
|
97
97
|
<div id="track_settings" class="col-8 collapse">
|
98
98
|
<form action="{{url_for('variant.update_tracks_settings')}}" method="POST">
|
@@ -235,16 +235,24 @@
|
|
235
235
|
{{ gene.common.hgnc_symbol if gene.common else gene.hgnc_id }}
|
236
236
|
</a>
|
237
237
|
</td>
|
238
|
-
<td class="d-flex
|
239
|
-
<a target="_blank" href="{{ transcript.ensembl_link }}">
|
238
|
+
<td class="d-flex align-items-center">
|
239
|
+
<a target="_blank" class="float-start" href="{{ transcript.ensembl_link }}">
|
240
240
|
{{ transcript.transcript_id }}
|
241
241
|
</a>
|
242
|
+
{% if transcript.transcript_id in variant.mane_transcripts %}
|
243
|
+
{% if variant.mane_transcripts[transcript.transcript_id].mane %}
|
244
|
+
<a href="#" data-bs-toggle="tooltip" title="MANE Select - {{variant.mane_transcripts[transcript.transcript_id].mane}}"><span class="badge bg-success ms-3" title="MANE Select">M</span></a>
|
245
|
+
{% endif %}
|
246
|
+
{% if variant.mane_transcripts[transcript.transcript_id].mane_plus %}
|
247
|
+
<a href="#" data-bs-toggle="tooltip" title="MANE Plus Clinical - {{variant.mane_transcripts[transcript.transcript_id].mane_plus}}"><span class="badge bg-success ms-3" title="MANE Plus Clinical">M+</span></a>
|
248
|
+
{% endif %}
|
249
|
+
{% endif %}
|
242
250
|
{% if transcript.is_canonical %}
|
243
|
-
<span class="badge bg-info">C</span>
|
251
|
+
<span class="badge bg-info ms-3">C</span>
|
244
252
|
{% endif %}
|
245
253
|
</td>
|
246
254
|
<td>
|
247
|
-
{{ transcript.refseq_identifiers|join(',') }}
|
255
|
+
{{ transcript.refseq_identifiers|join(', ') }}
|
248
256
|
</td>
|
249
257
|
<td>{{ transcript.biotype or '' }}</td>
|
250
258
|
<td data-bs-toggle="tooltip" data-bs-placement="right" title="{{ transcript.functional_annotations|join(', ') }}">
|
@@ -347,11 +355,31 @@
|
|
347
355
|
{% endif %}
|
348
356
|
</div>
|
349
357
|
<div class="col-3">
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
358
|
+
{% if variant.is_mitochondrial and not case.mt_bams %}
|
359
|
+
<span data-bs-toggle="tooltip" title="Alignment file(s) missing">
|
360
|
+
<a href="{{url_for('alignviewers.igv', institute_id=case['owner'], case_name=case['display_name'], variant_id=variant['_id'], chrom=chrom, start=align_start, stop=align_end)}}"
|
361
|
+
target="_blank"
|
362
|
+
class="btn btn-secondary btn-sm text-white
|
363
|
+
disabled" title="Alignment file(s) missing" data-bs-toggle="tooltip" aria-disabled="true"><span class="fa fa-times-circle fa-fw me-1"></span>IGV mtDNA
|
364
|
+
</a>
|
365
|
+
</span>
|
366
|
+
{% elif not case.bam_files and not variant.is_mitochondrial%}
|
367
|
+
<span data-bs-toggle="tooltip" title="Alignment file(s) missing">
|
368
|
+
<a href="{{url_for('alignviewers.igv', institute_id=case['owner'], case_name=case['display_name'], variant_id=variant['_id'], chrom=chrom, start=align_start, stop=align_end)}}"
|
369
|
+
target="_blank"
|
370
|
+
class="btn btn-secondary btn-sm text-white
|
371
|
+
disabled" title="Alignment file(s) missing" data-bs-toggle="tooltip" aria-disabled="true"><span class="fa fa-times-circle fa-fw me-1"></span>IGV gDNA
|
372
|
+
</a>
|
373
|
+
</span>
|
374
|
+
{% else %}
|
375
|
+
<span>
|
376
|
+
<a href="{{url_for('alignviewers.igv', institute_id=case['owner'], case_name=case['display_name'], variant_id=variant['_id'], chrom=chrom, start=align_start, stop=align_end)}}"
|
377
|
+
target="_blank"
|
378
|
+
class="btn btn-secondary btn-sm text-white"><span class="fa fa-magnifying-glass fa-fw me-1"></span>
|
379
|
+
IGV {% if variant.is_mitochondrial %}mt{% else %}g{% endif %}DNA
|
380
|
+
</a>
|
381
|
+
</span>
|
382
|
+
{% endif %}
|
355
383
|
</div>
|
356
384
|
<div class="col-1">
|
357
385
|
<!--Define build variable to be used in the UCSC link-->
|
@@ -969,8 +969,6 @@ def get_str_mc(variant_obj: dict) -> Optional[int]:
|
|
969
969
|
alt_mc = int(alt_num.group())
|
970
970
|
return alt_mc
|
971
971
|
|
972
|
-
return None
|
973
|
-
|
974
972
|
|
975
973
|
def download_str_variants(case_obj, variant_objs):
|
976
974
|
"""Download filtered STR variants for a case to a CSV file
|
@@ -1004,11 +1002,11 @@ def download_str_variants(case_obj, variant_objs):
|
|
1004
1002
|
for variant in variant_objs.limit(EXPORTED_VARIANTS_LIMIT):
|
1005
1003
|
variant_line = []
|
1006
1004
|
variant_line.append(str(variant.get("variant_rank", ""))) # index
|
1007
|
-
variant_line.append(variant.get("str_repid")) # Repeat locus
|
1005
|
+
variant_line.append(variant.get("str_repid", variant.get("str_trid", ""))) # Repeat locus
|
1008
1006
|
variant_line.append(
|
1009
1007
|
variant.get("str_display_ru", variant.get("str_ru", ""))
|
1010
1008
|
) # Reference repeat unit
|
1011
|
-
variant_line.append(get_str_mc(variant) or ".") # Estimated size
|
1009
|
+
variant_line.append(str(get_str_mc(variant) or ".")) # Estimated size
|
1012
1010
|
variant_line.append(str(variant.get("str_ref", ""))) # Reference size
|
1013
1011
|
variant_line.append(str(variant.get("str_status", ""))) # Status
|
1014
1012
|
gt_cell = ""
|
@@ -1078,6 +1076,13 @@ def variant_export_lines_common(store: MongoAdapter, variant: dict, case_obj: di
|
|
1078
1076
|
position = variant["position"]
|
1079
1077
|
change = variant["reference"] + ">" + variant["alternative"]
|
1080
1078
|
variant_line.append(variant.get("rank_score", "N/A"))
|
1079
|
+
cat = (
|
1080
|
+
variant.get("sub_category")
|
1081
|
+
if variant["category"] in ["sv", "cancer_sv"]
|
1082
|
+
else variant.get("category")
|
1083
|
+
)
|
1084
|
+
variant_line.append(cat.upper() if cat else "")
|
1085
|
+
variant_line.append(" ".join([f"{name}:{caller}" for name, caller in callers(variant)]))
|
1081
1086
|
variant_line.append(variant["chromosome"])
|
1082
1087
|
variant_line.append(position)
|
1083
1088
|
variant_line.append(change)
|
@@ -1,5 +1,5 @@
|
|
1
1
|
{% extends "layout.html" %}
|
2
|
-
{% from "variants/utils.html" import cancer_sv_filters,cell_rank, pagination_footer, pagination_hidden_div, filter_form_footer, filter_script_main, update_stash_filter_button_status, dismiss_variants_block %}
|
2
|
+
{% from "variants/utils.html" import cancer_sv_filters,cell_rank, pagination_footer, pagination_hidden_div, filter_form_footer, filter_script_main, update_stash_filter_button_status, dismiss_variants_block, callers_cell %}
|
3
3
|
{% from "variants/components.html" import external_scripts, external_stylesheets, frequency_cell_general, observed_cell_general, variant_gene_symbols_cell, variant_funct_anno_cell %}
|
4
4
|
|
5
5
|
{% block title %}
|
@@ -38,7 +38,7 @@
|
|
38
38
|
{% endblock %}
|
39
39
|
|
40
40
|
{% block content_main %}
|
41
|
-
<form method="POST" id="filters_form" action="{{url_for('variants.cancer_sv_variants', institute_id=institute._id, case_name=case.display_name)}}" >
|
41
|
+
<form method="POST" id="filters_form" action="{{url_for('variants.cancer_sv_variants', institute_id=institute._id, case_name=case.display_name)}}" enctype="multipart/form-data" onsubmit="return validateForm()">
|
42
42
|
<div class="container-float">
|
43
43
|
{{ pagination_hidden_div(page) }}
|
44
44
|
<div class="card panel-default" id="accordion">
|
@@ -57,8 +57,9 @@
|
|
57
57
|
<thead class="thead table-light">
|
58
58
|
<tr>
|
59
59
|
<th style="width:2%"></th>
|
60
|
-
<th style="width:
|
60
|
+
<th style="width:3%">Index</th>
|
61
61
|
<th>Type</th>
|
62
|
+
<th style="width:9%">Callers</th>
|
62
63
|
<th style="width:5%">Chr</th>
|
63
64
|
<th>Start</th>
|
64
65
|
<th>End</th>
|
@@ -103,8 +104,11 @@
|
|
103
104
|
<td>
|
104
105
|
{{ cell_rank(variant, institute, case, form, manual_rank_options) }}
|
105
106
|
</td>
|
106
|
-
<td
|
107
|
-
{{ variant.sub_category|upper }}
|
107
|
+
<td>
|
108
|
+
{{ variant.sub_category|upper }}
|
109
|
+
</td>
|
110
|
+
<td>
|
111
|
+
{{ callers_cell(variant) }}
|
108
112
|
</td>
|
109
113
|
<td>{{ variant.chromosome if variant.chromosome == variant.end_chrom else variant.chromosome+'-'+variant.end_chrom }}</td>
|
110
114
|
<td class="col-2"><span style="white-space: nowrap;">{{ variant.position|human_longint|safe }}</span></td>
|
@@ -1,7 +1,7 @@
|
|
1
1
|
{% extends "layout.html" %}
|
2
2
|
|
3
|
-
{% from "variants/components.html" import external_scripts, external_stylesheets, gene_cell, frequency_cell_general, observed_cell_general %}
|
4
|
-
{% from "variants/utils.html" import cancer_filters, cell_rank, pagination_footer, pagination_hidden_div, dismiss_variants_block, filter_form_footer, filter_script_main, update_stash_filter_button_status %}
|
3
|
+
{% from "variants/components.html" import external_scripts, external_stylesheets, gene_cell, frequency_cell_general, observed_cell_general, variant_funct_anno_cell %}
|
4
|
+
{% from "variants/utils.html" import cancer_filters, cell_rank, pagination_footer, pagination_hidden_div, dismiss_variants_block, filter_form_footer, filter_script_main, update_stash_filter_button_status, callers_cell %}
|
5
5
|
{% from "variants/indicators.html" import pin_indicator, causative_badge, clinical_assessments_badge, comments_badge, dismissals_badge, evaluations_badge, group_assessments_badge, matching_manual_rank, other_tiered_variants, research_assessments_badge %}
|
6
6
|
|
7
7
|
{% block title %}
|
@@ -56,14 +56,14 @@
|
|
56
56
|
<th title="Gene">Gene</th>
|
57
57
|
<th title="Variant" style="width:12%">HGVS[c/p]</th>
|
58
58
|
<th title="Assessments including variant tier" style="width:8%">Assessment</th>
|
59
|
-
<th title="Quality markers" style="width:6%">Qual</th>
|
60
59
|
<th title="Rank scores" style="width:4%">Rank</th>
|
61
60
|
<th title="CADD scores" style="width:4%">CADD</th>
|
62
61
|
<th title="Genomic coordinate" style="width:8%">Chr pos</th>
|
63
62
|
<th title="Population frequency">Pop Freq</th>
|
64
63
|
<th title="Observed" style="width:7%">Observed</th>
|
65
64
|
<th title="Variant type">Type</th>
|
66
|
-
<th title="
|
65
|
+
<th title="Callers" style="width:8%">Callers</th>
|
66
|
+
<th title="Functional consequence annotation" style="width:10%">Consequence</th>
|
67
67
|
<th data-bs-toggle="tooltip" data-bs-placement="top" title="Tumor alt. AF. 
 Alt. allele count | Ref. allele count">Tumor</th>
|
68
68
|
<th data-bs-toggle="tooltip" data-bs-placement="top" title="Normal alt. AF. 
 Alt. allele count | Ref. allele count">Normal</th>
|
69
69
|
</tr>
|
@@ -110,18 +110,14 @@
|
|
110
110
|
{{ causative_badge(variant, case) }}
|
111
111
|
{{ other_tiered_variants(variant) }}
|
112
112
|
</td>
|
113
|
-
<td>{{ quality_cell(variant) }}</td>
|
114
113
|
<td>{{ rank_cell(variant) }}</td>
|
115
114
|
<td>{{ cadd_cell(variant) }}</td>
|
116
115
|
<td>{{ position_cell(variant) }}</td>
|
117
116
|
<td class="text-end">{{ frequency_cell_general(variant) }}</td>
|
118
117
|
<td>{{ observed_cell_general(variant) }}</td>
|
119
118
|
<td>{{ variant.sub_category }}</td>
|
120
|
-
<td>
|
121
|
-
|
122
|
-
<div>{{ annotation }}</div>
|
123
|
-
{% endfor %}
|
124
|
-
</td>
|
119
|
+
<td>{{ callers_cell(variant) }}</td>
|
120
|
+
<td>{{ variant_funct_anno_cell(variant) }}</td>
|
125
121
|
<td>{{ allele_cell(variant.tumor or {}) }}</td>
|
126
122
|
<td>{{ allele_cell(variant.normal or {}) }}</td>
|
127
123
|
</tr>
|
@@ -177,13 +173,6 @@
|
|
177
173
|
{% endif %}
|
178
174
|
{% endmacro %}
|
179
175
|
|
180
|
-
{% macro quality_cell(variant) %}
|
181
|
-
{% for filter in variant.filters %}
|
182
|
-
<div data-bs-toggle="tooltip" data-bs-placement="top" title="{{ filter.description }}" class="badge bg-{{ filter.label_class }}">{{ filter.label }}</div>
|
183
|
-
{% endfor %}
|
184
|
-
{% endmacro %}
|
185
|
-
|
186
|
-
|
187
176
|
{% macro position_cell(variant) %}
|
188
177
|
{{ variant.chromosome }}<span class="text-muted">:{{ variant.position }}</span>
|
189
178
|
{% endmacro %}
|
@@ -108,9 +108,9 @@
|
|
108
108
|
{{ variant.position|human_longint|safe }}</span>
|
109
109
|
{{ reviewer_button(case,variant,case_groups,institute._id) }}
|
110
110
|
{% if case.bam_files %}
|
111
|
-
<a class="btn btn-secondary btn-sm text-white" href="{{url_for('alignviewers.igv', institute_id=institute['_id'], case_name=case['display_name'], variant_id=variant['_id'])}}" rel="noopener" target="_blank">IGV
|
111
|
+
<span><a class="btn btn-secondary btn-sm text-white" href="{{url_for('alignviewers.igv', institute_id=institute['_id'], case_name=case['display_name'], variant_id=variant['_id'])}}" rel="noopener" target="_blank"><i class="fa fa-magnifying-glass fa-fw me-1"></i>IGV gDNA</a></span>
|
112
112
|
{% else %}
|
113
|
-
<
|
113
|
+
<span data-bs-toggle="tooltip" title="BAM file(s) missing"><button class="btn btn-secondary btn-sm" disabled><i class="fa fa-times-circle fa-fw me-1"></i>IGV gDNA</button></span>
|
114
114
|
{% endif %}
|
115
115
|
|
116
116
|
</td>
|
@@ -1,5 +1,5 @@
|
|
1
1
|
{% extends "layout.html" %}
|
2
|
-
{% from "variants/utils.html" import sv_filters, cell_rank, pagination_footer, pagination_hidden_div, dismiss_variants_block, filter_form_footer, filter_script_main, update_stash_filter_button_status %}
|
2
|
+
{% from "variants/utils.html" import sv_filters, cell_rank, pagination_footer, pagination_hidden_div, dismiss_variants_block, filter_form_footer, filter_script_main, update_stash_filter_button_status, callers_cell %}
|
3
3
|
{% from "variants/components.html" import external_scripts, external_stylesheets, frequency_cell_general, observed_cell_general, variant_gene_symbols_cell, variant_funct_anno_cell %}
|
4
4
|
|
5
5
|
{% block title %}
|
@@ -60,6 +60,7 @@ onsubmit="return validateForm()">
|
|
60
60
|
<th>Rank</th>
|
61
61
|
<th>Score</th>
|
62
62
|
<th>Type</th>
|
63
|
+
<th>Callers</th>
|
63
64
|
<th>Chr</th>
|
64
65
|
<th>Start</th>
|
65
66
|
<th>End</th>
|
@@ -102,6 +103,12 @@ onsubmit="return validateForm()">
|
|
102
103
|
{{ cell_rank(variant, institute, case, form, manual_rank_options) }}
|
103
104
|
</td>
|
104
105
|
<td class="text-end">{{ variant.rank_score|int }}</td>
|
106
|
+
<td>
|
107
|
+
{{ variant.sub_category|upper }}
|
108
|
+
</td>
|
109
|
+
<td>
|
110
|
+
{{ callers_cell(variant) }}
|
111
|
+
</td>
|
105
112
|
<td><span data-bs-toggle="tooltip" data-bs-html="true" title="{% for name, caller in variant.callers %}{{ name }}: {{ caller }}<br>{% endfor %}">
|
106
113
|
{{ variant.sub_category|upper }}</span></td>
|
107
114
|
<td>{{ variant.chromosome if variant.chromosome == variant.end_chrom else variant.chromosome+'-'+variant.end_chrom }}</td>
|
@@ -48,6 +48,20 @@
|
|
48
48
|
</script>
|
49
49
|
{% endmacro %}
|
50
50
|
|
51
|
+
|
52
|
+
{% macro callers_cell(variant) %}
|
53
|
+
{% for filter in variant.filters %} <!-- collect info from variant's filters (PASS) -->
|
54
|
+
<span class="badge bg-{{ filter.label_class }}" data-bs-toggle="tooltip" data-bs-placement="top" title="{{ filter.description }}">
|
55
|
+
{{ filter.label }}
|
56
|
+
</span>
|
57
|
+
{% endfor %}
|
58
|
+
{% for name, caller in variant.callers %} <!-- Collect info for specific callers -->
|
59
|
+
<span class="badge {% if caller == 'Pass' %}bg-success{% elif caller == 'Filtered' %}bg-secondary{% else %}bg-black{% endif %}" data-bs-toggle="tooltip" data-bs-html="true" title="{{caller}}">
|
60
|
+
{{ name }}
|
61
|
+
</span>
|
62
|
+
{% endfor %}
|
63
|
+
{% endmacro %}
|
64
|
+
|
51
65
|
{% macro mark_heteroplasmic_mt(individuals, samples) %}
|
52
66
|
{% for ind in individuals if ind.phenotype == 2 %}
|
53
67
|
{% for gt in samples|selectattr("sample_id", "equalto", ind.individual_id) %}
|
@@ -18,6 +18,7 @@ from .ldap_extension import LdapManager
|
|
18
18
|
from .loqus_extension import LoqusDB
|
19
19
|
from .matchmaker_extension import MatchMaker
|
20
20
|
from .mongo_extension import MongoDB
|
21
|
+
from .panelapp_extension import PanelAppClient
|
21
22
|
from .phenopacket_extension import PhenopacketAPI
|
22
23
|
from .rerunner_extension import RerunnerError, RerunnerService
|
23
24
|
|
@@ -39,3 +40,4 @@ config_igv_tracks = AlignTrackHandler()
|
|
39
40
|
bionano_access = BioNanoAccessAPI()
|
40
41
|
chanjo_report = ChanjoReport()
|
41
42
|
chanjo2 = Chanjo2Client()
|
43
|
+
panelapp = PanelAppClient()
|
@@ -0,0 +1,75 @@
|
|
1
|
+
import logging
|
2
|
+
from typing import Optional
|
3
|
+
|
4
|
+
import requests
|
5
|
+
|
6
|
+
API_PANELS_URL = "https://panelapp.genomicsengland.co.uk/api/v1/panels/"
|
7
|
+
|
8
|
+
LOG = logging.getLogger(__name__)
|
9
|
+
|
10
|
+
|
11
|
+
class PanelAppClient:
|
12
|
+
"""Class that retrieves PanelAll green genes using the NHS-NGS/panelapp library."""
|
13
|
+
|
14
|
+
def __init__(self):
|
15
|
+
self.panels_page = 1
|
16
|
+
self.panel_types = set()
|
17
|
+
self.panel_ids = []
|
18
|
+
|
19
|
+
def get_panel_types(self) -> list:
|
20
|
+
"""Returns available panel types, collected from processed panels"""
|
21
|
+
return sorted(list(self.panel_types))
|
22
|
+
|
23
|
+
def get_panels(self, page: int, signed_off: bool = False) -> Optional[dict]:
|
24
|
+
"""Return a dictionary {panel_id: Panelapp.Panel} with all panels, signed off or not."""
|
25
|
+
|
26
|
+
panels_url = f"{API_PANELS_URL}?page={page}"
|
27
|
+
if signed_off:
|
28
|
+
panels_url = f"{API_PANELS_URL}signedoff/?page={page}"
|
29
|
+
|
30
|
+
resp = requests.get(panels_url, headers={"Content-Type": "application/json"})
|
31
|
+
if not resp.ok:
|
32
|
+
resp.raise_for_status()
|
33
|
+
return
|
34
|
+
|
35
|
+
return resp.json()
|
36
|
+
|
37
|
+
def set_panel_types(self, json_panels: dict):
|
38
|
+
"""Collect available panel types from a page of panels and add them to the self.panel_types variable."""
|
39
|
+
|
40
|
+
for panel in json_panels.get("results", []):
|
41
|
+
for type in panel.get("types", []):
|
42
|
+
self.panel_types.add(type["slug"])
|
43
|
+
|
44
|
+
def get_panel_ids(self, signed_off: bool) -> list[int]:
|
45
|
+
"""Returns a list of panel ids contained in a json document with gene panels data."""
|
46
|
+
|
47
|
+
def get_ids(json_panels):
|
48
|
+
LOG.info(f"Retrieving IDs from API page {self.panels_page}")
|
49
|
+
for panel in json_panels.get("results", []):
|
50
|
+
self.panel_ids.append(panel["id"])
|
51
|
+
self.panels_page += 1
|
52
|
+
|
53
|
+
json_panels: dict = self.get_panels(
|
54
|
+
signed_off=signed_off, page=self.panels_page
|
55
|
+
) # first page of results
|
56
|
+
get_ids(json_panels=json_panels)
|
57
|
+
self.set_panel_types(json_panels=json_panels)
|
58
|
+
|
59
|
+
# Iterate over remaining pages of results
|
60
|
+
while json_panels["next"] is not None:
|
61
|
+
json_panels = self.get_panels(signed_off=signed_off, page=self.panels_page)
|
62
|
+
get_ids(json_panels=json_panels)
|
63
|
+
self.set_panel_types(json_panels=json_panels)
|
64
|
+
|
65
|
+
return self.panel_ids
|
66
|
+
|
67
|
+
def get_panel(self, panel_id: str) -> Optional[dict]:
|
68
|
+
"""Retrieve a gene_panel. Apply filters on panel type, if available."""
|
69
|
+
panel_url = f"{API_PANELS_URL}{panel_id}"
|
70
|
+
resp = requests.get(panel_url, headers={"Content-Type": "application/json"})
|
71
|
+
if not resp.ok:
|
72
|
+
resp.raise_for_status()
|
73
|
+
return
|
74
|
+
|
75
|
+
return resp.json()
|
scout/server/links.py
CHANGED
@@ -355,7 +355,7 @@ def add_tx_links(tx_obj, build=37, hgnc_symbol=None):
|
|
355
355
|
return tx_obj
|
356
356
|
|
357
357
|
|
358
|
-
|
358
|
+
# Transcript links
|
359
359
|
def refseq(refseq_id):
|
360
360
|
link = "http://www.ncbi.nlm.nih.gov/nuccore/{}"
|
361
361
|
if not refseq_id:
|
@@ -480,6 +480,7 @@ def get_variant_links(institute_obj: dict, variant_obj: dict, build: int = None)
|
|
480
480
|
swegen_link=swegen_link(variant_obj),
|
481
481
|
cosmic_links=cosmic_links(variant_obj),
|
482
482
|
beacon_link=beacon_link(variant_obj, build),
|
483
|
+
franklin_link=franklin_link(variant_obj, build),
|
483
484
|
ucsc_link=ucsc_link(variant_obj, build),
|
484
485
|
decipher_link=decipher_link(variant_obj, build),
|
485
486
|
ensembl_link=ensembl_link(variant_obj, build),
|
@@ -633,6 +634,23 @@ def swegen_link(variant_obj):
|
|
633
634
|
return url_template.format(this=variant_obj)
|
634
635
|
|
635
636
|
|
637
|
+
def franklin_link(variant_obj: dict, build: int = 37):
|
638
|
+
"""Compose link to Franklin Variant Frequency Database."""
|
639
|
+
if variant_obj["category"] in ["cancer", "cancer_sv"]:
|
640
|
+
url_template = (
|
641
|
+
"https://franklin.genoox.com/clinical-db/variant/snpTumor/chr{this[chromosome]}-"
|
642
|
+
"{this[position]}-{this[reference]}-{this[alternative]}"
|
643
|
+
)
|
644
|
+
else:
|
645
|
+
url_template = (
|
646
|
+
"https://franklin.genoox.com/clinical-db/variant/snp/chr{this[chromosome]}-"
|
647
|
+
"{this[position]}-{this[reference]}-{this[alternative]}"
|
648
|
+
)
|
649
|
+
if build == 38:
|
650
|
+
url_template += "-hg38"
|
651
|
+
return url_template.format(this=variant_obj)
|
652
|
+
|
653
|
+
|
636
654
|
def cosmic_links(variant_obj):
|
637
655
|
"""Compose link to COSMIC Database.
|
638
656
|
|
scout/server/utils.py
CHANGED
@@ -297,51 +297,43 @@ def _check_path_name(ind: Dict, path_name: str) -> bool:
|
|
297
297
|
return False
|
298
298
|
|
299
299
|
|
300
|
-
def case_append_alignments(case_obj):
|
300
|
+
def case_append_alignments(case_obj: dict):
|
301
301
|
"""Deconvolute information about files to case_obj.
|
302
302
|
|
303
|
-
This function prepares
|
304
|
-
the templates.
|
305
|
-
|
306
|
-
Loops over the the individuals and gather bam/cram files, indexes and sample display names in
|
307
|
-
lists
|
308
|
-
|
309
|
-
Args:
|
310
|
-
case_obj(scout.models.Case)
|
303
|
+
This function prepares bam/cram files and their indexes for easy access in templates.
|
311
304
|
"""
|
312
305
|
unwrap_settings = [
|
313
306
|
{"path": "bam_file", "append_to": "bam_files", "index": "bai_files"},
|
314
307
|
{"path": "mt_bam", "append_to": "mt_bams", "index": "mt_bais"},
|
315
|
-
{"path": "rhocall_bed", "append_to": "rhocall_beds", "index":
|
316
|
-
{"path": "rhocall_wig", "append_to": "rhocall_wigs", "index":
|
317
|
-
{
|
318
|
-
|
319
|
-
|
320
|
-
"index": "no_index",
|
321
|
-
},
|
322
|
-
{"path": "upd_sites_bed", "append_to": "upd_sites_beds", "index": "no_index"},
|
323
|
-
{
|
324
|
-
"path": "tiddit_coverage_wig",
|
325
|
-
"append_to": "tiddit_coverage_wigs",
|
326
|
-
"index": "no_index",
|
327
|
-
},
|
308
|
+
{"path": "rhocall_bed", "append_to": "rhocall_beds", "index": None},
|
309
|
+
{"path": "rhocall_wig", "append_to": "rhocall_wigs", "index": None},
|
310
|
+
{"path": "upd_regions_bed", "append_to": "upd_regions_beds", "index": None},
|
311
|
+
{"path": "upd_sites_bed", "append_to": "upd_sites_beds", "index": None},
|
312
|
+
{"path": "tiddit_coverage_wig", "append_to": "tiddit_coverage_wigs", "index": None},
|
328
313
|
]
|
329
314
|
|
330
|
-
|
315
|
+
def process_file(case_obj, individual, setting):
|
316
|
+
"""Process a single file and its optional index."""
|
317
|
+
file_path = individual.get(setting["path"])
|
331
318
|
append_safe(
|
332
319
|
case_obj,
|
333
|
-
"
|
334
|
-
|
320
|
+
setting["append_to"],
|
321
|
+
file_path if file_path and os.path.exists(file_path) else "missing",
|
335
322
|
)
|
323
|
+
if setting["index"]:
|
324
|
+
index_path = (
|
325
|
+
find_index(file_path) if file_path and os.path.exists(file_path) else "missing"
|
326
|
+
)
|
327
|
+
append_safe(case_obj, setting["index"], index_path)
|
328
|
+
|
329
|
+
for individual in case_obj["individuals"]:
|
330
|
+
# Add sample name
|
331
|
+
sample_name = f"{case_obj.get('display_name', '')} - {individual.get('display_name', '')}"
|
332
|
+
append_safe(case_obj, "sample_names", sample_name)
|
333
|
+
|
334
|
+
# Process all file settings
|
336
335
|
for setting in unwrap_settings:
|
337
|
-
|
338
|
-
if not (file_path and os.path.exists(file_path)):
|
339
|
-
continue
|
340
|
-
append_safe(case_obj, setting["append_to"], file_path)
|
341
|
-
if not setting["index"] == "no_index":
|
342
|
-
append_safe(
|
343
|
-
case_obj, setting["index"], find_index(file_path)
|
344
|
-
) # either bai_files or mt_bais
|
336
|
+
process_file(case_obj, individual, setting)
|
345
337
|
|
346
338
|
|
347
339
|
def append_safe(obj, obj_index, elem):
|