scout-browser 4.95.0__py3-none-any.whl → 4.97.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/case.py +75 -70
- scout/adapter/mongo/filter.py +28 -11
- scout/adapter/mongo/institute.py +2 -0
- scout/adapter/mongo/omics_variant.py +20 -5
- scout/adapter/mongo/query.py +104 -95
- scout/adapter/mongo/variant.py +0 -5
- scout/adapter/mongo/variant_loader.py +10 -12
- scout/build/case.py +3 -1
- scout/build/individual.py +3 -11
- scout/commands/delete/delete_command.py +87 -49
- scout/commands/load/research.py +4 -4
- scout/commands/load/variants.py +25 -8
- scout/commands/setup/setup_scout.py +1 -1
- scout/commands/update/case.py +12 -0
- scout/commands/update/individual.py +1 -2
- scout/constants/__init__.py +7 -2
- scout/constants/acmg.py +25 -18
- scout/constants/file_types.py +68 -119
- scout/constants/filters.py +2 -1
- scout/constants/gene_tags.py +3 -3
- scout/constants/igv_tracks.py +7 -11
- scout/constants/query_terms.py +2 -2
- scout/demo/643594.config.yaml +6 -0
- scout/demo/643594.peddy.ped +1 -1
- scout/demo/643594.somalier.ancestry.tsv +4 -0
- scout/demo/643594.somalier.pairs.tsv +4 -0
- scout/demo/643594.somalier.samples.tsv +4 -0
- scout/demo/cancer.load_config.yaml +2 -3
- scout/demo/resources/__init__.py +1 -1
- scout/demo/resources/gnomad.v4.1.constraint_metrics_reduced.tsv +3755 -0
- scout/demo/rnafusion.load_config.yaml +1 -0
- scout/exceptions/database.py +1 -1
- scout/load/all.py +8 -16
- scout/models/case/case.py +1 -0
- scout/models/case/case_loading_models.py +15 -5
- scout/models/managed_variant.py +3 -3
- scout/models/omics_variant.py +3 -3
- scout/parse/case.py +113 -5
- scout/parse/pedqc.py +127 -0
- scout/parse/variant/frequency.py +9 -6
- scout/parse/variant/variant.py +71 -39
- scout/server/app.py +14 -0
- scout/server/blueprints/alignviewers/controllers.py +2 -0
- scout/server/blueprints/alignviewers/templates/alignviewers/igv_viewer.html +3 -0
- scout/server/blueprints/alignviewers/templates/alignviewers/utils.html +1 -1
- scout/server/blueprints/cases/controllers.py +25 -3
- scout/server/blueprints/cases/templates/cases/case.html +3 -0
- scout/server/blueprints/cases/templates/cases/case_report.html +28 -2
- scout/server/blueprints/cases/templates/cases/chanjo2_form.html +2 -2
- scout/server/blueprints/cases/templates/cases/collapsible_actionbar.html +12 -0
- scout/server/blueprints/cases/templates/cases/gene_panel.html +9 -3
- scout/server/blueprints/cases/templates/cases/individuals_table.html +4 -1
- scout/server/blueprints/cases/templates/cases/utils.html +23 -19
- scout/server/blueprints/cases/views.py +5 -9
- scout/server/blueprints/clinvar/controllers.py +12 -11
- scout/server/blueprints/clinvar/templates/clinvar/clinvar_submissions.html +10 -14
- scout/server/blueprints/clinvar/templates/clinvar/multistep_add_variant.html +15 -7
- scout/server/blueprints/clinvar/views.py +18 -31
- scout/server/blueprints/institutes/controllers.py +20 -1
- scout/server/blueprints/institutes/forms.py +5 -1
- scout/server/blueprints/institutes/templates/overview/institute_settings.html +7 -0
- scout/server/blueprints/institutes/templates/overview/utils.html +20 -1
- scout/server/blueprints/omics_variants/templates/omics_variants/outliers.html +9 -2
- scout/server/blueprints/omics_variants/views.py +8 -10
- scout/server/blueprints/variant/controllers.py +30 -1
- scout/server/blueprints/variant/templates/variant/cancer-variant.html +21 -5
- scout/server/blueprints/variant/templates/variant/components.html +26 -9
- scout/server/blueprints/variant/templates/variant/variant.html +4 -2
- scout/server/blueprints/variant/templates/variant/variant_details.html +1 -1
- scout/server/blueprints/variant/utils.py +2 -0
- scout/server/blueprints/variant/views.py +10 -3
- scout/server/blueprints/variants/controllers.py +29 -3
- scout/server/blueprints/variants/forms.py +37 -10
- scout/server/blueprints/variants/templates/variants/cancer-variants.html +5 -4
- scout/server/blueprints/variants/templates/variants/components.html +12 -10
- scout/server/blueprints/variants/templates/variants/str-variants.html +13 -9
- scout/server/blueprints/variants/templates/variants/utils.html +59 -36
- scout/server/blueprints/variants/views.py +45 -60
- scout/server/extensions/beacon_extension.py +1 -1
- scout/server/extensions/bionano_extension.py +5 -5
- scout/server/extensions/chanjo2_extension.py +40 -1
- scout/server/extensions/chanjo_extension.py +1 -1
- scout/server/extensions/clinvar_extension.py +56 -2
- scout/server/extensions/matchmaker_extension.py +1 -1
- scout/server/links.py +0 -14
- scout/server/static/bs_styles.css +2 -0
- scout/server/templates/layout.html +1 -0
- scout/server/utils.py +5 -0
- scout/utils/acmg.py +5 -5
- scout/utils/ensembl_biomart_clients.py +2 -11
- scout/utils/scout_requests.py +1 -1
- {scout_browser-4.95.0.dist-info → scout_browser-4.97.0.dist-info}/METADATA +1 -1
- {scout_browser-4.95.0.dist-info → scout_browser-4.97.0.dist-info}/RECORD +96 -94
- scout/demo/resources/gnomad.v4.0.constraint_metrics_reduced.tsv +0 -3755
- scout/parse/peddy.py +0 -149
- scout/utils/sort.py +0 -21
- {scout_browser-4.95.0.dist-info → scout_browser-4.97.0.dist-info}/WHEEL +0 -0
- {scout_browser-4.95.0.dist-info → scout_browser-4.97.0.dist-info}/entry_points.txt +0 -0
- {scout_browser-4.95.0.dist-info → scout_browser-4.97.0.dist-info}/licenses/LICENSE +0 -0
@@ -305,6 +305,7 @@ def set_sample_tracks(display_obj: dict, case_groups: list, chromosome: str):
|
|
305
305
|
|
306
306
|
A missing file is indicated with the string "missing", and no track is made for such entries.
|
307
307
|
"""
|
308
|
+
|
308
309
|
sample_tracks = []
|
309
310
|
|
310
311
|
track_items = "mt_bams" if chromosome == "M" else "bam_files"
|
@@ -330,6 +331,7 @@ def set_sample_tracks(display_obj: dict, case_groups: list, chromosome: str):
|
|
330
331
|
"indexURL": case[track_index_items][count],
|
331
332
|
"format": case[track_items][count].split(".")[-1], # "bam" or "cram"
|
332
333
|
"height": 700,
|
334
|
+
"show_soft_clips": case["track_items_soft_clips_settings"][count],
|
333
335
|
}
|
334
336
|
)
|
335
337
|
display_obj["sample_tracks"] = sample_tracks
|
@@ -116,6 +116,9 @@
|
|
116
116
|
url: "{{ url_for('alignviewers.remote_static', file=track.url) }}",
|
117
117
|
indexURL: "{{ url_for('alignviewers.remote_static', file=track.indexURL) }}",
|
118
118
|
sourceType: "file",
|
119
|
+
groupBy: "tag:HP",
|
120
|
+
colorBy: "basemod2:m",
|
121
|
+
showSoftClips: {{track.show_soft_clips | lower }},
|
119
122
|
format: "{{ track.format }}",
|
120
123
|
height: "{{track.height}}"
|
121
124
|
},
|
@@ -1,5 +1,5 @@
|
|
1
1
|
{% macro igv_script() %}
|
2
2
|
<link rel="shortcut icon" href="//igv.org/web/img/favicon.ico">
|
3
3
|
<!-- IGV JS-->
|
4
|
-
<script src="https://cdn.jsdelivr.net/npm/igv@3.
|
4
|
+
<script src="https://cdn.jsdelivr.net/npm/igv@3.2.0/dist/igv.min.js" integrity="sha512-MHnbGQeONlQXyEs6PgiW2bhwywJW5IwUnRKfQKrPaVSrzopctBTU1VtOiEXMf/ZPBk47eFimlVRxdff+sdsyAg==" crossorigin="anonymous"></script>
|
5
5
|
{% endmacro %}
|
@@ -13,6 +13,7 @@ from flask_login import current_user
|
|
13
13
|
from requests.auth import HTTPBasicAuth
|
14
14
|
from xlsxwriter import Workbook
|
15
15
|
|
16
|
+
from scout import __version__
|
16
17
|
from scout.adapter import MongoAdapter
|
17
18
|
from scout.constants import (
|
18
19
|
CANCER_PHENOTYPE_MAP,
|
@@ -757,6 +758,7 @@ def case_report_content(store: MongoAdapter, institute_obj: dict, case_obj: dict
|
|
757
758
|
data["manual_rank_options"] = MANUAL_RANK_OPTIONS
|
758
759
|
data["genetic_models"] = dict(GENETIC_MODELS)
|
759
760
|
data["report_created_at"] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
|
761
|
+
data["current_scout_version"] = __version__
|
760
762
|
|
761
763
|
case_report_variants(store, case_obj, institute_obj, data)
|
762
764
|
|
@@ -1086,11 +1088,12 @@ def update_default_panels(store, current_user, institute_id, case_name, panel_id
|
|
1086
1088
|
store.update_default_panels(institute_obj, case_obj, user_obj, link, panel_objs)
|
1087
1089
|
|
1088
1090
|
|
1089
|
-
def update_clinical_filter_hpo(store, current_user,
|
1091
|
+
def update_clinical_filter_hpo(store, current_user, institute_obj, case_obj, hpo_clinical_filter):
|
1090
1092
|
"""Update HPO clinical filter use for a case."""
|
1091
|
-
institute_obj, case_obj = institute_and_case(store, institute_id, case_name)
|
1092
1093
|
user_obj = store.user(current_user.email)
|
1093
|
-
link = url_for(
|
1094
|
+
link = url_for(
|
1095
|
+
"cases.case", institute_id=institute_obj["_id"], case_name=case_obj["display_name"]
|
1096
|
+
)
|
1094
1097
|
store.update_clinical_filter_hpo(institute_obj, case_obj, user_obj, link, hpo_clinical_filter)
|
1095
1098
|
|
1096
1099
|
|
@@ -1491,3 +1494,22 @@ def add_link_for_disease(case_obj: dict):
|
|
1491
1494
|
for diagnosis in case_diagnoses:
|
1492
1495
|
#: Add link
|
1493
1496
|
diagnosis.update({"disease_link": disease_link(disease_id=diagnosis["disease_id"])})
|
1497
|
+
|
1498
|
+
|
1499
|
+
def remove_dynamic_genes(store: dict, case_obj: dict, institute_obj: dict, request_form: dict):
|
1500
|
+
"""Remove one or more genes from the dynamic gene list. If there are no more
|
1501
|
+
genes on the list, also stop using the HPO panel for clinical filter."""
|
1502
|
+
case_dynamic_genes = [dyn_gene["hgnc_id"] for dyn_gene in case_obj.get("dynamic_gene_list")]
|
1503
|
+
genes_to_remove = [int(gene_id) for gene_id in request_form.getlist("dynamicGene")]
|
1504
|
+
hgnc_ids = list(set(case_dynamic_genes) - set(genes_to_remove))
|
1505
|
+
store.update_dynamic_gene_list(
|
1506
|
+
case_obj,
|
1507
|
+
hgnc_ids=hgnc_ids,
|
1508
|
+
delete_only=True,
|
1509
|
+
)
|
1510
|
+
if not hgnc_ids:
|
1511
|
+
hpo_clinical_filter = False
|
1512
|
+
case_obj["hpo_clinical_filter"] = hpo_clinical_filter
|
1513
|
+
update_clinical_filter_hpo(
|
1514
|
+
store, current_user, institute_obj, case_obj, hpo_clinical_filter
|
1515
|
+
)
|
@@ -295,6 +295,9 @@
|
|
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 %}
|
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
|
+
{% endif %}
|
298
301
|
</div>
|
299
302
|
</div>
|
300
303
|
</div>
|
@@ -63,7 +63,7 @@
|
|
63
63
|
</nav>
|
64
64
|
|
65
65
|
<div id="container" class="container">
|
66
|
-
<h4>Scout case analysis report</h4> - created on: <strong>{{report_created_at}}</strong><br><br>
|
66
|
+
<h4>Scout case analysis report</h4> - created on: <strong>{{report_created_at}}</strong> using <strong>Scout v.{{current_scout_version}}</strong><br><br>
|
67
67
|
{{ phenotype_panel() }}
|
68
68
|
{{ gene_panels_panel()}}
|
69
69
|
{{ causatives_panel()}}
|
@@ -101,8 +101,11 @@
|
|
101
101
|
<td>
|
102
102
|
<table class="table table-sm">
|
103
103
|
<tr>
|
104
|
-
|
104
|
+
<td>Case created: <strong>{{case.created_at.strftime('%Y-%m-%d')}}</strong></td>
|
105
105
|
<td>Last updated: <strong>{{case.updated_at.strftime('%Y-%m-%d') if case.updated_at else "n.a." }}</strong></td>
|
106
|
+
{% if case.scout_load_version %}
|
107
|
+
<td> Loaded with <strong>Scout v.{{case.scout_load_version}}</strong></td>
|
108
|
+
{% endif %}
|
106
109
|
</tr>
|
107
110
|
</table>
|
108
111
|
<br>
|
@@ -746,12 +749,34 @@
|
|
746
749
|
</tr>
|
747
750
|
</tbody>
|
748
751
|
</table>
|
752
|
+
<table id="severity-table" class="table table-sm" style="background-color: transparent">
|
753
|
+
<thead>
|
754
|
+
<tr>
|
755
|
+
<th>SIFT</th>
|
756
|
+
<th>REVEL score</th>
|
757
|
+
<th>REVEL rank score</th>
|
758
|
+
<th>Polyphen</th>
|
759
|
+
<th>SPIDEX</th>
|
760
|
+
<th>SpliceAI DS max</th>
|
761
|
+
</thead>
|
762
|
+
<tbody>
|
763
|
+
<tr>
|
764
|
+
<td>{{ variant.sift_predictions|join(', ') or '-'}}</td>
|
765
|
+
<td>{{ variant.revel or '-' }}</td>
|
766
|
+
<td>{{ variant.revel_score or '-' }}</td>
|
767
|
+
<td>{{ variant.polyphen_predictions|join(', ') or '-' }}</td>
|
768
|
+
<td>{{ variant.spidex|spidex_human if variant.spidex else none|spidex_human }}</td>
|
769
|
+
<td>{{ variant.spliceai_scores|join(', ') or '-' }}</td>
|
770
|
+
</tr>
|
771
|
+
</tbody>
|
772
|
+
</table>
|
749
773
|
<table id="panel-table" class="table table-sm" style="background-color: transparent">
|
750
774
|
<thead>
|
751
775
|
<tr>
|
752
776
|
<th>Affected gene(s)</th>
|
753
777
|
<th>Description</th>
|
754
778
|
<th>Region annotation</th>
|
779
|
+
<th>Consequence</th>
|
755
780
|
<th>Transcript - HGVS - Protein</th>
|
756
781
|
<th>Diagnoses [inheritance]</th>
|
757
782
|
</tr>
|
@@ -767,6 +792,7 @@
|
|
767
792
|
</td>
|
768
793
|
<td>{{gene.description|title}}</td>
|
769
794
|
<td>{{gene.region_annotation}}</td>
|
795
|
+
<td>{{gene.functional_annotation.replace("_"," ")|truncate(20, True)}}</td>
|
770
796
|
<td>
|
771
797
|
{{ variant_transcripts(gene) }}
|
772
798
|
</td>
|
@@ -1,4 +1,4 @@
|
|
1
|
-
{% macro chanjo2_report_form(institute_obj, case_obj, panel_name, report_type, hgnc_ids, link_text) %}
|
1
|
+
{% macro chanjo2_report_form(institute_obj, case_obj, panel_name, report_type, hgnc_ids, link_text, link_class = "link-primary") %}
|
2
2
|
|
3
3
|
{% set form_name = "chanjo2_"+panel_name+"_"+report_type %}
|
4
4
|
{% set form_action = config.CHANJO2_URL+"/"+report_type %}
|
@@ -40,7 +40,7 @@
|
|
40
40
|
{% if panel_name == "HPO Panel" %}
|
41
41
|
<button type="submit" class="btn btn-secondary btn-sm text-white"> Coverage {{report_type}} (Chanjo2) </button>
|
42
42
|
{% else %}
|
43
|
-
<a class="
|
43
|
+
<a class="{{ link_class }}" onclick="this.closest('form').submit();return false;">{{link_text}}</a>
|
44
44
|
{% endif %}
|
45
45
|
</form>
|
46
46
|
|
@@ -15,6 +15,7 @@
|
|
15
15
|
{{ reports(institute,case, report_types) }}
|
16
16
|
{{ default_gene_panel(institute, case) }}
|
17
17
|
{{ analysis_date(case) }}
|
18
|
+
{{ software_version(case) }}
|
18
19
|
{{ genome_build(case, case_groups, has_rna_tracks) }}
|
19
20
|
{{ rank_model(case) }}
|
20
21
|
<li class="list-group-item bg-dark sidebar-separator menu-collapsed"></li>
|
@@ -190,6 +191,17 @@
|
|
190
191
|
{% endif %}
|
191
192
|
{% endmacro %}
|
192
193
|
|
194
|
+
{% macro software_version(case) %}
|
195
|
+
{% if case.scout_load_version%}
|
196
|
+
<div href="#" class="bg-dark list-group-item text-white" data-bs-toggle="tooltip" title="Scout version used for loading the case" data-bs-placement="right">
|
197
|
+
<div class="d-flex w-100 justify-content-start align-items-center">
|
198
|
+
<span class="fa fa-gear fa-fw me-3"></span>
|
199
|
+
<span class="menu-collapsed">v.{{ case.scout_load_version }}</span>
|
200
|
+
</div>
|
201
|
+
</div>
|
202
|
+
{% endif %}
|
203
|
+
{% endmacro %}
|
204
|
+
|
193
205
|
{% macro genome_build(case, case_groups, has_rna_tracks) %}
|
194
206
|
<div href="#" class="bg-dark list-group-item text-white">
|
195
207
|
<div class="d-flex w-100 justify-content-start align-items-center">
|
@@ -79,8 +79,7 @@
|
|
79
79
|
<span class="mr-3" data-bs-toggle="tooltip" data-bs-placement="bottom" title="{{ case.dynamic_panel_phenotypes|join(', ') }}">
|
80
80
|
{{ case.dynamic_panel_phenotypes|length }} phenotypes
|
81
81
|
</span>
|
82
|
-
{% endif %}
|
83
|
-
)
|
82
|
+
{% endif %})
|
84
83
|
{% if case.dynamic_gene_list_edited %} <span class="text-danger ml-3"> (edited) </span> {% endif %}
|
85
84
|
</h6>
|
86
85
|
</div>
|
@@ -119,6 +118,7 @@
|
|
119
118
|
</div>
|
120
119
|
|
121
120
|
<div>
|
121
|
+
{% if case.track == 'rare' %}
|
122
122
|
<div class="row"> <!-- Show variants in dynamic gene list -->
|
123
123
|
<form action="{{ url_for('cases.update_clinical_filter_hpo', institute_id=institute._id, case_name=case.display_name)+'#hpo_clinical_filter' }}" method="POST">
|
124
124
|
<div class="form-check form-switch">
|
@@ -128,6 +128,7 @@
|
|
128
128
|
</div>
|
129
129
|
</form>
|
130
130
|
</div>
|
131
|
+
{% endif %}
|
131
132
|
<div class="row mt-3">
|
132
133
|
<div class="btn-group btn-group-sm">
|
133
134
|
{% if case.track == 'cancer' %}
|
@@ -180,7 +181,12 @@
|
|
180
181
|
Clinical HPO MEIs
|
181
182
|
</a>
|
182
183
|
{% endif %}
|
183
|
-
|
184
|
+
{% if case.has_outliers %}
|
185
|
+
<a class="btn btn-secondary btn-sm text-white" href="{{ url_for('omics_variants.outliers',
|
186
|
+
institute_id=institute._id, case_name=case.display_name, variant_type='clinical',
|
187
|
+
gene_panels=['hpo']) }}" target="_blank" rel="noopener">Clinical HPO WTS</a>
|
188
|
+
{% endif %}
|
189
|
+
{% endif %}
|
184
190
|
</div>
|
185
191
|
</div>
|
186
192
|
{% if case.chanjo_coverage or case.chanjo2_coverage and case.dynamic_gene_list|length > 0 %}
|
@@ -17,7 +17,9 @@
|
|
17
17
|
title="Measure of the tumor mutational burden">TMB</th>
|
18
18
|
<th data-bs-toggle='tooltip' data-bs-container='body' class="col-xs-1"
|
19
19
|
title="Measure of microsatellite instability">MSI</th>
|
20
|
-
|
20
|
+
<th data-bs-toggle='tooltip' data-bs-container='body' class="col-xs-1"
|
21
|
+
title="Homologous Recombination Deficiency">HRD</th>
|
22
|
+
<th data-bs-toggle='tooltip' data-bs-container='body' class="col-xs-1"
|
21
23
|
title="Measure of tumor purity">Tumor Purity</th>
|
22
24
|
<th data-bs-toggle='tooltip' data-bs-container='body' class="col-xs-1"
|
23
25
|
title="Downloadable CytoSure file">CGH</th>
|
@@ -41,6 +43,7 @@
|
|
41
43
|
<td>{{ ind.analysis_type|upper }}</td>
|
42
44
|
<td>{{ ind.tmb or 'N/A' }}</td>
|
43
45
|
<td>{{ ind.msi or 'N/A' }}</td>
|
46
|
+
<td>{{ ind.hrd or 'N/A' }}</td>
|
44
47
|
{% if ind.phenotype_human == "tumor" %}
|
45
48
|
<td>
|
46
49
|
<div class="d-flex align-items-center">
|
@@ -291,29 +291,33 @@
|
|
291
291
|
|
292
292
|
{% macro pretty_variant(variant) %}
|
293
293
|
|
294
|
-
{% if variant.category
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
{%
|
299
|
-
|
300
|
-
{%
|
301
|
-
{
|
302
|
-
|
303
|
-
{
|
304
|
-
|
305
|
-
{
|
294
|
+
{% if variant.category %}
|
295
|
+
{% if variant.category in "str" %}
|
296
|
+
{{ variant.str_repid }} {{ variant.alternative | replace("<", " ") | replace(">", "")}}
|
297
|
+
|
298
|
+
{% elif variant.category in ("snv", "cancer") %}
|
299
|
+
{% set display_genes = [] %}
|
300
|
+
{% for gene in variant.genes %}
|
301
|
+
{% if gene.hgvs_identifier and gene.hgnc_symbol %}
|
302
|
+
{{ "" if display_genes.append(gene.hgnc_symbol + ' ' + gene.hgvs_identifier|truncate(20,True)) }}
|
303
|
+
{% elif gene.hgnc_symbol %}
|
304
|
+
{{ "" if display_genes.append(gene.hgnc_symbol) }}
|
305
|
+
{% elif gene.hgvs_identifier and gene.hgnc_id %}
|
306
|
+
{{ "" if display_genes.append( gene.hgnc_id|string + ' ' + gene.hgvs_identifier|truncate(20,True)) }}
|
307
|
+
{% endif %}
|
308
|
+
{% endfor %}
|
309
|
+
|
310
|
+
{% if not display_genes %}
|
311
|
+
{{ "" if display_genes.append( variant.simple_id|truncate(40,True) ) }}
|
306
312
|
{% endif %}
|
307
|
-
{% endfor %}
|
308
313
|
|
309
|
-
|
310
|
-
{{ "" if display_genes.append( variant.simple_id|truncate(40,True) ) }}
|
311
|
-
{% endif %}
|
314
|
+
{{ display_genes|join(", ") }}
|
312
315
|
|
313
|
-
{
|
314
|
-
|
315
|
-
{
|
316
|
+
{% else %} <!-- structural variants -->
|
317
|
+
{{ variant.sub_category|upper }}({{ variant.chromosome }}{{ variant.cytoband_start }}-{{ variant.end_chrom }}{{ variant.cytoband_end }})
|
318
|
+
{% endif %}
|
316
319
|
{% endif %}
|
320
|
+
|
317
321
|
{% endmacro %}
|
318
322
|
|
319
323
|
{% macro pretty_link_variant(variant, case) %}
|
@@ -486,6 +486,7 @@ def phenotypes_actions(institute_id, case_name):
|
|
486
486
|
hpo_ids = request.form.getlist("hpo_id")
|
487
487
|
user_obj = store.user(current_user.email)
|
488
488
|
|
489
|
+
### match action for 3.10
|
489
490
|
if action == "PHENOMIZER":
|
490
491
|
diseases = controllers.phenomizer_diseases(hpo_ids, case_obj)
|
491
492
|
if diseases:
|
@@ -511,14 +512,8 @@ def phenotypes_actions(institute_id, case_name):
|
|
511
512
|
)
|
512
513
|
store.update_dynamic_gene_list(case_obj, hgnc_ids=list(hgnc_ids), add_only=True)
|
513
514
|
|
514
|
-
if action == "REMOVEGENES":
|
515
|
-
|
516
|
-
genes_to_remove = [int(gene_id) for gene_id in request.form.getlist("dynamicGene")]
|
517
|
-
store.update_dynamic_gene_list(
|
518
|
-
case_obj,
|
519
|
-
hgnc_ids=list(set(case_dynamic_genes) - set(genes_to_remove)),
|
520
|
-
delete_only=True,
|
521
|
-
)
|
515
|
+
if action == "REMOVEGENES":
|
516
|
+
controllers.remove_dynamic_genes(store, case_obj, institute_obj, request.form)
|
522
517
|
|
523
518
|
if action == "GENES":
|
524
519
|
hgnc_symbols = parse_raw_gene_symbols(request.form.getlist("genes"))
|
@@ -928,9 +923,10 @@ def default_panels(institute_id, case_name):
|
|
928
923
|
@cases_bp.route("/<institute_id>/<case_name>/update-clinical-filter-hpo", methods=["POST"])
|
929
924
|
def update_clinical_filter_hpo(institute_id, case_name):
|
930
925
|
"""Update default panels for a case."""
|
926
|
+
institute_obj, case_obj = institute_and_case(store, institute_id, case_name)
|
931
927
|
hpo_clinical_filter = request.form.get("hpo_clinical_filter")
|
932
928
|
controllers.update_clinical_filter_hpo(
|
933
|
-
store, current_user,
|
929
|
+
store, current_user, institute_obj, case_obj, hpo_clinical_filter
|
934
930
|
)
|
935
931
|
return redirect(request.referrer)
|
936
932
|
|
@@ -2,7 +2,7 @@ import csv
|
|
2
2
|
import logging
|
3
3
|
from datetime import datetime
|
4
4
|
from tempfile import NamedTemporaryFile
|
5
|
-
from typing import List, Optional
|
5
|
+
from typing import List, Optional, Tuple
|
6
6
|
|
7
7
|
from flask import flash
|
8
8
|
from flask_login import current_user
|
@@ -17,6 +17,7 @@ from scout.constants.clinvar import (
|
|
17
17
|
)
|
18
18
|
from scout.constants.variant_tags import MANUAL_RANK_OPTIONS
|
19
19
|
from scout.models.clinvar import clinvar_variant
|
20
|
+
from scout.server.blueprints.variant.utils import add_gene_info
|
20
21
|
from scout.server.extensions import clinvar_api, store
|
21
22
|
from scout.utils.hgvs import validate_hgvs
|
22
23
|
from scout.utils.scout_requests import fetch_refseq_version
|
@@ -26,18 +27,15 @@ from .form import CaseDataForm, SNVariantForm, SVariantForm
|
|
26
27
|
LOG = logging.getLogger(__name__)
|
27
28
|
|
28
29
|
|
29
|
-
def _get_var_tx_hgvs(case_obj, variant_obj):
|
30
|
-
"""Retrieve all transcripts / hgvs for a given variant
|
31
|
-
|
32
|
-
case_obj(scout.models.Case)
|
33
|
-
variant_obj(scout.models.Variant)
|
34
|
-
Returns:
|
35
|
-
list of tuples. example: [("NM_002340.6:c.1840C>T", "NM_002340.6:c.1840C>T (validated)" ), ("NM_001145436.2:c.1840C>T", "NM_001145436.2:c.1840C>T"), .. ]
|
36
|
-
"""
|
30
|
+
def _get_var_tx_hgvs(case_obj: dict, variant_obj: dict) -> List[Tuple[str, str]]:
|
31
|
+
"""Retrieve all transcripts / hgvs for a given variant."""
|
32
|
+
|
37
33
|
build = str(case_obj.get("genome_build", "37"))
|
38
34
|
tx_hgvs_list = [("", "Do not specify")]
|
35
|
+
add_gene_info(store, variant_obj, genome_build=build)
|
39
36
|
for gene in variant_obj.get("genes", []):
|
40
37
|
for tx in gene.get("transcripts", []):
|
38
|
+
mane_select = tx.get("mane_select_transcript")
|
41
39
|
if all([tx.get("refseq_id"), tx.get("coding_sequence_name")]):
|
42
40
|
for refseq in tx.get("refseq_identifiers"):
|
43
41
|
refseq_version = fetch_refseq_version(refseq) # adds version to a refseq ID
|
@@ -48,10 +46,12 @@ def _get_var_tx_hgvs(case_obj, variant_obj):
|
|
48
46
|
|
49
47
|
label = hgvs_simple
|
50
48
|
if validated:
|
51
|
-
label += "
|
49
|
+
label += "_validated_"
|
52
50
|
|
53
|
-
|
51
|
+
if mane_select and mane_select == refseq_version:
|
52
|
+
label += "_mane-select_"
|
54
53
|
|
54
|
+
tx_hgvs_list.append((hgvs_simple, label))
|
55
55
|
return tx_hgvs_list
|
56
56
|
|
57
57
|
|
@@ -363,6 +363,7 @@ def update_clinvar_submission_status(request_obj, institute_id, submission_id):
|
|
363
363
|
f"Removed {deleted_objects} objects and {deleted_submissions} submission from database",
|
364
364
|
"info",
|
365
365
|
)
|
366
|
+
|
366
367
|
if update_status == "submit":
|
367
368
|
submitter_key = request_obj.form.get("apiKey")
|
368
369
|
send_api_submission(institute_id, submission_id, submitter_key)
|
@@ -58,13 +58,6 @@
|
|
58
58
|
<nav aria-label="breadcrumb">
|
59
59
|
<ol class="breadcrumb align-items-center">
|
60
60
|
<li class="breadcrumb-item">
|
61
|
-
<!-- Download the Variant data CSV file -->
|
62
|
-
<a href="{{ url_for('clinvar.clinvar_download_csv', submission=subm_obj._id, csv_type='variant_data', clinvar_id=subm_obj.clinvar_subm_id or 'None') }}" class="btn btn-primary btn-xs text-white" target="_blank" rel="noopener">
|
63
|
-
Download variants csv file
|
64
|
-
</a>
|
65
|
-
<a href=" {{ url_for('clinvar.clinvar_download_csv', submission=subm_obj._id, csv_type='case_data', clinvar_id=subm_obj.clinvar_subm_id or 'None') }}" class="btn btn-primary btn-xs text-white" target="_blank" rel="noopener">
|
66
|
-
Download casedata csv file
|
67
|
-
</a>
|
68
61
|
<a id="download_clinvar_json" href="{{ url_for('clinvar.clinvar_download_json', submission=subm_obj._id, clinvar_id=subm_obj.clinvar_subm_id or 'None') }}" class="btn btn-primary btn-xs text-white" target="_blank" rel="noopener">
|
69
62
|
Download submission json file
|
70
63
|
</a>
|
@@ -77,8 +70,7 @@
|
|
77
70
|
{% if subm_obj.status != 'submitted' %}
|
78
71
|
<li class="breadcrumb-item"><button type="submit" name="update_submission" value="submitted" class="btn btn-success btn-xs">Mark as submitted</button></li>
|
79
72
|
{% endif %}
|
80
|
-
|
81
|
-
<li class="breadcrumb-item"><button type="submit" name="update_submission" value="delete" class="btn btn-danger btn-xs">Delete submission</button></li>
|
73
|
+
<li class="breadcrumb-item"><button type="submit" name="update_submission" value="delete" class="btn btn-danger btn-xs">Delete submission from Scout</button></li>
|
82
74
|
</ol>
|
83
75
|
</nav>
|
84
76
|
</form>
|
@@ -101,9 +93,13 @@
|
|
101
93
|
{% endif %}
|
102
94
|
{% if subm_obj.status == 'submitted' %} <!--Submission status query -->
|
103
95
|
<form id="statusApi_{{subm_obj._id}}" action="{{ url_for('clinvar.clinvar_submission_status', submission_id=subm_obj.clinvar_subm_id) }}" method="POST">
|
104
|
-
{{
|
96
|
+
{{ action_modal("status", subm_obj._id) }}
|
105
97
|
<td style="width: 20%"><button type="button" class="btn btn-sm btn-secondary form-control" name="status_enquiry" value="api_status" data-bs-toggle="modal" data-bs-target="#statusModal_{{subm_obj._id}}">Submission status enquiry</button></td>
|
106
98
|
</form>
|
99
|
+
<form id="deleteApi_{{subm_obj._id}}" action="{{ url_for('clinvar.clinvar_submission_delete', submission_id=subm_obj.clinvar_subm_id) }}" method="POST">
|
100
|
+
{{ action_modal("remove", subm_obj._id) }}
|
101
|
+
<td style="width: 20%"><button type="button" class="btn btn-sm btn-danger form-control" name="_enquiry" value="api_status" data-bs-toggle="modal" data-bs-target="#removeModal_{{subm_obj._id}}">Delete submission from ClinVar</button></td>
|
102
|
+
</form>
|
107
103
|
{% endif %}
|
108
104
|
</tr>
|
109
105
|
</table>
|
@@ -246,12 +242,12 @@
|
|
246
242
|
</div>
|
247
243
|
{% endmacro %}
|
248
244
|
|
249
|
-
{% macro
|
250
|
-
<div class="modal fade" id="
|
245
|
+
{% macro action_modal(action_type, subm_id) %}
|
246
|
+
<div class="modal fade" id="{{action_type}}Modal_{{subm_id}}" tabindex="-1">
|
251
247
|
<div class="modal-dialog">
|
252
248
|
<div class="modal-content">
|
253
249
|
<div class="modal-header">
|
254
|
-
<h5 class="modal-title">Submission
|
250
|
+
<h5 class="modal-title">Submission {{action_type}}</h5>
|
255
251
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
256
252
|
</div>
|
257
253
|
<div class="modal-body">
|
@@ -260,7 +256,7 @@
|
|
260
256
|
</div>
|
261
257
|
<div class="modal-footer">
|
262
258
|
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
|
263
|
-
<button type="submit" name="
|
259
|
+
<button type="submit" name="{{action_type}}" value="{{action_type}}" class="btn btn-primary">Submission {{action_type}} enquiry</button>
|
264
260
|
</div>
|
265
261
|
</div>
|
266
262
|
</div>
|
@@ -110,26 +110,34 @@
|
|
110
110
|
<table style="width:100%; table-layout: auto; border-collapse: collapse;">
|
111
111
|
<caption></caption>
|
112
112
|
<tr><th></th></tr>
|
113
|
-
{% set ns = namespace(label='', validated=false) %}
|
114
113
|
{% for item_row in variant_data.var_form.tx_hgvs | batch(3) %}
|
115
114
|
<tr>
|
116
115
|
{% for item in item_row %}
|
117
|
-
|
116
|
+
{% set ns = namespace(label='', validated=false, mane_select=false) %}
|
118
117
|
|
119
|
-
{% if "
|
118
|
+
{% if "_validated_" in item.label.text %}
|
120
119
|
{% set ns.validated = true %}
|
121
|
-
{% else %}
|
122
|
-
{% set ns.validated = false %}
|
123
120
|
{% endif %}
|
124
|
-
{%
|
121
|
+
{% if "_mane-select_" in item.label.text %}
|
122
|
+
{% set ns.mane_select = true %}
|
123
|
+
{% endif %}
|
124
|
+
{% set ns.label = item.label.text | replace("_validated_", "") | replace("_mane-select_", "") %}
|
125
|
+
|
126
|
+
<td style="width: 3%; text-align: right; vertical-align: baseline;">
|
127
|
+
<input type="radio" name="tx_hgvs" value="{{ ns.label }}"
|
128
|
+
{% if ns.mane_select %}checked{% endif %}>
|
129
|
+
</td>
|
125
130
|
|
126
131
|
<td style="width: 30%; text-align: left; ">
|
127
132
|
<p class="text-dark"
|
128
|
-
{% if ns.label|length >
|
133
|
+
{% if ns.label|length > 50 %}
|
129
134
|
data-bs-toggle="tooltip" title="{{ns.label}}">{{ ns.label|truncate(25,true,'..') }}
|
130
135
|
{% else %}
|
131
136
|
>{{ ns.label }}
|
132
137
|
{% endif %}
|
138
|
+
{% if ns.mane_select %}
|
139
|
+
<span class='badge bg-dark'>MANE SELECT</span>
|
140
|
+
{% endif %}
|
133
141
|
{% if ns.validated %}
|
134
142
|
<em class="fa fa-check text-success" aria-hidden="true" data-bs-toggle="tooltip" title="Verified by VariantValidator"></em>
|
135
143
|
{% endif %}
|
@@ -1,8 +1,7 @@
|
|
1
|
-
import csv
|
2
1
|
import logging
|
3
2
|
from json import dumps
|
4
3
|
from tempfile import NamedTemporaryFile
|
5
|
-
from typing import List
|
4
|
+
from typing import List, Tuple
|
6
5
|
|
7
6
|
from flask import Blueprint, flash, redirect, render_template, request, send_file, url_for
|
8
7
|
from flask_login import current_user
|
@@ -29,10 +28,25 @@ def clinvar_submission_status(submission_id):
|
|
29
28
|
"""Sends a request to ClinVar to retrieve and display the status of a submission."""
|
30
29
|
|
31
30
|
# flash a message with current submission status for a ClinVar submission
|
32
|
-
|
33
|
-
submission_id=submission_id, api_key=request.form.get("apiKey")
|
31
|
+
flash(
|
32
|
+
f'Response from ClinVar: {clinvar_api.json_submission_status( submission_id=submission_id, api_key=request.form.get("apiKey"))}',
|
33
|
+
"primary",
|
34
34
|
)
|
35
|
+
return redirect(request.referrer)
|
36
|
+
|
37
|
+
|
38
|
+
@clinvar_bp.route("/clinvar/delete-enquiry/<submission_id>", methods=["POST"])
|
39
|
+
def clinvar_submission_delete(submission_id):
|
40
|
+
"""Sends a request to ClinVar to delete a successfully processed submission."""
|
35
41
|
|
42
|
+
# Retrieve the actual submission status:
|
43
|
+
delete_res: Tuple[int, dict] = clinvar_api.delete_clinvar_submission(
|
44
|
+
submission_id=submission_id, api_key=request.form.get("apiKey")
|
45
|
+
)
|
46
|
+
flash(
|
47
|
+
f"ClinVar response: { str(delete_res[1]) }",
|
48
|
+
"success" if delete_res[0] == 201 else "warning",
|
49
|
+
)
|
36
50
|
return redirect(request.referrer)
|
37
51
|
|
38
52
|
|
@@ -104,33 +118,6 @@ def clinvar_update_submission(institute_id, submission):
|
|
104
118
|
return redirect(request.referrer)
|
105
119
|
|
106
120
|
|
107
|
-
@clinvar_bp.route("/<submission>/download/csv/<csv_type>/<clinvar_id>", methods=["GET"])
|
108
|
-
def clinvar_download_csv(submission, csv_type, clinvar_id):
|
109
|
-
"""Download a csv (Variant file or CaseData file) for a clinVar submission"""
|
110
|
-
|
111
|
-
clinvar_file_data = controllers.clinvar_submission_file(submission, csv_type, clinvar_id)
|
112
|
-
|
113
|
-
if clinvar_file_data is None:
|
114
|
-
return redirect(request.referrer)
|
115
|
-
|
116
|
-
# Write temp CSV file and serve it in response
|
117
|
-
tmp_csv = NamedTemporaryFile(
|
118
|
-
mode="a+", prefix=clinvar_file_data[0].split(".")[0], suffix=".csv"
|
119
|
-
)
|
120
|
-
writes = csv.writer(tmp_csv, delimiter=",", quoting=csv.QUOTE_ALL)
|
121
|
-
writes.writerow(clinvar_file_data[1]) # Write header
|
122
|
-
writes.writerows(clinvar_file_data[2]) # Write lines
|
123
|
-
tmp_csv.flush()
|
124
|
-
tmp_csv.seek(0)
|
125
|
-
|
126
|
-
return send_file(
|
127
|
-
tmp_csv.name,
|
128
|
-
download_name=clinvar_file_data[0],
|
129
|
-
mimetype="text/csv",
|
130
|
-
as_attachment=True,
|
131
|
-
)
|
132
|
-
|
133
|
-
|
134
121
|
@clinvar_bp.route("/<submission>/download/json/<clinvar_id>", methods=["GET"])
|
135
122
|
def clinvar_download_json(submission, clinvar_id):
|
136
123
|
"""Download a json for a clinVar submission"""
|
@@ -20,7 +20,10 @@ from scout.constants import (
|
|
20
20
|
SEX_MAP,
|
21
21
|
VARIANTS_TARGET_FROM_CATEGORY,
|
22
22
|
)
|
23
|
-
from scout.server.blueprints.variant.utils import
|
23
|
+
from scout.server.blueprints.variant.utils import (
|
24
|
+
predictions,
|
25
|
+
update_representative_gene,
|
26
|
+
)
|
24
27
|
from scout.server.extensions import beacon, store
|
25
28
|
from scout.server.utils import institute_and_case, user_institutes
|
26
29
|
|
@@ -328,6 +331,21 @@ def get_clinvar_submitters(form: MultiDict) -> Optional[List[str]]:
|
|
328
331
|
return clinvar_submitters
|
329
332
|
|
330
333
|
|
334
|
+
def get_soft_filters(form: MultiDict) -> Optional[list]:
|
335
|
+
"""
|
336
|
+
Return a list with custom soft filters or None.
|
337
|
+
This is not available on the form for unprivileged users, only admin.
|
338
|
+
"""
|
339
|
+
if current_user.is_admin is False:
|
340
|
+
return None
|
341
|
+
|
342
|
+
soft_filters = []
|
343
|
+
for filter in form.getlist("soft_filters"):
|
344
|
+
soft_filters.append(filter)
|
345
|
+
|
346
|
+
return soft_filters
|
347
|
+
|
348
|
+
|
331
349
|
def get_loqusdb_ids(form: MultiDict) -> Optional[List[str]]:
|
332
350
|
"""
|
333
351
|
Return loqusdb ids from the form multiselect.
|
@@ -390,6 +408,7 @@ def update_institute_settings(store: MongoAdapter, institute_obj: Dict, form: Mu
|
|
390
408
|
check_show_all_vars=form.get("check_show_all_vars"),
|
391
409
|
clinvar_key=form.get("clinvar_key"),
|
392
410
|
clinvar_submitters=get_clinvar_submitters(form),
|
411
|
+
soft_filters=get_soft_filters(form),
|
393
412
|
)
|
394
413
|
return updated_institute
|
395
414
|
|