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
@@ -3,7 +3,7 @@
|
|
3
3
|
{% from "variant/buttons.html" import reviewer_button%}
|
4
4
|
{% from "variant/gene_disease_relations.html" import inheritance_badge %}
|
5
5
|
{% from "variants/components.html" import external_scripts, external_stylesheets, frequency_cell_general %}
|
6
|
-
{% 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, filter_script_main %}
|
6
|
+
{% from "variants/utils.html" import callers_cell, cell_rank, dismiss_variants_block, filter_form_footer, update_stash_filter_button_status, pagination_footer, pagination_hidden_div, str_filters, filter_script_main %}
|
7
7
|
|
8
8
|
|
9
9
|
{% block title %}
|
@@ -60,13 +60,14 @@
|
|
60
60
|
<thead class="thead table-light">
|
61
61
|
<tr>
|
62
62
|
<th style="width:8%" title="Index">Index</th>
|
63
|
-
<th title="Repeat ID">Repeat locus</th>
|
64
|
-
<th
|
65
|
-
<th title="ALT">
|
66
|
-
<th title="ReferenceSize">
|
67
|
-
<th title="
|
63
|
+
<th title="Repeat locus ID">Repeat locus</th>
|
64
|
+
<th style="width:8%" title="Reference repeat unit">Ref RU</th>
|
65
|
+
<th style="width:6%" title="ALT">Est size</th>
|
66
|
+
<th style="width:6%" title="ReferenceSize">Ref size</th>
|
67
|
+
<th style="width:8%" title="Caller and call filter status">Qual</th>
|
68
|
+
<th style="width:8%" title="Status">Status</th>
|
68
69
|
<th title="GT">Genotype</th>
|
69
|
-
<th title="Chromosome" style="width:6%">Chr.</th>
|
70
|
+
<th style="width:6%" title="Chromosome" style="width:6%">Chr.</th>
|
70
71
|
<th title="Position" style="width:20%">Position</th>
|
71
72
|
</tr>
|
72
73
|
</thead>
|
@@ -95,11 +96,14 @@
|
|
95
96
|
<td class="text-end">{{ variant.str_display_ru or variant.str_ru or variant.reference }}</td>
|
96
97
|
<td class="text-end"><b><span data-bs-toggle="tooltip" title="{{ variant.alternative }}">{{ variant.str_mc }}</span></b></td>
|
97
98
|
<td class="text-end"><span data-bs-toggle="tooltip" title="{{ variant.reference }}">{{ variant.str_ref or "." }}</span></td>
|
99
|
+
<td>{{ callers_cell(variant) }}</td>
|
98
100
|
<td>{{ str_status(variant) }}</td>
|
99
101
|
<td>{% for sample in variant.samples %}
|
100
102
|
{% if sample.genotype_call != "./." %}
|
101
|
-
<div class="
|
102
|
-
|
103
|
+
<div class="row">
|
104
|
+
<div class="col-8">{{ sample.display_name }}</div>
|
105
|
+
<div class="col-4 text-end">{{ sample.genotype_call }}</div>
|
106
|
+
</div>
|
103
107
|
{% endif %}
|
104
108
|
{% endfor %}
|
105
109
|
</td>
|
@@ -36,12 +36,18 @@
|
|
36
36
|
the_form.submit();
|
37
37
|
}}
|
38
38
|
|
39
|
-
var show_unaffected =document.getElementById('show_unaffected');
|
39
|
+
var show_unaffected = document.getElementById('show_unaffected');
|
40
40
|
if (show_unaffected) {
|
41
41
|
show_unaffected.onchange = function() {
|
42
42
|
the_form.submit();
|
43
43
|
}}
|
44
44
|
|
45
|
+
var show_soft_filtered = document.getElementById('show_soft_filtered');
|
46
|
+
if (show_soft_filtered) {
|
47
|
+
show_soft_filtered.onchange = function() {
|
48
|
+
the_form.submit();
|
49
|
+
}}
|
50
|
+
|
45
51
|
function resetPage(){
|
46
52
|
document.getElementById('page').value = "1";
|
47
53
|
}
|
@@ -102,6 +108,18 @@
|
|
102
108
|
{{ form.hide_dismissed(class="form-check-input") }}
|
103
109
|
{{ form.hide_dismissed.label(class="form-check-label ms-2") }}
|
104
110
|
</div>
|
111
|
+
{% if institute.soft_filters %} <!-- Available only for institutes with soft filters in settings -->
|
112
|
+
<div class="form-check d-flex justify-content-start">
|
113
|
+
{{ form.show_soft_filtered(class="form-check-input") }}
|
114
|
+
{{ form.show_soft_filtered.label(
|
115
|
+
class="form-check-label ms-2",
|
116
|
+
data_bs_toggle="tooltip",
|
117
|
+
title="Filters are defined by an admin in the institute settings. Current filters are: " ~
|
118
|
+
form.institute_soft_filters.data|safe
|
119
|
+
) }}
|
120
|
+
{{ form.institute_soft_filters() }}
|
121
|
+
</div>
|
122
|
+
{% endif %}
|
105
123
|
<div class="form-check d-flex justify-content-start">
|
106
124
|
{% if institute.check_show_all_vars %}
|
107
125
|
<input type="checkbox" class="form-check-input" name="show_unaffected" id="show_unaffected" checked disabled>
|
@@ -275,6 +293,16 @@
|
|
275
293
|
</div>
|
276
294
|
{% endmacro %}
|
277
295
|
|
296
|
+
{% macro variant_size_filter(form) %}
|
297
|
+
<div class="col-2">
|
298
|
+
{{ form.size_selector.label(class="control-label") }}
|
299
|
+
<div class="input-group">
|
300
|
+
{{ form.size_selector(class="form-select") }}
|
301
|
+
{{ form.size(class="form-control") }}
|
302
|
+
</div>
|
303
|
+
</div>
|
304
|
+
{% endmacro %}
|
305
|
+
|
278
306
|
{% macro snv_filters(form, institute, case, filters)%}
|
279
307
|
<input type="hidden" name="variant_type" value="{{ form.variant_type.data }}">
|
280
308
|
{{ variants_common_filters(form, "snv") }}
|
@@ -308,16 +336,28 @@
|
|
308
336
|
{{ form.spidex_human(class="selectpicker", data_style="btn-secondary") }}
|
309
337
|
</div>
|
310
338
|
<div class="col-2">
|
311
|
-
{{ form.clinsig.label(class="control-label") }}
|
339
|
+
<span>{{ form.clinsig.label(class="control-label") }}</span>
|
340
|
+
<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>
|
312
341
|
{{ form.clinsig(class="selectpicker", data_style="btn-secondary") }}
|
313
342
|
</div>
|
314
|
-
<div class="col-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
343
|
+
<div class="col-2">
|
344
|
+
<div class="row">
|
345
|
+
<div class="class="form-check">
|
346
|
+
{{ form.clinvar_trusted_revstat(class="form-check-input") }}
|
347
|
+
{{ form.clinvar_trusted_revstat.label(class="form-check-label", data_bs_toggle="tooltip", data_bs_placement="top", title="Limit search to variants with trusted ClinVar revision status levels: mult, multiple_submitters, single, single_submitter, exp, reviewed_by_expert_panel, guideline, practice_guideline.") }}
|
348
|
+
</div>
|
349
|
+
</div>
|
350
|
+
<div class="row">
|
351
|
+
<div class="class="form-check">
|
352
|
+
{{ form.clinvar_tag(class="form-check-input") }}
|
353
|
+
{{ form.clinvar_tag.label(class="form-check-label", data_bs_toggle="tooltip", data_bs_placement="top", title="Return only variants with annotated ClinVar significance.") }}
|
354
|
+
</div>
|
355
|
+
</div>
|
356
|
+
<div class="row">
|
357
|
+
<div class="class="form-check">
|
358
|
+
{{ form.prioritise_clinvar(class="form-check-input") }}
|
359
|
+
{{ form.prioritise_clinvar.label(class="form-check-label", data_bs_toggle="tooltip", data_bs_placement="top", title="Include variants matching the selected ClinVar conditions, in addition to those found using the other search criteria (broadens the search). Note that variants excluded using ClinVar tags will still be returned when found using the other search criteria.") }}
|
360
|
+
</div>
|
321
361
|
</div>
|
322
362
|
</div>
|
323
363
|
<div class="row mb-2">
|
@@ -336,10 +376,10 @@
|
|
336
376
|
</div>
|
337
377
|
</div>
|
338
378
|
</div>
|
339
|
-
<div class="col-
|
379
|
+
<div class="col-1">
|
340
380
|
{{ wtf.form_field(form.start) }}
|
341
381
|
</div>
|
342
|
-
<div class="col-
|
382
|
+
<div class="col-1">
|
343
383
|
{{ wtf.form_field(form.end) }}
|
344
384
|
</div>
|
345
385
|
<div class="col-2">
|
@@ -348,6 +388,7 @@
|
|
348
388
|
<div class="col-2">
|
349
389
|
{{ wtf.form_field(form.cytoband_end) }}
|
350
390
|
</div>
|
391
|
+
{{ variant_size_filter(form) }}
|
351
392
|
</div>
|
352
393
|
<div class="row">
|
353
394
|
<div class="col-2">
|
@@ -577,16 +618,6 @@
|
|
577
618
|
gene_panels=['hpo']) }}">HPO gene list</a>
|
578
619
|
</div>
|
579
620
|
</div>
|
580
|
-
<div class="col-2">
|
581
|
-
{{ form.size.label(class="control-label") }}
|
582
|
-
{{ form.size(class="form-control", type="number") }}
|
583
|
-
</div>
|
584
|
-
<div class="col-2 d-flex align-items-end">
|
585
|
-
<div class="mb-2 form-check">
|
586
|
-
{{ form.size_shorter.label(class="form-check-label") }}
|
587
|
-
{{ form.size_shorter(class="form-check-input",type="checkbox") }}
|
588
|
-
</div>
|
589
|
-
</div>
|
590
621
|
<div class="col-1 d-flex align-items-end">
|
591
622
|
<div class="mb-2 form-check">
|
592
623
|
{{ form.decipher.label(class="form-check-label") }}
|
@@ -622,10 +653,10 @@
|
|
622
653
|
</div>
|
623
654
|
</div>
|
624
655
|
</div>
|
625
|
-
<div class="col-
|
656
|
+
<div class="col-1">
|
626
657
|
{{ wtf.form_field(form.start) }}
|
627
658
|
</div>
|
628
|
-
<div class="col-
|
659
|
+
<div class="col-1">
|
629
660
|
{{ wtf.form_field(form.end) }}
|
630
661
|
</div>
|
631
662
|
<div class="col-1">
|
@@ -634,6 +665,7 @@
|
|
634
665
|
<div class="col-1">
|
635
666
|
{{ wtf.form_field(form.cytoband_end) }}
|
636
667
|
</div>
|
668
|
+
{{ variant_size_filter(form) }}
|
637
669
|
</div>
|
638
670
|
<div class="row mb-3">
|
639
671
|
<div class="col-2">
|
@@ -694,7 +726,8 @@
|
|
694
726
|
{{ form.genetic_models(class="selectpicker", data_style="btn-secondary") }}
|
695
727
|
</div>
|
696
728
|
<div class="col-2">
|
697
|
-
{{ form.clinsig.label(class="control-label") }}
|
729
|
+
<span>{{ form.clinsig.label(class="control-label") }}</span>
|
730
|
+
<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>
|
698
731
|
{{ form.clinsig(class="selectpicker", data_style="btn-secondary") }}
|
699
732
|
</div>
|
700
733
|
<div class="col-2">
|
@@ -765,9 +798,9 @@
|
|
765
798
|
<div class="col">
|
766
799
|
{{ wtf.form_field(form.cytoband_end) }}
|
767
800
|
</div>
|
801
|
+
{{ variant_size_filter(form) }}
|
768
802
|
</div>
|
769
803
|
<div class="row" style="margin-top:10px;">
|
770
|
-
<div class="col-2"></div>
|
771
804
|
<div class="col-2">
|
772
805
|
<a class="btn btn-secondary btn-sm form-control" data-bs-toggle="collapse" href="#stringcoords" aria-expanded="false" aria-controls="stringcoords">
|
773
806
|
String coordinates
|
@@ -841,16 +874,6 @@
|
|
841
874
|
{{ form.hgnc_symbols.label(class="control-label") }}
|
842
875
|
{{ form.hgnc_symbols(class="form-control") }}
|
843
876
|
</div>
|
844
|
-
<div class="col-2">
|
845
|
-
{{ form.size.label(class="control-label") }}
|
846
|
-
{{ form.size(class="form-control", type="number") }}
|
847
|
-
</div>
|
848
|
-
<div class="col-2 d-flex align-items-end">
|
849
|
-
<div class="form-check mb-2">
|
850
|
-
{{ form.size_shorter.label(class="form-check-label") }}
|
851
|
-
{{ form.size_shorter(class="form-check-input",type="checkbox") }}
|
852
|
-
</div>
|
853
|
-
</div>
|
854
877
|
<div class="col-1">
|
855
878
|
</div>
|
856
879
|
<div class="col-1">
|
@@ -889,9 +912,9 @@
|
|
889
912
|
<div class="col">
|
890
913
|
{{ wtf.form_field(form.cytoband_end) }}
|
891
914
|
</div>
|
915
|
+
{{ variant_size_filter(form) }}
|
892
916
|
</div>
|
893
917
|
<div class="row" style="margin-top:10px;">
|
894
|
-
<div class="col-2"></div>
|
895
918
|
<div class="col-2">
|
896
919
|
<a class="btn btn-secondary btn-sm form-control" data-bs-toggle="collapse" href="#stringcoords" aria-expanded="false" aria-controls="stringcoords">
|
897
920
|
String coordinates
|
@@ -67,8 +67,7 @@ def variants(institute_id, case_name):
|
|
67
67
|
|
68
68
|
variants_stats = store.case_variants_count(case_obj["_id"], institute_id, variant_type, False)
|
69
69
|
|
70
|
-
|
71
|
-
case_obj["hpo_clinical_filter"] = True
|
70
|
+
controllers.set_hpo_clinical_filter(case_obj, request.form)
|
72
71
|
|
73
72
|
user_obj = store.user(current_user.email)
|
74
73
|
if request.method == "POST":
|
@@ -97,14 +96,12 @@ def variants(institute_id, case_name):
|
|
97
96
|
|
98
97
|
controllers.populate_force_show_unaffected_vars(institute_obj, form)
|
99
98
|
|
100
|
-
# populate filters dropdown
|
101
|
-
available_filters = list(store.filters(institute_id, category))
|
102
|
-
form.filters.choices = [
|
103
|
-
(filter.get("_id"), filter.get("display_name")) for filter in available_filters
|
104
|
-
]
|
105
99
|
# Populate chromosome select choices
|
106
100
|
controllers.populate_chrom_choices(form, case_obj)
|
107
101
|
|
102
|
+
# Populate custom soft filters
|
103
|
+
controllers.populate_institute_soft_filters(form=form, institute_obj=institute_obj)
|
104
|
+
|
108
105
|
# populate available panel choices
|
109
106
|
form.gene_panels.choices = controllers.gene_panel_choices(store, institute_obj, case_obj)
|
110
107
|
|
@@ -161,7 +158,9 @@ def variants(institute_id, case_name):
|
|
161
158
|
cytobands=cytobands,
|
162
159
|
dismiss_variant_options=DISMISS_VARIANT_OPTIONS,
|
163
160
|
expand_search=controllers.get_expand_search(request.form),
|
164
|
-
filters=
|
161
|
+
filters=controllers.populate_persistent_filters_choices(
|
162
|
+
institute_id=institute_id, category=category, form=form
|
163
|
+
),
|
165
164
|
form=form,
|
166
165
|
genetic_models_palette=GENETIC_MODELS_PALETTE,
|
167
166
|
inherit_palette=INHERITANCE_PALETTE,
|
@@ -210,15 +209,12 @@ def str_variants(institute_id, case_name):
|
|
210
209
|
controllers.populate_force_show_unaffected_vars(institute_obj, form)
|
211
210
|
controllers.update_form_hgnc_symbols(store, case_obj, form)
|
212
211
|
|
213
|
-
# populate filters dropdown
|
214
|
-
available_filters = list(store.filters(institute_id, category))
|
215
|
-
form.filters.choices = [
|
216
|
-
(filter.get("_id"), filter.get("display_name")) for filter in available_filters
|
217
|
-
]
|
218
|
-
|
219
212
|
# Populate chromosome select choices
|
220
213
|
controllers.populate_chrom_choices(form, case_obj)
|
221
214
|
|
215
|
+
# Populate custom soft filters
|
216
|
+
controllers.populate_institute_soft_filters(form=form, institute_obj=institute_obj)
|
217
|
+
|
222
218
|
# populate available panel choices
|
223
219
|
form.gene_panels.choices = controllers.gene_panel_choices(store, institute_obj, case_obj)
|
224
220
|
|
@@ -254,7 +250,9 @@ def str_variants(institute_id, case_name):
|
|
254
250
|
cytobands=cytobands,
|
255
251
|
dismiss_variant_options=DISMISS_VARIANT_OPTIONS,
|
256
252
|
expand_search=controllers.get_expand_search(request.form),
|
257
|
-
filters=
|
253
|
+
filters=controllers.populate_persistent_filters_choices(
|
254
|
+
institute_id=institute_id, category=category, form=form
|
255
|
+
),
|
258
256
|
form=form,
|
259
257
|
inherit_palette=INHERITANCE_PALETTE,
|
260
258
|
institute=institute_obj,
|
@@ -282,8 +280,7 @@ def sv_variants(institute_id, case_name):
|
|
282
280
|
variant_type = "clinical"
|
283
281
|
variants_stats = store.case_variants_count(case_obj["_id"], institute_id, variant_type, False)
|
284
282
|
|
285
|
-
|
286
|
-
case_obj["hpo_clinical_filter"] = True
|
283
|
+
controllers.set_hpo_clinical_filter(case_obj, request.form)
|
287
284
|
|
288
285
|
if "dismiss_submit" in request.form: # dismiss a list of variants
|
289
286
|
controllers.dismiss_variant_list(
|
@@ -299,15 +296,12 @@ def sv_variants(institute_id, case_name):
|
|
299
296
|
controllers.activate_case(store, institute_obj, case_obj, current_user)
|
300
297
|
form = controllers.populate_sv_filters_form(store, institute_obj, case_obj, category, request)
|
301
298
|
|
302
|
-
# populate filters dropdown
|
303
|
-
available_filters = list(store.filters(institute_obj["_id"], category))
|
304
|
-
form.filters.choices = [
|
305
|
-
(filter.get("_id"), filter.get("display_name")) for filter in available_filters
|
306
|
-
]
|
307
|
-
|
308
299
|
# Populate chromosome select choices
|
309
300
|
controllers.populate_chrom_choices(form, case_obj)
|
310
301
|
|
302
|
+
# Populate custom soft filters
|
303
|
+
controllers.populate_institute_soft_filters(form=form, institute_obj=institute_obj)
|
304
|
+
|
311
305
|
genome_build = "38" if "38" in str(case_obj.get("genome_build", "37")) else "37"
|
312
306
|
cytobands = store.cytoband_by_chrom(genome_build)
|
313
307
|
|
@@ -334,7 +328,9 @@ def sv_variants(institute_id, case_name):
|
|
334
328
|
cytobands=cytobands,
|
335
329
|
dismiss_variant_options=DISMISS_VARIANT_OPTIONS,
|
336
330
|
expand_search=controllers.get_expand_search(request.form),
|
337
|
-
filters=
|
331
|
+
filters=controllers.populate_persistent_filters_choices(
|
332
|
+
institute_id=institute_id, category=category, form=form
|
333
|
+
),
|
338
334
|
form=form,
|
339
335
|
inherit_palette=INHERITANCE_PALETTE,
|
340
336
|
institute=institute_obj,
|
@@ -361,8 +357,7 @@ def mei_variants(institute_id, case_name):
|
|
361
357
|
)
|
362
358
|
variants_stats = store.case_variants_count(case_obj["_id"], institute_id, variant_type, False)
|
363
359
|
|
364
|
-
|
365
|
-
case_obj["hpo_clinical_filter"] = True
|
360
|
+
controllers.set_hpo_clinical_filter(case_obj, request.form)
|
366
361
|
|
367
362
|
if "dismiss_submit" in request.form: # dismiss a list of variants
|
368
363
|
controllers.dismiss_variant_list(
|
@@ -393,15 +388,12 @@ def mei_variants(institute_id, case_name):
|
|
393
388
|
# set chromosome to all chromosomes
|
394
389
|
form.chrom.data = request.args.get("chrom", "")
|
395
390
|
|
396
|
-
# populate filters dropdown
|
397
|
-
available_filters = list(store.filters(institute_obj["_id"], category))
|
398
|
-
form.filters.choices = [
|
399
|
-
(filter.get("_id"), filter.get("display_name")) for filter in available_filters
|
400
|
-
]
|
401
|
-
|
402
391
|
# Populate chromosome select choices
|
403
392
|
controllers.populate_chrom_choices(form, case_obj)
|
404
393
|
|
394
|
+
# Populate custom soft filters
|
395
|
+
controllers.populate_institute_soft_filters(form=form, institute_obj=institute_obj)
|
396
|
+
|
405
397
|
# populate available panel choices
|
406
398
|
form.gene_panels.choices = controllers.gene_panel_choices(store, institute_obj, case_obj)
|
407
399
|
|
@@ -431,7 +423,9 @@ def mei_variants(institute_id, case_name):
|
|
431
423
|
cytobands=cytobands,
|
432
424
|
dismiss_variant_options=DISMISS_VARIANT_OPTIONS,
|
433
425
|
expand_search=controllers.get_expand_search(request.form),
|
434
|
-
filters=
|
426
|
+
filters=controllers.populate_persistent_filters_choices(
|
427
|
+
institute_id=institute_id, category=category, form=form
|
428
|
+
),
|
435
429
|
form=form,
|
436
430
|
inherit_palette=INHERITANCE_PALETTE,
|
437
431
|
institute=institute_obj,
|
@@ -507,15 +501,12 @@ def cancer_variants(institute_id, case_name):
|
|
507
501
|
# update status of case if visited for the first time
|
508
502
|
controllers.activate_case(store, institute_obj, case_obj, current_user)
|
509
503
|
|
510
|
-
# populate filters dropdown
|
511
|
-
available_filters = list(store.filters(institute_id, category))
|
512
|
-
form.filters.choices = [
|
513
|
-
(filter.get("_id"), filter.get("display_name")) for filter in available_filters
|
514
|
-
]
|
515
|
-
|
516
504
|
# Populate chromosome select choices
|
517
505
|
controllers.populate_chrom_choices(form, case_obj)
|
518
506
|
|
507
|
+
# Populate custom soft filters
|
508
|
+
controllers.populate_institute_soft_filters(form=form, institute_obj=institute_obj)
|
509
|
+
|
519
510
|
form.gene_panels.choices = controllers.gene_panel_choices(store, institute_obj, case_obj)
|
520
511
|
|
521
512
|
genome_build = "38" if "38" in str(case_obj.get("genome_build", "37")) else "37"
|
@@ -550,7 +541,9 @@ def cancer_variants(institute_id, case_name):
|
|
550
541
|
**CANCER_SPECIFIC_VARIANT_DISMISS_OPTIONS,
|
551
542
|
},
|
552
543
|
expand_search=controllers.get_expand_search(request.form),
|
553
|
-
filters=
|
544
|
+
filters=controllers.populate_persistent_filters_choices(
|
545
|
+
institute_id=institute_id, category=category, form=form
|
546
|
+
),
|
554
547
|
result_size=result_size,
|
555
548
|
show_dismiss_block=controllers.get_show_dismiss_block(),
|
556
549
|
total_variants=variants_stats.get(variant_type, {}).get(category, "NA"),
|
@@ -574,9 +567,6 @@ def cancer_sv_variants(institute_id, case_name):
|
|
574
567
|
variant_type = "clinical"
|
575
568
|
variants_stats = store.case_variants_count(case_obj["_id"], institute_id, variant_type, False)
|
576
569
|
|
577
|
-
if request.form.get("hpo_clinical_filter"):
|
578
|
-
case_obj["hpo_clinical_filter"] = True
|
579
|
-
|
580
570
|
if "dismiss_submit" in request.form: # dismiss a list of variants
|
581
571
|
controllers.dismiss_variant_list(
|
582
572
|
store,
|
@@ -591,15 +581,12 @@ def cancer_sv_variants(institute_id, case_name):
|
|
591
581
|
controllers.activate_case(store, institute_obj, case_obj, current_user)
|
592
582
|
form = controllers.populate_sv_filters_form(store, institute_obj, case_obj, category, request)
|
593
583
|
|
594
|
-
# populate filters dropdown
|
595
|
-
available_filters = list(store.filters(institute_obj["_id"], category))
|
596
|
-
form.filters.choices = [
|
597
|
-
(filter.get("_id"), filter.get("display_name")) for filter in available_filters
|
598
|
-
]
|
599
|
-
|
600
584
|
# Populate chromosome select choices
|
601
585
|
controllers.populate_chrom_choices(form, case_obj)
|
602
586
|
|
587
|
+
# Populate custom soft filters
|
588
|
+
controllers.populate_institute_soft_filters(form=form, institute_obj=institute_obj)
|
589
|
+
|
603
590
|
genome_build = "38" if "38" in str(case_obj.get("genome_build", "37")) else "37"
|
604
591
|
cytobands = store.cytoband_by_chrom(genome_build)
|
605
592
|
|
@@ -630,7 +617,9 @@ def cancer_sv_variants(institute_id, case_name):
|
|
630
617
|
**CANCER_SPECIFIC_VARIANT_DISMISS_OPTIONS,
|
631
618
|
},
|
632
619
|
expand_search=controllers.get_expand_search(request.form),
|
633
|
-
filters=
|
620
|
+
filters=controllers.populate_persistent_filters_choices(
|
621
|
+
institute_id=institute_id, category=category, form=form
|
622
|
+
),
|
634
623
|
form=form,
|
635
624
|
inherit_palette=INHERITANCE_PALETTE,
|
636
625
|
institute=institute_obj,
|
@@ -660,9 +649,6 @@ def fusion_variants(institute_id, case_name):
|
|
660
649
|
variant_type = "clinical"
|
661
650
|
variants_stats = store.case_variants_count(case_obj["_id"], institute_id, variant_type, False)
|
662
651
|
|
663
|
-
if request.form.get("hpo_clinical_filter"):
|
664
|
-
case_obj["hpo_clinical_filter"] = True
|
665
|
-
|
666
652
|
if "dismiss_submit" in request.form: # dismiss a list of variants
|
667
653
|
controllers.dismiss_variant_list(
|
668
654
|
store,
|
@@ -679,15 +665,12 @@ def fusion_variants(institute_id, case_name):
|
|
679
665
|
store, institute_obj, case_obj, category, request
|
680
666
|
)
|
681
667
|
|
682
|
-
# populate filters dropdown
|
683
|
-
available_filters = list(store.filters(institute_obj["_id"], category))
|
684
|
-
form.filters.choices = [
|
685
|
-
(filter.get("_id"), filter.get("display_name")) for filter in available_filters
|
686
|
-
]
|
687
|
-
|
688
668
|
# Populate chromosome select choices
|
689
669
|
controllers.populate_chrom_choices(form, case_obj)
|
690
670
|
|
671
|
+
# Populate custom soft filters
|
672
|
+
controllers.populate_institute_soft_filters(form=form, institute_obj=institute_obj)
|
673
|
+
|
691
674
|
genome_build = "38" if "38" in str(case_obj.get("genome_build", "37")) else "37"
|
692
675
|
cytobands = store.cytoband_by_chrom(genome_build)
|
693
676
|
|
@@ -717,7 +700,9 @@ def fusion_variants(institute_id, case_name):
|
|
717
700
|
**DISMISS_VARIANT_OPTIONS,
|
718
701
|
},
|
719
702
|
expand_search=controllers.get_expand_search(request.form),
|
720
|
-
filters=
|
703
|
+
filters=controllers.populate_persistent_filters_choices(
|
704
|
+
institute_id=institute_id, category=category, form=form
|
705
|
+
),
|
721
706
|
form=form,
|
722
707
|
institute=institute_obj,
|
723
708
|
manual_rank_options=MANUAL_RANK_OPTIONS,
|
@@ -1,10 +1,10 @@
|
|
1
1
|
"""
|
2
|
-
|
2
|
+
Connect to BioNano Access server via its API.
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
The server API we connect to is described in the following document:
|
5
|
+
https://bionano.com/wp-content/uploads/2023/01/30462-Bionano-Access-API-Guide-1.pdf
|
6
|
+
For further development, the server has a Swagger-like demo interface at https://bionano-access.scilifelab.se/Bnx/
|
7
|
+
which is useful for details, and for sniffing actual message content structure, required cookie variable names etc.
|
8
8
|
"""
|
9
9
|
|
10
10
|
import json
|
@@ -1,5 +1,5 @@
|
|
1
1
|
import logging
|
2
|
-
from typing import Dict
|
2
|
+
from typing import Dict, List
|
3
3
|
|
4
4
|
import requests
|
5
5
|
from flask import current_app
|
@@ -8,6 +8,9 @@ REF_CHROM = "14"
|
|
8
8
|
MT_CHROM = "MT"
|
9
9
|
LOG = logging.getLogger(__name__)
|
10
10
|
|
11
|
+
CHANJO_BUILD_37 = "GRCh37"
|
12
|
+
CHANJO_BUILD_38 = "GRCh38"
|
13
|
+
|
11
14
|
|
12
15
|
class Chanjo2Client:
|
13
16
|
"""Runs requests to chanjo2 and returns results in the expected format."""
|
@@ -44,3 +47,39 @@ class Chanjo2Client:
|
|
44
47
|
coverage_stats[ind["individual_id"]] = coverage_info
|
45
48
|
|
46
49
|
return coverage_stats
|
50
|
+
|
51
|
+
def get_gene_complete_coverage(
|
52
|
+
self, hgnc_id: int, threshold: int = 15, individuals: dict = {}, build: str = "38"
|
53
|
+
) -> bool:
|
54
|
+
"""
|
55
|
+
Return complete coverage for hgnc_id at a coverage threshold.
|
56
|
+
"""
|
57
|
+
chanjo_build = CHANJO_BUILD_37 if "37" in build else CHANJO_BUILD_38
|
58
|
+
chanjo2_gene_cov_url: str = "/".join(
|
59
|
+
[current_app.config.get("CHANJO2_URL"), "coverage/d4/genes/summary"]
|
60
|
+
)
|
61
|
+
|
62
|
+
gene_cov_query = {
|
63
|
+
"build": chanjo_build,
|
64
|
+
"coverage_threshold": threshold,
|
65
|
+
"hgnc_gene_ids": [hgnc_id],
|
66
|
+
"interval_type": "genes",
|
67
|
+
"samples": [],
|
68
|
+
}
|
69
|
+
for ind in individuals:
|
70
|
+
if not ind.get("d4_file"):
|
71
|
+
continue
|
72
|
+
|
73
|
+
gene_cov_query["samples"].append(
|
74
|
+
{"coverage_file_path": ind["d4_file"], "name": ind["individual_id"]}
|
75
|
+
)
|
76
|
+
|
77
|
+
resp = requests.post(chanjo2_gene_cov_url, json=gene_cov_query)
|
78
|
+
gene_cov = resp.json()
|
79
|
+
|
80
|
+
full_coverage = bool(gene_cov)
|
81
|
+
for sample in gene_cov.keys():
|
82
|
+
if gene_cov[sample]["coverage_completeness_percent"] < 100:
|
83
|
+
full_coverage = False
|
84
|
+
|
85
|
+
return full_coverage
|
@@ -1,5 +1,5 @@
|
|
1
1
|
"""
|
2
|
-
|
2
|
+
Generate coverage reports using chanjo and chanjo-report. Documentation under -> `docs/admin-guide/chanjo_coverage_integration.md`
|
3
3
|
"""
|
4
4
|
|
5
5
|
import json
|
@@ -1,5 +1,7 @@
|
|
1
1
|
import json
|
2
2
|
import logging
|
3
|
+
from io import StringIO
|
4
|
+
from typing import Optional, Tuple
|
3
5
|
|
4
6
|
import requests
|
5
7
|
from flask import flash
|
@@ -17,6 +19,7 @@ class ClinVarApi:
|
|
17
19
|
|
18
20
|
def init_app(self, app):
|
19
21
|
self.convert_service = "/".join([PRECLINVAR_URL, "csv_2_json"])
|
22
|
+
self.delete_service = "/".join([PRECLINVAR_URL, "delete"])
|
20
23
|
self.submit_service_url = app.config.get("CLINVAR_API_URL") or CLINVAR_API_URL_DEFAULT
|
21
24
|
|
22
25
|
def set_header(self, api_key) -> dict:
|
@@ -77,10 +80,61 @@ class ClinVarApi:
|
|
77
80
|
except Exception as ex:
|
78
81
|
return self.submit_service_url, None, ex
|
79
82
|
|
80
|
-
def
|
83
|
+
def json_submission_status(self, submission_id: str, api_key=None) -> dict:
|
81
84
|
"""Retrieve the status of a ClinVar submission using the https://submit.ncbi.nlm.nih.gov/api/v1/submissions/SUBnnnnnn/actions/ endpoint."""
|
82
85
|
|
83
86
|
header: dict = self.set_header(api_key)
|
84
87
|
actions_url = f"{self.submit_service_url}{submission_id}/actions/"
|
85
88
|
actions_resp: requests.models.Response = requests.get(actions_url, headers=header)
|
86
|
-
|
89
|
+
return actions_resp.json()
|
90
|
+
|
91
|
+
def get_clinvar_scv_accession(self, url: str) -> Optional[str]:
|
92
|
+
"""Downloads a submission summary from the given URL into a temporary file and parses this file to retrieve a SCV accession, if available."""
|
93
|
+
# Send a GET request to download the file
|
94
|
+
response = requests.get(url)
|
95
|
+
response.raise_for_status() # Raise an error if the request failed
|
96
|
+
|
97
|
+
# Use an in-memory file-like object to hold the JSON content
|
98
|
+
with StringIO(response.text) as memory_file:
|
99
|
+
|
100
|
+
json_data = json.load(memory_file)
|
101
|
+
|
102
|
+
submission_data: dict = json_data["submissions"][0]
|
103
|
+
processing_status: str = submission_data["processingStatus"]
|
104
|
+
if processing_status != "Success":
|
105
|
+
flash(
|
106
|
+
f"Could not delete provided submission because its processing status is '{processing_status}'.",
|
107
|
+
"warning",
|
108
|
+
)
|
109
|
+
return
|
110
|
+
return submission_data["identifiers"]["clinvarAccession"]
|
111
|
+
|
112
|
+
def delete_clinvar_submission(self, submission_id: str, api_key=None) -> Tuple[int, dict]:
|
113
|
+
"""Remove a successfully processed submission from ClinVar."""
|
114
|
+
|
115
|
+
try:
|
116
|
+
submission_status_doc: dict = self.json_submission_status(
|
117
|
+
submission_id=submission_id, api_key=api_key
|
118
|
+
)
|
119
|
+
|
120
|
+
subm_response: dict = submission_status_doc["actions"][0]["responses"][0]
|
121
|
+
submission_status = subm_response["status"]
|
122
|
+
|
123
|
+
if submission_status != "processed":
|
124
|
+
return (
|
125
|
+
500,
|
126
|
+
f"Clinvar submission status should be 'processed' and in order to attempt data deletion. Submission status is '{submission_status}'.",
|
127
|
+
)
|
128
|
+
|
129
|
+
# retrieve ClinVar SCV accession (SCVxxxxxxxx) from file url returned by subm_response
|
130
|
+
subm_summary_url: str = subm_response["files"][0]["url"]
|
131
|
+
scv_accession: Optional(str) = self.get_clinvar_scv_accession(url=subm_summary_url)
|
132
|
+
|
133
|
+
# Remove ClinVar submission using preClinVar's 'delete' endpoint
|
134
|
+
resp = requests.post(
|
135
|
+
self.delete_service, data={"api_key": api_key, "clinvar_accession": scv_accession}
|
136
|
+
)
|
137
|
+
return resp.status_code, resp.json()
|
138
|
+
|
139
|
+
except Exception as ex:
|
140
|
+
return 500, str(ex)
|