scout-browser 4.98.0__py3-none-any.whl → 4.100.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 +30 -15
- scout/adapter/mongo/clinvar.py +23 -31
- scout/adapter/mongo/event.py +14 -4
- scout/adapter/mongo/institute.py +42 -55
- scout/adapter/mongo/omics_variant.py +14 -1
- scout/adapter/mongo/query.py +24 -1
- scout/adapter/mongo/variant.py +44 -22
- scout/adapter/mongo/variant_loader.py +169 -186
- scout/build/individual.py +5 -1
- scout/build/variant/variant.py +8 -0
- scout/commands/download/ensembl.py +18 -3
- scout/commands/load/research.py +2 -3
- scout/commands/update/individual.py +3 -0
- scout/commands/update/panelapp.py +15 -2
- scout/constants/__init__.py +6 -2
- scout/constants/clnsig.py +2 -0
- scout/constants/file_types.py +12 -0
- scout/constants/igv_tracks.py +9 -6
- scout/constants/indexes.py +5 -4
- scout/constants/panels.py +3 -0
- scout/constants/query_terms.py +1 -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.py +1 -0
- scout/models/case/case_loading_models.py +7 -1
- scout/parse/ensembl.py +8 -3
- 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 +39 -22
- scout/server/blueprints/alignviewers/controllers.py +29 -10
- scout/server/blueprints/alignviewers/templates/alignviewers/igv_viewer.html +51 -11
- scout/server/blueprints/cases/controllers.py +9 -3
- scout/server/blueprints/cases/templates/cases/case_report.html +25 -13
- scout/server/blueprints/cases/templates/cases/chanjo2_form.html +1 -1
- scout/server/blueprints/cases/templates/cases/collapsible_actionbar.html +1 -1
- scout/server/blueprints/cases/templates/cases/gene_panel.html +1 -1
- scout/server/blueprints/cases/templates/cases/utils.html +25 -6
- scout/server/blueprints/clinvar/controllers.py +34 -15
- scout/server/blueprints/clinvar/templates/clinvar/clinvar_submissions.html +34 -12
- scout/server/blueprints/clinvar/templates/clinvar/multistep_add_variant.html +14 -5
- scout/server/blueprints/clinvar/views.py +14 -2
- scout/server/blueprints/diagnoses/static/diagnoses.js +8 -1
- scout/server/blueprints/institutes/controllers.py +10 -2
- scout/server/blueprints/institutes/static/variants_list_scripts.js +9 -1
- scout/server/blueprints/institutes/templates/overview/institute_sidebar.html +9 -1
- scout/server/blueprints/login/controllers.py +112 -12
- scout/server/blueprints/login/views.py +38 -60
- scout/server/blueprints/mme/__init__.py +1 -0
- scout/server/blueprints/mme/controllers.py +18 -0
- scout/server/blueprints/mme/templates/mme/mme_submissions.html +153 -0
- scout/server/blueprints/mme/views.py +34 -0
- scout/server/blueprints/panels/templates/panels/panel.html +19 -6
- scout/server/blueprints/phenotypes/templates/phenotypes/hpo_terms.html +8 -1
- scout/server/blueprints/public/templates/public/index.html +5 -1
- scout/server/blueprints/variant/controllers.py +19 -10
- scout/server/blueprints/variant/templates/variant/acmg.html +15 -2
- scout/server/blueprints/variant/templates/variant/cancer-variant.html +1 -1
- scout/server/blueprints/variant/templates/variant/components.html +38 -16
- scout/server/blueprints/variant/templates/variant/sv-variant.html +2 -2
- scout/server/blueprints/variant/templates/variant/utils.html +23 -11
- scout/server/blueprints/variant/templates/variant/variant.html +42 -1
- scout/server/blueprints/variant/views.py +12 -0
- scout/server/blueprints/variants/controllers.py +20 -3
- scout/server/blueprints/variants/forms.py +8 -3
- scout/server/blueprints/variants/templates/variants/components.html +34 -0
- scout/server/blueprints/variants/templates/variants/indicators.html +11 -13
- 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 +35 -34
- scout/server/blueprints/variants/templates/variants/variants.html +4 -25
- scout/server/config.py +8 -0
- scout/server/extensions/bionano_extension.py +0 -1
- scout/server/extensions/chanjo2_extension.py +54 -13
- scout/server/links.py +15 -0
- scout/server/static/bs_styles.css +34 -6
- scout/server/templates/utils.html +9 -10
- scout/server/utils.py +40 -5
- scout/utils/acmg.py +25 -26
- scout/utils/ensembl_biomart_clients.py +2 -1
- scout/utils/ensembl_rest_clients.py +25 -32
- scout/utils/hgvs.py +1 -1
- scout/utils/scout_requests.py +1 -3
- {scout_browser-4.98.0.dist-info → scout_browser-4.100.0.dist-info}/METADATA +10 -14
- {scout_browser-4.98.0.dist-info → scout_browser-4.100.0.dist-info}/RECORD +91 -87
- {scout_browser-4.98.0.dist-info → scout_browser-4.100.0.dist-info}/WHEEL +0 -0
- {scout_browser-4.98.0.dist-info → scout_browser-4.100.0.dist-info}/entry_points.txt +0 -0
- {scout_browser-4.98.0.dist-info → scout_browser-4.100.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,5 +1,6 @@
|
|
1
1
|
{% import "bootstrap/wtf.html" as wtf %}
|
2
2
|
{% from "utils.html" import comments_table %}
|
3
|
+
{% from "cases/utils.html" import pretty_link_variant %}
|
3
4
|
{% from "variants/indicators.html" import pin_indicator, causative_badge, clinical_assessments_badge, comments_badge, dismissals_badge, evaluations_badge, group_assessments_badge, matching_manual_rank, research_assessments_badge %}
|
4
5
|
|
5
6
|
{% macro filter_script_main(cytobands) %}
|
@@ -192,9 +193,9 @@
|
|
192
193
|
{% endif %}
|
193
194
|
<td>
|
194
195
|
{% if compound.not_loaded %}
|
195
|
-
{{ compound.display_name }} <small>(not loaded)</small>
|
196
|
+
{{ compound.display_name|truncate(20, True) }} <small>(not loaded)</small>
|
196
197
|
{% elif is_popover %}
|
197
|
-
{{ compound.display_name }}
|
198
|
+
{{ compound.display_name|truncate(20, True) }}
|
198
199
|
{% else %}
|
199
200
|
<a href='{{ url_for("variant.variant",
|
200
201
|
institute_id=institute._id,
|
@@ -217,7 +218,7 @@
|
|
217
218
|
<td>
|
218
219
|
{% for annotation in compound.functional_annotations %}
|
219
220
|
{{ annotation }}<br>
|
220
|
-
{% endfor %}
|
221
|
+
{% endfor %}x
|
221
222
|
</td>
|
222
223
|
</tr>
|
223
224
|
{% endfor %}
|
@@ -225,30 +226,25 @@
|
|
225
226
|
</table>
|
226
227
|
{% endmacro %}
|
227
228
|
|
228
|
-
{% macro
|
229
|
+
{% macro overlapping_tooltip_table(institute, case, overlapping) %}
|
229
230
|
<table class='table table-bordered table-hover table-condensed'>
|
230
231
|
<thead>
|
231
232
|
<tr>
|
232
|
-
<th>
|
233
|
+
<th>Pos</th>
|
233
234
|
<th>Type</th>
|
234
235
|
<th>Length</th>
|
235
236
|
<th>Rank score</th>
|
236
237
|
</tr>
|
237
238
|
</thead>
|
238
239
|
<tbody>
|
239
|
-
{% for
|
240
|
+
{% for variant in overlapping %}
|
240
241
|
<tr>
|
241
242
|
<td>
|
242
|
-
|
243
|
-
institute_id=institute._id,
|
244
|
-
case_name=case.display_name,
|
245
|
-
variant_id=sv._id) }}'>
|
246
|
-
{{ sv.chromosome }}{{ sv.cytoband_start }}
|
247
|
-
</a>
|
243
|
+
{{ pretty_link_variant(variant, case) }}
|
248
244
|
</td>
|
249
|
-
<td class='text-end'>{{
|
250
|
-
<td class='text-end'>{{
|
251
|
-
<td class='text-end'>{{
|
245
|
+
<td class='text-end'>{{ variant.sub_category }}</td>
|
246
|
+
<td class='text-end'>{{ variant.length if variant.length and variant.length < 100000000000 else '-' }}</td>
|
247
|
+
<td class='text-end'>{{ variant.rank_score }}</td>
|
252
248
|
</tr>
|
253
249
|
{% endfor %}
|
254
250
|
</tbody>
|
@@ -336,7 +332,7 @@
|
|
336
332
|
{{ form.spidex_human(class="selectpicker", data_style="btn-secondary") }}
|
337
333
|
</div>
|
338
334
|
<div class="col-2">
|
339
|
-
<span>{{ form.clinsig.label(class="control-label") }}</span>
|
335
|
+
<span>{{ form.clinsig.label(class="control-label", data_bs_toggle="tooltip", data_bs_placement="left", title="Germline classification criteria.") }}</span>
|
340
336
|
<span style="float:right;">{{ form.clinsig_exclude.label(class="control-label", data_bs_toggle="tooltip", data_bs_placement="left", title="Exclude variants with clinical significance among the selected categories.") }} {{form.clinsig_exclude}}</span>
|
341
337
|
{{ form.clinsig(class="selectpicker", data_style="btn-secondary") }}
|
342
338
|
</div>
|
@@ -625,7 +621,7 @@
|
|
625
621
|
</div>
|
626
622
|
</div>
|
627
623
|
<div class="col-2">
|
628
|
-
{{ form.clinsig.label(class="control-label") }}
|
624
|
+
{{ form.clinsig.label(class="control-label", data_bs_toggle="tooltip", data_bs_placement="left", title="Germline classification criteria.") }}
|
629
625
|
{{ form.clinsig(class="selectpicker", data_style="btn-secondary") }}
|
630
626
|
</div>
|
631
627
|
<div class="col-1">
|
@@ -726,26 +722,17 @@
|
|
726
722
|
{{ form.genetic_models(class="selectpicker", data_style="btn-secondary") }}
|
727
723
|
</div>
|
728
724
|
<div class="col-2">
|
729
|
-
<span>{{ form.clinsig.label(class="control-label") }}</span>
|
725
|
+
<span>{{ form.clinsig.label(class="control-label", data_bs_toggle="tooltip", data_bs_placement="left", title="Germline classification criteria.") }}</span>
|
730
726
|
<span style="float:right;">{{ form.clinsig_exclude.label(class="control-label", data_bs_toggle="tooltip", data_bs_placement="left", title="Exclude variants with clinical significance among the selected categories.") }} {{form.clinsig_exclude}}</span>
|
731
727
|
{{ form.clinsig(class="selectpicker", data_style="btn-secondary") }}
|
732
728
|
</div>
|
733
729
|
<div class="col-2">
|
734
|
-
<
|
735
|
-
|
736
|
-
|
737
|
-
</div>
|
738
|
-
<div class="form-check d-flex justify-content-start">
|
739
|
-
{{ form.clinvar_tag(class="form-check-input",type="checkbox") }}
|
740
|
-
{{ form.clinvar_tag.label(class="ms-2 form-check-label") }}
|
741
|
-
</div>
|
742
|
-
<div class="form-check d-flex justify-content-start">
|
743
|
-
{{ form.cosmic_tag(class="form-check-input",type="checkbox") }}
|
744
|
-
{{ form.cosmic_tag.label(class="ms-2 form-check-label") }}
|
745
|
-
</div>
|
730
|
+
<span>{{ form.clinsig_onc.label(class="control-label", data_bs_toggle="tooltip", data_bs_placement="left", title="ClinVar oncogenicity criteria will be applied alongside the other search criteria, not as an alternative. Oncogenicity annotations are still sparse in ClinVar, so relying solely on them could be too restrictive.") }}</span>
|
731
|
+
<span style="float:right;">{{ form.clinsig_onc_exclude.label(class="control-label", data_bs_toggle="tooltip", data_bs_placement="left", title="Exclude variants with oncogenicity among the selected categories.") }} {{form.clinsig_onc_exclude}}</span>
|
732
|
+
{{ form.clinsig_onc(class="selectpicker", data_style="btn-secondary") }}
|
746
733
|
</div>
|
747
734
|
</div>
|
748
|
-
<div class="row">
|
735
|
+
<div class="row mt-2">
|
749
736
|
<div class="col-4">
|
750
737
|
{{ form.hgnc_symbols.label(class="control-label") }}
|
751
738
|
{{ form.hgnc_symbols(class="form-control") }}
|
@@ -766,14 +753,28 @@
|
|
766
753
|
{{ form.alt_count.label(class="control-label") }}
|
767
754
|
{{ form.alt_count(class="form-control") }}
|
768
755
|
</div>
|
769
|
-
<div class="col-
|
756
|
+
<div class="col-1">
|
770
757
|
{{ form.tumor_frequency.label(class="control-label") }}
|
771
758
|
{{ form.tumor_frequency(class="form-control", min="0", max=1) }}
|
772
759
|
</div>
|
773
|
-
<div class="col-
|
760
|
+
<div class="col-1">
|
774
761
|
{{ form.control_frequency.label(class="control-label") }}
|
775
762
|
{{ form.control_frequency(class="form-control", min="0", max=1) }}
|
776
763
|
</div>
|
764
|
+
<div class="col-2">
|
765
|
+
<div class="form-check d-flex justify-content-start">
|
766
|
+
{{ form.mvl_tag(class="form-check-input",type="checkbox") }}
|
767
|
+
{{ form.mvl_tag.label(class="ms-2 form-check-label") }}
|
768
|
+
</div>
|
769
|
+
<div class="form-check d-flex justify-content-start">
|
770
|
+
{{ form.clinvar_tag(class="form-check-input",type="checkbox") }}
|
771
|
+
{{ form.clinvar_tag.label(class="ms-2 form-check-label") }}
|
772
|
+
</div>
|
773
|
+
<div class="form-check d-flex justify-content-start">
|
774
|
+
{{ form.cosmic_tag(class="form-check-input",type="checkbox") }}
|
775
|
+
{{ form.cosmic_tag.label(class="ms-2 form-check-label") }}
|
776
|
+
</div>
|
777
|
+
</div>
|
777
778
|
</div>
|
778
779
|
<div class="form" id="chromosome_search">
|
779
780
|
<div class="row" style="margin-top:20px;">
|
@@ -865,7 +866,7 @@
|
|
865
866
|
{{ form.genetic_models(class="selectpicker", data_style="btn-secondary") }}
|
866
867
|
</div>
|
867
868
|
<div class="col-2">
|
868
|
-
{{ form.clinsig.label(class="control-label") }}
|
869
|
+
{{ form.clinsig.label(class="control-label", data_bs_toggle="tooltip", data_bs_placement="left", title="Germline classification criteria.") }}
|
869
870
|
{{ form.clinsig(class="selectpicker", data_style="btn-secondary") }}
|
870
871
|
</div>
|
871
872
|
</div>
|
@@ -1,6 +1,6 @@
|
|
1
1
|
{% extends "layout.html" %}
|
2
|
-
{% from "variants/utils.html" import
|
3
|
-
{% from "variants/components.html" import external_scripts, external_stylesheets, frequency_cell_general, gene_cell, observed_cell_general %}
|
2
|
+
{% from "variants/utils.html" import snv_filters, cell_rank, pagination_footer, pagination_hidden_div, dismiss_variants_block, filter_form_footer, filter_script_main, update_stash_filter_button_status, mark_heteroplasmic_mt %}
|
3
|
+
{% from "variants/components.html" import external_scripts, external_stylesheets, frequency_cell_general, gene_cell, observed_cell_general, overlapping_cell %}
|
4
4
|
|
5
5
|
{% block title %}
|
6
6
|
{{ super() }} - {{ institute.display_name }} - {{ case.display_name }} - {{ form.variant_type.data|capitalize }} variants
|
@@ -59,7 +59,7 @@
|
|
59
59
|
<th style="width:5%" title="Rank score">Score</th>
|
60
60
|
<th style="width:4%" title="Chromosome">Chr.</th>
|
61
61
|
<th style="width:12%" title="HGNC symbols">Gene</th>
|
62
|
-
<th style="width:6%" title="
|
62
|
+
<th style="width:6%" title="Population Frequency">Pop Freq</th>
|
63
63
|
<th style="width:10%" title="Observed database matches">Observed</th>
|
64
64
|
<th style="width:5%" title="CADD score">CADD</th>
|
65
65
|
<th style="width:18%" title="Functional annotation">Function</th>
|
@@ -93,7 +93,7 @@
|
|
93
93
|
{{ functional_annotation_cell(variant) }}
|
94
94
|
</td>
|
95
95
|
<td>{{ cell_models(variant) }}</td>
|
96
|
-
<td>{{ overlapping_cell(variant) }}</td>
|
96
|
+
<td>{{ overlapping_cell(variant, institute, case) }}</td>
|
97
97
|
</tr>
|
98
98
|
{% else %}
|
99
99
|
<tr>
|
@@ -159,27 +159,6 @@
|
|
159
159
|
{% endif %}
|
160
160
|
{% endmacro %}
|
161
161
|
|
162
|
-
{% macro overlapping_cell(variant) %}
|
163
|
-
|
164
|
-
{% if variant.compounds %}
|
165
|
-
{% set ns=namespace(show_compounds=false) %}
|
166
|
-
{% for compound in variant.compounds if not compound.is_dismissed %}
|
167
|
-
{% set ns.show_compounds = true %}
|
168
|
-
{% endfor %}
|
169
|
-
{% if ns.show_compounds %}
|
170
|
-
<a href="#" class="badge bg-primary text-white" data-bs-toggle="popover" data-bs-placement="left"
|
171
|
-
data-bs-html="true" data-bs-trigger="hover click"
|
172
|
-
data-bs-content="{{ compounds_table(institute, case, variant.compounds[:20], is_popover=true) }}">Compounds</a>
|
173
|
-
{% endif %}
|
174
|
-
{% endif %}
|
175
|
-
|
176
|
-
{% if variant.overlapping %}
|
177
|
-
<a href="#" class="badge bg-warning" data-bs-toggle="popover" data-bs-placement="left"
|
178
|
-
data-bs-html="true" data-bs-trigger="hover click"
|
179
|
-
data-bs-content="{{ svs_table(institute, case, variant.overlapping[:20]) }}">Overlapping SVs</a>
|
180
|
-
{% endif %}
|
181
|
-
{% endmacro %}
|
182
|
-
|
183
162
|
{% block scripts %}
|
184
163
|
{{ super() }}
|
185
164
|
{{ external_scripts() }}
|
scout/server/config.py
CHANGED
@@ -38,6 +38,14 @@ ACCREDITATION_BADGE = "swedac-1926-iso17025.png"
|
|
38
38
|
# discovery_url="https://accounts.google.com/.well-known/openid-configuration",
|
39
39
|
# )
|
40
40
|
|
41
|
+
# Parameters required for login with Keycloak
|
42
|
+
# KEYCLOAK = dict(
|
43
|
+
# client_id="<name_of_client>",
|
44
|
+
# client_secret="secret",
|
45
|
+
# discovery_url="http://localhost:8080/realms/<name_of_realm>/.well-known/openid-configuration",
|
46
|
+
# logout_url="http://localhost:8080/realms/<name_of_realm>/protocol/openid-connect/logout",
|
47
|
+
# )
|
48
|
+
|
41
49
|
# Chanjo database connection string - used by chanjo report to create coverage reports
|
42
50
|
# SQLALCHEMY_DATABASE_URI = "mysql+pymysql://test_user:test_passwordw@127.0.0.1:3306/chanjo"
|
43
51
|
|
@@ -7,7 +7,6 @@ For further development, the server has a Swagger-like demo interface at https:/
|
|
7
7
|
which is useful for details, and for sniffing actual message content structure, required cookie variable names etc.
|
8
8
|
"""
|
9
9
|
|
10
|
-
import json
|
11
10
|
import logging
|
12
11
|
from typing import Dict, Iterable, List, Optional, Tuple
|
13
12
|
|
@@ -1,11 +1,13 @@
|
|
1
1
|
import logging
|
2
|
-
from typing import Dict
|
2
|
+
from typing import Dict
|
3
3
|
|
4
4
|
import requests
|
5
|
-
from flask import current_app
|
5
|
+
from flask import Flask, current_app
|
6
|
+
|
7
|
+
from scout.server.utils import get_case_mito_chromosome
|
8
|
+
from scout.utils.scout_requests import get_request_json, post_request_json
|
6
9
|
|
7
10
|
REF_CHROM = "14"
|
8
|
-
MT_CHROM = "MT"
|
9
11
|
LOG = logging.getLogger(__name__)
|
10
12
|
|
11
13
|
CHANJO_BUILD_37 = "GRCh37"
|
@@ -15,14 +17,33 @@ CHANJO_BUILD_38 = "GRCh38"
|
|
15
17
|
class Chanjo2Client:
|
16
18
|
"""Runs requests to chanjo2 and returns results in the expected format."""
|
17
19
|
|
18
|
-
def
|
20
|
+
def init_app(self, app: Flask):
|
21
|
+
"""The chanjo2 client has nothing to init, just check chanjo2 heartbeat."""
|
22
|
+
self.get_status(app)
|
23
|
+
|
24
|
+
def get_status(self, app: Flask) -> dict:
|
25
|
+
"""Check Chanjo2 heartbeat, LOG status and return status."""
|
26
|
+
chanjo2_heartbeat_url: str = app.config.get("CHANJO2_URL")
|
27
|
+
json_resp = get_request_json(chanjo2_heartbeat_url)
|
28
|
+
|
29
|
+
if not json_resp:
|
30
|
+
LOG.warning(
|
31
|
+
f"Chanjo2 is configured at {chanjo2_heartbeat_url} but does not return heartbeat."
|
32
|
+
)
|
33
|
+
return json_resp
|
34
|
+
|
35
|
+
LOG.info(f"{json_resp.get('content',{}).get('message')}")
|
36
|
+
return json_resp
|
37
|
+
|
38
|
+
def mt_coverage_stats(self, case_obj: dict) -> Dict[str, dict]:
|
19
39
|
"""Sends a POST requests to the chanjo2 coverage/d4/interval to collect stats for the MT case report."""
|
20
40
|
|
21
41
|
chanjo2_chrom_cov_url: str = "/".join(
|
22
42
|
[current_app.config.get("CHANJO2_URL"), "coverage/d4/interval/"]
|
23
43
|
)
|
24
44
|
coverage_stats = {}
|
25
|
-
|
45
|
+
case_mt_chrom = get_case_mito_chromosome(case_obj)
|
46
|
+
for ind in case_obj.get("individuals", []):
|
26
47
|
|
27
48
|
if not ind.get("d4_file"):
|
28
49
|
continue
|
@@ -30,14 +51,21 @@ class Chanjo2Client:
|
|
30
51
|
|
31
52
|
# Get mean coverage over chr14
|
32
53
|
chrom_cov_query["chromosome"] = REF_CHROM
|
33
|
-
resp = requests.post(chanjo2_chrom_cov_url, json=chrom_cov_query)
|
34
|
-
autosome_cov = resp.json().get("mean_coverage")
|
35
54
|
|
36
|
-
|
37
|
-
|
38
|
-
|
55
|
+
autosome_cov_json = post_request_json(chanjo2_chrom_cov_url, chrom_cov_query)
|
56
|
+
if autosome_cov_json.get("status_code") != 200:
|
57
|
+
raise ValueError(
|
58
|
+
f"Chanjo2 get autosome coverage failed: {autosome_cov_json.get('message')}"
|
59
|
+
)
|
60
|
+
|
61
|
+
autosome_cov = autosome_cov_json.get("content", {}).get("mean_coverage")
|
39
62
|
|
40
|
-
|
63
|
+
# Get mean coverage over chrMT
|
64
|
+
chrom_cov_query["chromosome"] = case_mt_chrom
|
65
|
+
mt_cov_json = post_request_json(chanjo2_chrom_cov_url, chrom_cov_query)
|
66
|
+
if mt_cov_json.get("status_code") != 200:
|
67
|
+
raise ValueError(f"Chanjo2 get MT coverage failed: {mt_cov_json.get('message')}")
|
68
|
+
mt_cov = mt_cov_json.get("content", {}).get("mean_coverage")
|
41
69
|
|
42
70
|
coverage_info = dict(
|
43
71
|
mt_coverage=mt_cov,
|
@@ -66,6 +94,8 @@ class Chanjo2Client:
|
|
66
94
|
"interval_type": "genes",
|
67
95
|
"samples": [],
|
68
96
|
}
|
97
|
+
analysis_types = []
|
98
|
+
|
69
99
|
for ind in individuals:
|
70
100
|
if not ind.get("d4_file"):
|
71
101
|
continue
|
@@ -73,10 +103,21 @@ class Chanjo2Client:
|
|
73
103
|
gene_cov_query["samples"].append(
|
74
104
|
{"coverage_file_path": ind["d4_file"], "name": ind["individual_id"]}
|
75
105
|
)
|
106
|
+
analysis_types.append(ind.get("analysis_type"))
|
76
107
|
|
77
|
-
|
78
|
-
|
108
|
+
if "wes" in analysis_types:
|
109
|
+
gene_cov_query["interval_type"] = "exons"
|
110
|
+
elif "wts" in analysis_types:
|
111
|
+
gene_cov_query["interval_type"] = "transcripts"
|
112
|
+
|
113
|
+
gene_cov_json = post_request_json(chanjo2_gene_cov_url, gene_cov_query)
|
114
|
+
|
115
|
+
if gene_cov_json.get("status_code") != 200:
|
116
|
+
raise ValueError(
|
117
|
+
f"Chanjo2 get complete coverage failed: {gene_cov_json.get('message')}"
|
118
|
+
)
|
79
119
|
|
120
|
+
gene_cov = gene_cov_json.get("content")
|
80
121
|
full_coverage = bool(gene_cov)
|
81
122
|
for sample in gene_cov.keys():
|
82
123
|
if gene_cov[sample]["coverage_completeness_percent"] < 100:
|
scout/server/links.py
CHANGED
@@ -54,6 +54,7 @@ def add_gene_links(
|
|
54
54
|
gene_obj["string_link"] = string(ensembl_id)
|
55
55
|
gene_obj["reactome_link"] = reactome(ensembl_id)
|
56
56
|
gene_obj["clingen_link"] = clingen(hgnc_id)
|
57
|
+
gene_obj["cspec_link"] = clingen_cspec(hgnc_id)
|
57
58
|
gene_obj["gencc_link"] = gencc(hgnc_id)
|
58
59
|
gene_obj["expression_atlas_link"] = expression_atlas(ensembl_id)
|
59
60
|
gene_obj["exac_link"] = exac(ensembl_id)
|
@@ -80,6 +81,7 @@ def add_gene_links(
|
|
80
81
|
gene_obj["gnomad_str_link"] = gnomad_str_gene(hgnc_symbol)
|
81
82
|
gene_obj["panelapp_link"] = panelapp_gene(hgnc_symbol)
|
82
83
|
gene_obj["decipher_link"] = decipher_gene(hgnc_symbol)
|
84
|
+
gene_obj["cancer_hotspots_link"] = cancer_hotspots_gene(hgnc_symbol)
|
83
85
|
if institute:
|
84
86
|
gene_obj["alamut_link"] = alamut_gene_link(institute, gene_obj, build)
|
85
87
|
|
@@ -90,6 +92,12 @@ def decipher_gene(hgnc_symbol: str) -> Optional[str]:
|
|
90
92
|
return f"https://www.deciphergenomics.org/gene/{hgnc_symbol}/overview/clinical-info"
|
91
93
|
|
92
94
|
|
95
|
+
def cancer_hotspots_gene(hgnc_symbol: str) -> Optional[str]:
|
96
|
+
"""Create link to Decipher gene."""
|
97
|
+
if hgnc_symbol:
|
98
|
+
return f"https://www.cancerhotspots.org/#/home?search={hgnc_symbol}"
|
99
|
+
|
100
|
+
|
93
101
|
def panelapp_gene(hgnc_symbol):
|
94
102
|
link = "https://panelapp.genomicsengland.co.uk/panels/entities/{}"
|
95
103
|
|
@@ -250,6 +258,13 @@ def clingen(hgnc_id):
|
|
250
258
|
return link.format(hgnc_id)
|
251
259
|
|
252
260
|
|
261
|
+
def clingen_cspec(hgnc_id):
|
262
|
+
link = "https://cspec.genome.network/cspec/ui/svi/?search=HGNC:{}"
|
263
|
+
if not hgnc_id:
|
264
|
+
return None
|
265
|
+
return link.format(hgnc_id)
|
266
|
+
|
267
|
+
|
253
268
|
def gencc(hgnc_id):
|
254
269
|
link = "https://search.thegencc.org/genes/HGNC:{}"
|
255
270
|
if not hgnc_id:
|
@@ -371,6 +371,23 @@ body {
|
|
371
371
|
}
|
372
372
|
/* end of sidebar-related stuff */
|
373
373
|
|
374
|
+
/* A collapse icon for button use */
|
375
|
+
.btn[aria-expanded="false"] .collapse-button-icon::before {
|
376
|
+
content: " \f0da";
|
377
|
+
font-family: "Font Awesome 5 Free", ui-monospace;
|
378
|
+
font-weight: 900;
|
379
|
+
display: inline;
|
380
|
+
text-align: left;
|
381
|
+
}
|
382
|
+
|
383
|
+
.btn[aria-expanded="true"] .collapse-button-icon::before {
|
384
|
+
content: " \f0d7";
|
385
|
+
font-family: "Font Awesome 5 Free", ui-monospace;
|
386
|
+
font-weight: 900;
|
387
|
+
display: inline;
|
388
|
+
text-align: left;
|
389
|
+
|
390
|
+
|
374
391
|
option {
|
375
392
|
width: 100%;
|
376
393
|
}
|
@@ -386,9 +403,20 @@ option {
|
|
386
403
|
}
|
387
404
|
|
388
405
|
.str-variants-reviewer-container {
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
406
|
+
padding: 20px 5px;
|
407
|
+
}
|
408
|
+
|
409
|
+
.str-variants-reviewer-container svg {
|
410
|
+
width: 100%;
|
411
|
+
height: auto;
|
412
|
+
}
|
413
|
+
|
414
|
+
.blink_me {
|
415
|
+
animation: blinker 2s linear infinite;
|
416
|
+
}
|
417
|
+
|
418
|
+
@keyframes blinker {
|
419
|
+
50% {
|
420
|
+
opacity: 0;
|
421
|
+
}
|
422
|
+
}
|
@@ -234,16 +234,15 @@
|
|
234
234
|
{% endmacro %}
|
235
235
|
|
236
236
|
{% macro db_table_external_stylesheets() %}
|
237
|
-
<link rel="stylesheet" href="https://cdn.datatables.net/
|
238
|
-
<link rel="stylesheet" href="https://cdn.datatables.net/buttons/2.2
|
239
|
-
|
240
|
-
{% endmacro %}
|
237
|
+
<link rel="stylesheet" href="https://cdn.datatables.net/2.2.2/css/dataTables.bootstrap5.min.css" integrity="sha512-pVSTZJo4Kj/eLMUG1w+itkGx+scwF00G5dMb02FjgU9WwF7F/cpZvu1Bf1ojA3iAf8y94cltGnuPV9vwv3CgZw==" referrerpolicy="no-referrer" crossorigin="anonymous">
|
238
|
+
<link rel="stylesheet" href="https://cdn.datatables.net/buttons/3.2.2/css/buttons.bootstrap5.min.css" integrity="sha512-9Mi4C/wtfinscXIdOGZvqv0ZYvhBUOQCrwqmTZyiYuEgJQHWnXenkYQOC44I3kyh7uaOmz9eNxeMkC8xGBLOWg==" referrerpolicy="no-referrer" crossorigin="anonymous">
|
239
|
+
{% endmacro %}
|
241
240
|
|
242
241
|
{% macro db_table_external_scripts() %}
|
243
|
-
<script src="https://cdn.datatables.net/
|
244
|
-
<script src="https://cdn.datatables.net/
|
245
|
-
<script src="https://cdn.datatables.net/buttons/2.2
|
246
|
-
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.
|
247
|
-
<script src="https://cdn.datatables.net/buttons/2.2
|
248
|
-
<script src="https://cdn.datatables.net/buttons/2.2
|
242
|
+
<script src="https://cdn.datatables.net/2.2.2/js/dataTables.min.js" integrity="sha512-iwuEh+XTZQNzwdjYrqsxyWJXyReKVclGX4D2uGYBGNIR6o0VBwIfeYQv+lQjxmye87K6C6mc7qrqJlCcaeZJhA==" referrerpolicy="no-referrer" crossorigin="anonymous"></script>
|
243
|
+
<script src="https://cdn.datatables.net/2.2.2/js/dataTables.bootstrap5.min.js" integrity="sha512-Cwi0jz7fz7mrX990DlJ1+rmiH/D9/rjfOoEex8C9qrPRDDqwMPdWV7pJFKzhM10gAAPlufZcWhfMuPN699Ej0w==" referrerpolicy="no-referrer" crossorigin="anonymous"></script>
|
244
|
+
<script src="https://cdn.datatables.net/buttons/3.2.2/js/dataTables.buttons.min.js" integrity="sha512-T/4qQRgBJ8Ev6Lk8M943pMIbCpgqtVf5XI77hxe3nQn0f9a0gn/WKXWMbuGv6cpjp9NM8Mc+3/3GnpW+3+TckA==" referrerpolicy="no-referrer" crossorigin="anonymous"></script>
|
245
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js" integrity="sha512-XMVd28F1oH/O71fzwBnV7HucLxVwtxf26XV8P4wPk26EDxuGZ91N8bsOttmnomcCD3CS5ZMRL50H0GgOHvegtg==" referrerpolicy="no-referrer" crossorigin="anonymous"></script>
|
246
|
+
<script src="https://cdn.datatables.net/buttons/3.2.2/js/buttons.html5.min.js" integrity="sha512-I86DAquH004N60kPVNHp+9QkC5y61C5ODHj6ewhddXnewOJ31wyOJHNlNEr8NUR3xqtGozfMb557So1pR0uDCA==" referrerpolicy="no-referrer" crossorigin="anonymous"></script>
|
247
|
+
<script src="https://cdn.datatables.net/buttons/3.2.2/js/buttons.bootstrap5.min.js" integrity="sha512-rVAaUqNTbX7lADp767RxnIMvbROobK+gn7SpbbPc+qMUPotgAkSsRhCKF5ziUEquBsdGcv3eOaZK1fIbGwh5CA==" referrerpolicy="no-referrer" crossorigin="anonymous"></script>
|
249
248
|
{% endmacro %}
|
scout/server/utils.py
CHANGED
@@ -206,6 +206,19 @@ def user_institutes(store, login_user):
|
|
206
206
|
return institutes
|
207
207
|
|
208
208
|
|
209
|
+
def get_case_genome_build(case_obj: dict) -> str:
|
210
|
+
"""returns the genome build of a case, as a string."""
|
211
|
+
return "38" if "38" in case_obj.get("genome_build", "37") else "37"
|
212
|
+
|
213
|
+
|
214
|
+
def get_case_mito_chromosome(case_obj: dict) -> str:
|
215
|
+
"""Returns MT or M, according to the genome build of a case."""
|
216
|
+
case_build = get_case_genome_build(case_obj)
|
217
|
+
if case_build == "38":
|
218
|
+
return "M"
|
219
|
+
return "MT"
|
220
|
+
|
221
|
+
|
209
222
|
def case_has_mtdna_report(case_obj: dict):
|
210
223
|
"""Display mtDNA report on the case sidebar only for some specific cases."""
|
211
224
|
if case_obj.get("track", "rare") == "cancer":
|
@@ -244,15 +257,23 @@ def case_has_chanjo2_coverage(case_obj: dict):
|
|
244
257
|
def case_has_alignments(case_obj: dict):
|
245
258
|
"""Add info on bam/cram files availability to a case dictionary
|
246
259
|
|
260
|
+
This sets the availability of alignments for autosomal chromosomes.
|
261
|
+
If paraphase or de novo assembly alignments, this is also sufficient to set
|
262
|
+
alignment availability.
|
247
263
|
Args:
|
248
264
|
case_obj(scout.models.Case)
|
249
265
|
"""
|
250
|
-
case_obj["bam_files"] = False
|
266
|
+
case_obj["bam_files"] = False
|
251
267
|
for ind in case_obj.get("individuals"):
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
268
|
+
for alignment_path_key in [
|
269
|
+
"bam_file",
|
270
|
+
"assembly_alignment_path",
|
271
|
+
"paraphase_alignment_path",
|
272
|
+
]:
|
273
|
+
bam_path = ind.get(alignment_path_key)
|
274
|
+
if bam_path and os.path.exists(bam_path):
|
275
|
+
case_obj["bam_files"] = True
|
276
|
+
return
|
256
277
|
|
257
278
|
|
258
279
|
def case_has_mt_alignments(case_obj: dict):
|
@@ -301,14 +322,28 @@ def case_append_alignments(case_obj: dict):
|
|
301
322
|
"""Deconvolute information about files to case_obj.
|
302
323
|
|
303
324
|
This function prepares bam/cram files and their indexes for easy access in templates.
|
325
|
+
|
326
|
+
index is set only if it is expected as a separate key on the indiviudal: an
|
327
|
+
attempt at discovery is still made for files with index: None.
|
304
328
|
"""
|
305
329
|
unwrap_settings = [
|
306
330
|
{"path": "bam_file", "append_to": "bam_files", "index": "bai_files"},
|
331
|
+
{"path": "assembly_alignment_path", "append_to": "assembly_alignments", "index": None},
|
307
332
|
{"path": "mt_bam", "append_to": "mt_bams", "index": "mt_bais"},
|
333
|
+
{
|
334
|
+
"path": "paraphase_alignment_path",
|
335
|
+
"append_to": "paraphase_alignments",
|
336
|
+
"index": None,
|
337
|
+
},
|
308
338
|
{"path": "rhocall_bed", "append_to": "rhocall_beds", "index": None},
|
309
339
|
{"path": "rhocall_wig", "append_to": "rhocall_wigs", "index": None},
|
310
340
|
{"path": "upd_regions_bed", "append_to": "upd_regions_beds", "index": None},
|
311
341
|
{"path": "upd_sites_bed", "append_to": "upd_sites_beds", "index": None},
|
342
|
+
{
|
343
|
+
"path": "minor_allele_frequency_wig",
|
344
|
+
"append_to": "minor_allele_frequency_wigs",
|
345
|
+
"index": None,
|
346
|
+
},
|
312
347
|
{"path": "tiddit_coverage_wig", "append_to": "tiddit_coverage_wigs", "index": None},
|
313
348
|
]
|
314
349
|
|
scout/utils/acmg.py
CHANGED
@@ -173,7 +173,7 @@ def get_acmg_criteria(acmg_terms: set) -> tuple:
|
|
173
173
|
finally, check remaining prefixes if no suffix match or stand-alone criteria match
|
174
174
|
|
175
175
|
Return a tuple with
|
176
|
-
|
176
|
+
pvs_terms: This variable indicates if Pathogenicity Very Strong exists
|
177
177
|
ps_terms: Collection of terms with Pathogenicity Strong
|
178
178
|
pm_terms: Collection of terms with Pathogenicity moderate
|
179
179
|
pp_terms: Collection of terms with Pathogenicity supporting
|
@@ -182,25 +182,29 @@ def get_acmg_criteria(acmg_terms: set) -> tuple:
|
|
182
182
|
bp_terms: Collection of terms with supporting Benign evidence
|
183
183
|
"""
|
184
184
|
|
185
|
-
|
185
|
+
pvs_terms = []
|
186
186
|
ps_terms = []
|
187
187
|
pm_terms = []
|
188
188
|
pp_terms = []
|
189
189
|
|
190
|
-
|
190
|
+
ba_terms = []
|
191
191
|
bs_terms = []
|
192
192
|
bp_terms = []
|
193
193
|
|
194
194
|
suffix_map = {
|
195
|
+
"_Stand-alone": {"B": ba_terms},
|
196
|
+
"_Very Strong": {"P": pvs_terms},
|
195
197
|
"_Strong": {"P": ps_terms, "B": bs_terms},
|
196
198
|
"_Moderate": {"P": pm_terms},
|
197
199
|
"_Supporting": {"P": pp_terms, "B": bp_terms},
|
198
200
|
}
|
199
201
|
|
200
202
|
prefix_map = {
|
203
|
+
"PVS": pvs_terms,
|
201
204
|
"PS": ps_terms,
|
202
205
|
"PM": pm_terms,
|
203
206
|
"PP": pp_terms,
|
207
|
+
"BA": ba_terms,
|
204
208
|
"BS": bs_terms,
|
205
209
|
"BP": bp_terms,
|
206
210
|
}
|
@@ -216,17 +220,12 @@ def get_acmg_criteria(acmg_terms: set) -> tuple:
|
|
216
220
|
continue
|
217
221
|
break
|
218
222
|
else:
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
else:
|
224
|
-
for prefix, term_list in prefix_map.items():
|
225
|
-
if term.startswith(prefix):
|
226
|
-
term_list.append(term)
|
227
|
-
break
|
223
|
+
for prefix, term_list in prefix_map.items():
|
224
|
+
if term.startswith(prefix):
|
225
|
+
term_list.append(term)
|
226
|
+
break
|
228
227
|
|
229
|
-
return (
|
228
|
+
return (pvs_terms, ps_terms, pm_terms, pp_terms, ba_terms, bs_terms, bp_terms)
|
230
229
|
|
231
230
|
|
232
231
|
def get_acmg(acmg_terms: set) -> Optional[str]:
|
@@ -316,19 +315,19 @@ def get_acmg_temperature(acmg_terms: set) -> Optional[dict]:
|
|
316
315
|
if not acmg_terms:
|
317
316
|
return {}
|
318
317
|
|
319
|
-
(
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
318
|
+
(pvs_terms, ps_terms, pm_terms, pp_terms, ba_terms, bs_terms, bp_terms) = get_acmg_criteria(
|
319
|
+
acmg_terms
|
320
|
+
)
|
321
|
+
|
322
|
+
points = (
|
323
|
+
8 * len(pvs_terms)
|
324
|
+
+ 4 * len(ps_terms)
|
325
|
+
+ 2 * len(pm_terms)
|
326
|
+
+ len(pp_terms)
|
327
|
+
- 8 * len(ba_terms)
|
328
|
+
- 4 * len(bs_terms)
|
329
|
+
- len(bp_terms)
|
330
|
+
)
|
332
331
|
|
333
332
|
if points <= -7:
|
334
333
|
point_classification = "benign"
|