scout-browser 4.88.1__py3-none-any.whl → 4.89__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- scout/__version__.py +1 -1
- scout/adapter/mongo/base.py +1 -1
- scout/adapter/mongo/case.py +185 -117
- scout/adapter/mongo/omics_variant.py +19 -0
- scout/build/case.py +1 -0
- scout/commands/load/variants.py +121 -40
- scout/commands/update/case.py +56 -10
- scout/constants/case_tags.py +5 -0
- scout/constants/disease_parsing.py +2 -2
- scout/constants/igv_tracks.py +2 -2
- scout/constants/indexes.py +8 -1
- scout/demo/643594.config.yaml +1 -0
- scout/demo/panel_1.txt +2 -0
- scout/demo/resources/ensembl_exons_37_reduced.txt +135 -0
- scout/demo/resources/ensembl_exons_38_reduced.txt +166 -0
- scout/demo/resources/ensembl_genes_37_reduced.txt +2 -0
- scout/demo/resources/ensembl_genes_38_reduced.txt +2 -0
- scout/demo/resources/ensembl_transcripts_37_reduced.txt +27 -0
- scout/demo/resources/ensembl_transcripts_38_reduced.txt +36 -0
- scout/demo/resources/hgnc_reduced_set.txt +2 -0
- scout/log/handlers.py +2 -1
- scout/log/log.py +48 -61
- scout/models/case/case_loading_models.py +2 -0
- scout/parse/omim.py +2 -2
- scout/server/app.py +23 -7
- scout/server/blueprints/alignviewers/controllers.py +46 -23
- scout/server/blueprints/cases/controllers.py +21 -47
- scout/server/blueprints/cases/templates/cases/case_report.html +4 -1
- scout/server/blueprints/cases/templates/cases/case_sma.html +19 -0
- scout/server/blueprints/cases/templates/cases/collapsible_actionbar.html +7 -7
- scout/server/blueprints/cases/templates/cases/individuals_table.html +1 -1
- scout/server/blueprints/clinvar/form.py +1 -1
- scout/server/blueprints/clinvar/templates/clinvar/clinvar_submissions.html +2 -2
- scout/server/blueprints/clinvar/templates/clinvar/multistep_add_variant.html +9 -3
- scout/server/blueprints/institutes/controllers.py +11 -38
- scout/server/blueprints/institutes/forms.py +18 -4
- scout/server/blueprints/institutes/templates/overview/cases.html +137 -46
- scout/server/blueprints/login/views.py +16 -12
- scout/server/blueprints/panels/controllers.py +4 -1
- scout/server/blueprints/panels/templates/panels/panel.html +1 -1
- scout/server/blueprints/panels/templates/panels/panels.html +5 -5
- scout/server/blueprints/public/templates/public/index.html +18 -10
- scout/server/blueprints/variant/templates/variant/components.html +0 -1
- scout/server/blueprints/variant/templates/variant/gene_disease_relations.html +1 -1
- scout/server/config.py +3 -0
- scout/server/extensions/__init__.py +2 -0
- scout/server/extensions/chanjo2_extension.py +46 -0
- scout/server/extensions/chanjo_extension.py +44 -1
- scout/server/extensions/matchmaker_extension.py +0 -1
- scout/server/links.py +11 -2
- scout/server/templates/report_base.html +1 -0
- scout/utils/convert.py +1 -1
- scout/utils/date.py +1 -1
- {scout_browser-4.88.1.dist-info → scout_browser-4.89.dist-info}/METADATA +1 -1
- {scout_browser-4.88.1.dist-info → scout_browser-4.89.dist-info}/RECORD +59 -58
- {scout_browser-4.88.1.dist-info → scout_browser-4.89.dist-info}/LICENSE +0 -0
- {scout_browser-4.88.1.dist-info → scout_browser-4.89.dist-info}/WHEEL +0 -0
- {scout_browser-4.88.1.dist-info → scout_browser-4.89.dist-info}/entry_points.txt +0 -0
- {scout_browser-4.88.1.dist-info → scout_browser-4.89.dist-info}/top_level.txt +0 -0
@@ -1,7 +1,6 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
import datetime
|
3
3
|
import itertools
|
4
|
-
import json
|
5
4
|
import logging
|
6
5
|
import os
|
7
6
|
from typing import Dict, List, Set
|
@@ -21,6 +20,7 @@ from scout.constants import (
|
|
21
20
|
CASE_TAGS,
|
22
21
|
CUSTOM_CASE_REPORTS,
|
23
22
|
DATE_DAY_FORMATTER,
|
23
|
+
INHERITANCE_PALETTE,
|
24
24
|
MITODEL_HEADER,
|
25
25
|
MT_COV_STATS_HEADER,
|
26
26
|
MT_EXPORT_HEADER,
|
@@ -41,7 +41,16 @@ from scout.export.variant import export_mt_variants
|
|
41
41
|
from scout.parse.matchmaker import genomic_features, hpo_terms, omim_terms, parse_matches
|
42
42
|
from scout.server.blueprints.variant.controllers import variant as variant_decorator
|
43
43
|
from scout.server.blueprints.variants.controllers import get_manual_assessments
|
44
|
-
from scout.server.extensions import
|
44
|
+
from scout.server.extensions import (
|
45
|
+
RerunnerError,
|
46
|
+
bionano_access,
|
47
|
+
chanjo2,
|
48
|
+
chanjo_report,
|
49
|
+
gens,
|
50
|
+
matchmaker,
|
51
|
+
rerunner,
|
52
|
+
store,
|
53
|
+
)
|
45
54
|
from scout.server.links import disease_link
|
46
55
|
from scout.server.utils import (
|
47
56
|
case_has_alignments,
|
@@ -692,6 +701,7 @@ def case_report_content(store: MongoAdapter, institute_obj: dict, case_obj: dict
|
|
692
701
|
category="case", institute=institute_obj, case=case_obj, verb="filter_audit"
|
693
702
|
)
|
694
703
|
|
704
|
+
data["inherit_palette"] = INHERITANCE_PALETTE
|
695
705
|
data["manual_rank_options"] = MANUAL_RANK_OPTIONS
|
696
706
|
data["genetic_models"] = dict(GENETIC_MODELS)
|
697
707
|
data["report_created_at"] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M")
|
@@ -701,48 +711,6 @@ def case_report_content(store: MongoAdapter, institute_obj: dict, case_obj: dict
|
|
701
711
|
return data
|
702
712
|
|
703
713
|
|
704
|
-
def mt_coverage_stats(individuals, ref_chrom="14") -> dict:
|
705
|
-
"""Send a request to chanjo report endpoint to retrieve MT vs autosome coverage stats
|
706
|
-
|
707
|
-
Args:
|
708
|
-
individuals(dict): case_obj["individuals"] object
|
709
|
-
ref_chrom(str): reference chromosome (1-22)
|
710
|
-
|
711
|
-
Returns:
|
712
|
-
coverage_stats(dict): a dictionary with mean MT and autosome transcript coverage stats
|
713
|
-
"""
|
714
|
-
coverage_stats = {}
|
715
|
-
ind_ids = []
|
716
|
-
for ind in individuals:
|
717
|
-
ind_ids.append(ind["individual_id"])
|
718
|
-
|
719
|
-
# Prepare complete url to Chanjo report chromosome mean coverage calculation endpoint
|
720
|
-
cov_calc_url = url_for("report.json_chrom_coverage", _external=True)
|
721
|
-
# Prepare request data to calculate mean MT coverage
|
722
|
-
data = dict(sample_ids=",".join(ind_ids), chrom="MT")
|
723
|
-
# Send POST request with data to chanjo endpoint
|
724
|
-
resp = requests.post(cov_calc_url, json=data)
|
725
|
-
mt_cov_data = json.loads(resp.text)
|
726
|
-
|
727
|
-
# Change request data to calculate mean autosomal coverage
|
728
|
-
data["chrom"] = str(ref_chrom) # convert to string if an int is provided
|
729
|
-
# Send POST request with data to chanjo endpoint
|
730
|
-
resp = requests.post(cov_calc_url, json=data)
|
731
|
-
ref_cov_data = json.loads(resp.text) # mean coverage over the transcripts of ref chrom
|
732
|
-
|
733
|
-
for ind in ind_ids:
|
734
|
-
if not (mt_cov_data.get(ind) and ref_cov_data.get(ind)):
|
735
|
-
continue
|
736
|
-
coverage_info = dict(
|
737
|
-
mt_coverage=round(mt_cov_data[ind], 2),
|
738
|
-
autosome_cov=round(ref_cov_data[ind], 2),
|
739
|
-
mt_copy_number=round((mt_cov_data[ind] / ref_cov_data[ind]) * 2, 2),
|
740
|
-
)
|
741
|
-
coverage_stats[ind] = coverage_info
|
742
|
-
|
743
|
-
return coverage_stats
|
744
|
-
|
745
|
-
|
746
714
|
def mt_excel_files(store, case_obj, temp_excel_dir):
|
747
715
|
"""Collect MT variants and format line of a MT variant report
|
748
716
|
to be exported in excel format. Create mt excel files, one for each sample,
|
@@ -760,9 +728,15 @@ def mt_excel_files(store, case_obj, temp_excel_dir):
|
|
760
728
|
today = datetime.datetime.now().strftime(DATE_DAY_FORMATTER)
|
761
729
|
samples = case_obj.get("individuals")
|
762
730
|
coverage_stats = None
|
763
|
-
|
764
|
-
|
765
|
-
|
731
|
+
|
732
|
+
case_has_chanjo_coverage(case_obj)
|
733
|
+
case_has_chanjo2_coverage(case_obj)
|
734
|
+
|
735
|
+
# Check if coverage and MT copy number stats are available via chanjo2 or chanjo
|
736
|
+
if case_obj.get("chanjo2_coverage"):
|
737
|
+
coverage_stats: Dict[str, dict] = chanjo2.mt_coverage_stats(individuals=samples)
|
738
|
+
elif case_obj.get("chanjo_coverage"):
|
739
|
+
coverage_stats: Dict[str, dict] = chanjo_report.mt_coverage_stats(individuals=samples)
|
766
740
|
|
767
741
|
query = {"chrom": "MT"}
|
768
742
|
mt_variants = list(
|
@@ -1,5 +1,6 @@
|
|
1
1
|
{% from "cases/utils.html" import variant_transcripts, filter_audits %}
|
2
2
|
{% from "utils.html" import comments_table, variant_related_comments_table %}
|
3
|
+
{% from "variant/gene_disease_relations.html" import inheritance_badge %}
|
3
4
|
{% from "variants/components.html" import fusion_variants_header, default_fusion_variant_cells %}
|
4
5
|
|
5
6
|
{% extends "report_base.html" %}
|
@@ -712,7 +713,9 @@
|
|
712
713
|
<td>
|
713
714
|
<ul class="p-0" style="list-style-type: none;">
|
714
715
|
{% for disease_term in gene.disease_terms %}
|
715
|
-
<li class="d-flex align-items-baseline"><span class="badge bg-secondary m-1">{{ disease_term._id}}</span> <span>{{ disease_term.description }} {
|
716
|
+
<li class="d-flex align-items-baseline"><span class="badge bg-secondary m-1">{{ disease_term._id}}</span> <span>{{ disease_term.description }} {% if disease_term.source != 'ORPHA' and disease_term.inheritance %}
|
717
|
+
{% for model in disease_term.inheritance %} {{ inheritance_badge(model,inherit_palette) }}{% endfor %}
|
718
|
+
{% endif %}</span></li>
|
716
719
|
{% endfor %}
|
717
720
|
</ul>
|
718
721
|
</td>
|
@@ -50,6 +50,25 @@
|
|
50
50
|
</div>
|
51
51
|
</div> <!-- end of div class row -->
|
52
52
|
|
53
|
+
<div class="d-flex">
|
54
|
+
{% if case.vcf_files.vcf_snv %}
|
55
|
+
<span class="me-3">
|
56
|
+
<form action="{{url_for('variants.variants', institute_id=institute._id, case_name=case.display_name) }}">
|
57
|
+
<input type="hidden" id="hgnc_symbols" name="hgnc_symbols" value="SMN1, SMN2"></input>
|
58
|
+
<input type="hidden" id="gene_panels" name="gene_panels" value="['']"></input>
|
59
|
+
<span><button type="submit" class="btn btn-secondary btn-sm" target="_blank" rel="noopener" data-bs-toggle="tooltip" title="SNV and INDEL variants view filtered for the genes SMN1 and SMN2">SNVs</button></span>
|
60
|
+
</form>
|
61
|
+
</span>
|
62
|
+
{% endif %}
|
63
|
+
{% if case.vcf_files.vcf_sv %}
|
64
|
+
<form action="{{url_for('variants.sv_variants', institute_id=institute._id, case_name=case.display_name) }}">
|
65
|
+
<input type="hidden" id="hgnc_symbols" name="hgnc_symbols" value="SMN1, SMN2"></input>
|
66
|
+
<input type="hidden" id="gene_panels" name="gene_panels" value="['']"></input>
|
67
|
+
<button type="submit" class="btn btn-secondary btn-sm" target="_blank" rel="noopener" data-bs-toggle="tooltip" title="Structural variants view filtered for the genes SMN1 and SMN2">SVs</button></span>
|
68
|
+
</form>
|
69
|
+
{% endif %}
|
70
|
+
</div>
|
71
|
+
|
53
72
|
<div class="row">
|
54
73
|
<div class="col-sm-12">{{activity_panel(events)}}</div>
|
55
74
|
</div>
|
@@ -86,7 +86,7 @@
|
|
86
86
|
</div>
|
87
87
|
|
88
88
|
<!-- If connected to a chanjo or chanjo2 instance, display coverage report -->
|
89
|
-
{% if case.chanjo2_coverage
|
89
|
+
{% if case.chanjo2_coverage %}
|
90
90
|
<div href="#" class="bg-dark list-group-item text-white">
|
91
91
|
<div class="d-flex flex-row flex-fill bd-highlight">
|
92
92
|
<span class="menu-collapsed">Coverage (chanjo2)</span>
|
@@ -121,11 +121,6 @@
|
|
121
121
|
</div>
|
122
122
|
{% endif %}
|
123
123
|
|
124
|
-
<!-- Custom case reports as in constants.CUSTOM_CASE_REPORTS -->
|
125
|
-
{% for _, report_data in report_types.items() %}
|
126
|
-
{{ custom_case_report(institute, case, report_data) }}
|
127
|
-
{% endfor %}
|
128
|
-
|
129
124
|
<!-- Delivery reports for old analyses -->
|
130
125
|
{% if case.analyses %}
|
131
126
|
{% for analysis in case.analyses %}
|
@@ -134,6 +129,11 @@
|
|
134
129
|
{% endif %}
|
135
130
|
{% endfor %}
|
136
131
|
{% endif %}
|
132
|
+
|
133
|
+
<!-- Custom case reports as in constants.CUSTOM_CASE_REPORTS -->
|
134
|
+
{% for _, report_data in report_types.items() %}
|
135
|
+
{{ custom_case_report(institute, case, report_data) }}
|
136
|
+
{% endfor %}
|
137
137
|
</div>
|
138
138
|
<!-- end reports macro -->
|
139
139
|
{% endmacro %}
|
@@ -144,7 +144,7 @@
|
|
144
144
|
<div href="#" class="bg-dark list-group-item text-white">
|
145
145
|
<div class="d-flex flex-row bd-highlight mt-1">
|
146
146
|
<div>
|
147
|
-
<span class="menu-collapsed">{{report_key|replace("_"," ")|title}}</span>
|
147
|
+
<span class="menu-collapsed">{{report_key|replace("_"," ")|title|upper_na}}</span>
|
148
148
|
</div>
|
149
149
|
{% if case.get(report_key) is existing %}
|
150
150
|
<div>
|
@@ -29,7 +29,7 @@
|
|
29
29
|
<tbody>
|
30
30
|
{% for ind in case.individuals %}
|
31
31
|
<tr {% if ind.phenotype_human == 'tumor' %} class="bg-danger-light" {% endif %}>
|
32
|
-
<td>{{ ind.display_name }}{% if gens_info.display
|
32
|
+
<td>{{ ind.display_name }}{% if gens_info.display %}
|
33
33
|
<a style="font-size: .8rem;" class="btn btn-secondary btn-sm float-end" target="_blank" href="http://{{gens_info.host}}/{{ind.display_name}}?&genome_build={{case.genome_build}}&case_id={{case._id}}&individual_id={{ind.individual_id}}">CN profile</a>
|
34
34
|
{% endif %}</td>
|
35
35
|
{% if ind.phenotype_human == "normal" %}
|
@@ -55,7 +55,7 @@ class ClinVarVariantForm(FlaskForm):
|
|
55
55
|
clinsig = SelectField(
|
56
56
|
"Germline classification", choices=[(item, item) for item in GERMLINE_CLASSIF_TERMS[:5]]
|
57
57
|
)
|
58
|
-
clinsig_comment = TextAreaField("Comment on
|
58
|
+
clinsig_comment = TextAreaField("Comment on classification")
|
59
59
|
clinsig_cit = TextAreaField("Clinical significance citations (with identifier)")
|
60
60
|
last_evaluated = DateField("Date evaluated")
|
61
61
|
hpo_terms = MultiCheckboxField("Case-associated HPO terms", choices=[])
|
@@ -160,7 +160,7 @@
|
|
160
160
|
<td>{{subm_variant.clinsig}}</td>
|
161
161
|
<td>{{subm_variant.added_by or "N/A"}}</td>
|
162
162
|
<form id="delete_variant_{{subm_variant._id}}" action="{{ url_for('clinvar.clinvar_delete_object', submission=subm_obj._id, object_type='variant_data') }}" method="POST">
|
163
|
-
<td><button type="submit" name="delete_object" value="{{subm_variant._id}}" class="btn btn-danger btn-xs"><span class="fa fa-trash
|
163
|
+
<td><button type="submit" name="delete_object" value="{{subm_variant._id}}" class="btn btn-danger btn-xs"><span class="fa fa-trash" aria-hidden="true"></span></button></td>
|
164
164
|
</form>
|
165
165
|
</tr>
|
166
166
|
<tr>
|
@@ -217,7 +217,7 @@
|
|
217
217
|
<td>{{var_key_name[case.linking_id]|truncate(25,true,'..')}}</td>
|
218
218
|
<td>{{case.allele_origin}}</td>
|
219
219
|
<form id="delete_casedata_{{case._id}}" action="{{ url_for('clinvar.clinvar_delete_object', submission=subm_obj._id, object_type='case_data') }}" method="POST">
|
220
|
-
<td><button type="submit" name="delete_object" value="{{case._id}}" class="btn btn-danger btn-xs"><span class="fa fa-trash
|
220
|
+
<td><button type="submit" name="delete_object" value="{{case._id}}" class="btn btn-danger btn-xs"><span class="fa fa-trash" aria-hidden="true"></span></button></td>
|
221
221
|
</form>
|
222
222
|
</tr>
|
223
223
|
<tr>
|
@@ -209,18 +209,24 @@
|
|
209
209
|
<legend class="text-dark">Germline Classification</legend>
|
210
210
|
<h3 class="fs-subtitle">Represents a variant-level classification for a disease, rather than an interpretation of the clinical significance of a variant for a specific patient.</h3>
|
211
211
|
|
212
|
+
{% set scout_classif = variant_data.var_obj.classification|replace("_"," ") if variant_data.var_obj.classification else "n.a." %}
|
213
|
+
|
212
214
|
<div class="row">
|
213
215
|
<div class="col-4">
|
214
216
|
<span class="text-dark">Classification in Scout:</span><br>
|
215
|
-
<span class="badge bg-secondary">{{
|
217
|
+
<span class="badge bg-secondary">{{ scout_classif|upper }}</span>
|
216
218
|
</div>
|
217
219
|
<div class="col-8">
|
218
220
|
{{variant_data.var_form.clinsig.label(class="fw-bold, text-dark")}} <span class="text-danger" data-bs-toggle='tooltip' title="Required field"><strong>*</strong></span>
|
219
|
-
|
221
|
+
<select name="clinsig" class="form-control, btn-secondary">
|
222
|
+
{% for classif, classif_value in variant_data.var_form.clinsig.choices %}
|
223
|
+
<option value="{{classif_value}}" {% if classif==scout_classif|capitalize %} selected {% endif %}>{{classif}}</option>
|
224
|
+
{% endfor %}
|
225
|
+
</select>
|
220
226
|
</div>
|
221
227
|
</div>
|
222
228
|
<br><br><br>
|
223
|
-
{{variant_data.var_form.clinsig_comment.label(class="fw-bold, text-dark")}} <span class="text-danger" data-bs-toggle='tooltip' title="
|
229
|
+
{{variant_data.var_form.clinsig_comment.label(class="fw-bold, text-dark")}} <span class="text-danger" data-bs-toggle='tooltip' title="Optional, but highly encouraged. Free text describing the rationale for the classification."><strong>?</strong></span>
|
224
230
|
{{variant_data.var_form.clinsig_comment(class="form-control, bg-white", placeholder="(optional)")}}
|
225
231
|
<br><br>
|
226
232
|
{{variant_data.var_form.clinsig_cit.label(class="fw-bold, text-dark")}}
|
@@ -10,13 +10,7 @@ from pymongo.cursor import Cursor
|
|
10
10
|
from werkzeug.datastructures import Headers, MultiDict
|
11
11
|
|
12
12
|
from scout.adapter.mongo.base import MongoAdapter
|
13
|
-
from scout.constants import
|
14
|
-
CASE_SEARCH_TERMS,
|
15
|
-
CASE_STATUSES,
|
16
|
-
DATE_DAY_FORMATTER,
|
17
|
-
ID_PROJECTION,
|
18
|
-
PHENOTYPE_GROUPS,
|
19
|
-
)
|
13
|
+
from scout.constants import CASE_STATUSES, DATE_DAY_FORMATTER, ID_PROJECTION, PHENOTYPE_GROUPS
|
20
14
|
from scout.server.blueprints.variant.utils import predictions, update_representative_gene
|
21
15
|
from scout.server.extensions import beacon, store
|
22
16
|
from scout.server.utils import institute_and_case, user_institutes
|
@@ -435,20 +429,14 @@ def cases(store, request, institute_id):
|
|
435
429
|
Returns:
|
436
430
|
data(dict): includes the cases, how many there are and the limit.
|
437
431
|
"""
|
438
|
-
data = {
|
432
|
+
data = {}
|
439
433
|
institute_obj = institute_and_case(store, institute_id)
|
440
434
|
data["institute"] = institute_obj
|
441
|
-
|
442
|
-
|
443
|
-
name_query = "".join(
|
444
|
-
[
|
445
|
-
request.args.get("search_type"),
|
446
|
-
request.args["search_term"].strip(),
|
447
|
-
]
|
448
|
-
)
|
449
|
-
data["name_query"] = name_query
|
435
|
+
|
436
|
+
name_query = request.form
|
450
437
|
limit = int(request.args.get("search_limit")) if request.args.get("search_limit") else 100
|
451
|
-
|
438
|
+
|
439
|
+
data["form"] = CaseFilterForm(request.form)
|
452
440
|
|
453
441
|
ALL_CASES_PROJECTION = {
|
454
442
|
"analysis_date": 1,
|
@@ -479,7 +467,6 @@ def cases(store, request, institute_id):
|
|
479
467
|
# local function to add info to case obj
|
480
468
|
def populate_case_obj(case_obj):
|
481
469
|
analysis_types = set(ind["analysis_type"] for ind in case_obj["individuals"])
|
482
|
-
LOG.debug("Analysis types found in %s: %s", case_obj["_id"], ",".join(analysis_types))
|
483
470
|
if len(analysis_types) > 1:
|
484
471
|
LOG.debug("Set analysis types to {'mixed'}")
|
485
472
|
analysis_types = set(["mixed"])
|
@@ -523,11 +510,11 @@ def cases(store, request, institute_id):
|
|
523
510
|
all_cases = store.cases(
|
524
511
|
collaborator=institute_id,
|
525
512
|
name_query=name_query,
|
526
|
-
skip_assigned=request.
|
527
|
-
is_research=request.
|
528
|
-
has_rna_data=request.
|
529
|
-
verification_pending=request.
|
530
|
-
has_clinvar_submission=request.
|
513
|
+
skip_assigned=request.form.get("skip_assigned"),
|
514
|
+
is_research=request.form.get("is_research"),
|
515
|
+
has_rna_data=request.form.get("has_rna"),
|
516
|
+
verification_pending=request.form.get("validation_ordered"),
|
517
|
+
has_clinvar_submission=request.form.get("clinvar_submitted"),
|
531
518
|
projection=ALL_CASES_PROJECTION,
|
532
519
|
)
|
533
520
|
all_cases = _sort_cases(data, request, all_cases)
|
@@ -550,20 +537,6 @@ def cases(store, request, institute_id):
|
|
550
537
|
return data
|
551
538
|
|
552
539
|
|
553
|
-
def populate_case_filter_form(params):
|
554
|
-
"""Populate filter form with params previosly submitted by user
|
555
|
-
|
556
|
-
Args:
|
557
|
-
params(werkzeug.datastructures.ImmutableMultiDict)
|
558
|
-
|
559
|
-
Returns:
|
560
|
-
form(scout.server.blueprints.cases.forms.CaseFilterForm)
|
561
|
-
"""
|
562
|
-
form = CaseFilterForm(params)
|
563
|
-
form.search_type.default = params.get("search_type")
|
564
|
-
return form
|
565
|
-
|
566
|
-
|
567
540
|
def _get_unevaluated_variants_for_case(
|
568
541
|
case_obj: dict, var_ids_list: List[str], sanger_validated_by_user_by_case: Dict[str, List[str]]
|
569
542
|
) -> Tuple[Dict[str, list]]:
|
@@ -157,11 +157,25 @@ class GeneVariantFiltersForm(FlaskForm):
|
|
157
157
|
|
158
158
|
|
159
159
|
class CaseFilterForm(FlaskForm):
|
160
|
-
"""Takes care of cases filtering
|
161
|
-
|
162
|
-
|
163
|
-
|
160
|
+
"""Takes care of cases filtering on cases page"""
|
161
|
+
|
162
|
+
case = StringField("Case or Individual name", [validators.Optional()])
|
163
|
+
exact_pheno = StringField("HPO terms, comma-separated", [validators.Optional()])
|
164
|
+
exact_dia = StringField("Disease terms, comma-separated", [validators.Optional()])
|
165
|
+
synopsis = StringField("Synopsis", [validators.Optional()])
|
166
|
+
panel = StringField("Gene panel name", [validators.Optional()])
|
167
|
+
status = StringField("Case status", [validators.Optional()])
|
168
|
+
tags = StringField("Case tag", [validators.Optional()])
|
169
|
+
track = StringField("Case track", [validators.Optional()])
|
170
|
+
pheno_group = StringField("Phenotype group", [validators.Optional()])
|
171
|
+
cohort = StringField("Cohort", [validators.Optional()])
|
172
|
+
similar_case = StringField("Similar case", [validators.Optional()])
|
173
|
+
similar_pheno = StringField("Similar phenotype", [validators.Optional()])
|
174
|
+
pinned = StringField("Pinned gene", [validators.Optional()])
|
175
|
+
causative = StringField("Causative gene", [validators.Optional()])
|
176
|
+
user = StringField("Assigned user", [validators.Optional()])
|
164
177
|
search_limit = IntegerField("Limit", [validators.Optional()], default=100)
|
178
|
+
advanced_search = BooleanField("Advanced search options")
|
165
179
|
skip_assigned = BooleanField("Hide assigned")
|
166
180
|
is_research = BooleanField("Research only")
|
167
181
|
clinvar_submitted = BooleanField("Has ClinVar submissions")
|
@@ -22,50 +22,133 @@
|
|
22
22
|
|
23
23
|
|
24
24
|
{% macro search_form() %}
|
25
|
-
<form action="{{ url_for('overview.cases', institute_id=institute._id) }}" method="GET" accept-charset="utf-8">
|
26
|
-
<div class="row d-flex align-items-end">
|
27
|
-
<div class="col-3">
|
28
|
-
{{ form.search_type.label(class="control-label") }}
|
29
|
-
{{ form.search_type(class="form-control") }}
|
30
|
-
</div>
|
31
|
-
<div class="col-3">
|
32
|
-
<label class="control-label" for="search_term">Search cases</label>
|
33
|
-
<input class="form-control" id="search_term" name="search_term" value="{{form.search_term.data if form.search_term.data}}" placeholder="example:18201" type="text" pattern="[^\\\<\>\?\!\=\/]*" title="Characters \<>?!=/ are not allowed">
|
34
|
-
</div>
|
35
|
-
<div class="col-1">
|
36
|
-
{{ form.search(class="btn btn-primary form-control") }}
|
37
|
-
</div>
|
38
|
-
<div class="col-1">
|
39
|
-
{{ form.search_limit.label(class="control-label") }}
|
40
|
-
{{ form.search_limit(class="form-control") }}
|
41
|
-
</div>
|
42
25
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
26
|
+
<!-- Collect advanced search key/values -->
|
27
|
+
{% set search = {} %}
|
28
|
+
{% for field in form._fields %}
|
29
|
+
{% if field not in ["search", "csrf_token", "advanced_search"] and form._fields[field].data %}
|
30
|
+
{% do search.update({field: form._fields[field].data}) %}
|
31
|
+
{% endif %}
|
32
|
+
{% endfor %}
|
33
|
+
|
34
|
+
<form action="{{ url_for('overview.cases', institute_id=institute._id) }}" method="POST" accept-charset="utf-8">
|
35
|
+
{{ form.hidden_tag() }}
|
36
|
+
|
37
|
+
<div class="row">
|
38
|
+
<div class="col-md-3 mb-3">
|
39
|
+
{{ form.case.label(class="form-label") }}
|
40
|
+
{{ form.case(class="form-control", placeholder="example:18201", pattern="[^\\\<\>\?\!\=\/]*", title="Characters \<>?!=/ are not allowed") }}
|
47
41
|
</div>
|
48
|
-
<div class="
|
49
|
-
|
50
|
-
|
42
|
+
<div class="col-md-1 mb-3">
|
43
|
+
{{ form.search_limit.label(class="form-label") }}
|
44
|
+
{{ form.search_limit(class="form-control") }}
|
51
45
|
</div>
|
52
|
-
<div class="
|
53
|
-
{{ form.
|
54
|
-
{{ form.has_rna.label(class="form-check-label") }}
|
46
|
+
<div class="d-grid btn-sm mb-3 col-md-2 mx-auto">
|
47
|
+
{{ form.search(class="btn btn-lg btn-primary mt-4") }}
|
55
48
|
</div>
|
56
|
-
|
57
|
-
|
58
|
-
<div class="form-check">
|
59
|
-
{{ form.clinvar_submitted(class="form-check-input") }}
|
60
|
-
{{ form.clinvar_submitted.label(class="form-check-label") }}
|
49
|
+
<div class="d-grid btn-sm mb-3 col-md-2 mx-auto">
|
50
|
+
<a href="{{ url_for('overview.cases', institute_id=institute._id) }}" class="btn btn-lg btn-secondary mt-4 text-white" role="button">Reset search</a>
|
61
51
|
</div>
|
62
|
-
<div class="form-check">
|
63
|
-
{{ form.
|
64
|
-
{{ form.
|
52
|
+
<div class="col-md-3 mb-3 form-check align-self-center mt-3 ms-3" data-bs-toggle="tooltip" title="Using multiple search criteria will narrow down your results as returned cases will contain all your searched conditions.">
|
53
|
+
{{ form.advanced_search(class="form-check-input") }}
|
54
|
+
{{ form.advanced_search.label(class="form-check-label") }}
|
65
55
|
</div>
|
66
|
-
</div>
|
67
56
|
</div>
|
57
|
+
<div class="row">
|
58
|
+
<div class="col-md-2 mb-3 form-check ms-3">
|
59
|
+
{{ form.skip_assigned(class="form-check-input") }}
|
60
|
+
{{ form.skip_assigned.label(class="form-check-label") }}
|
61
|
+
</div>
|
62
|
+
<div class="col-md-2 mb-3 form-check">
|
63
|
+
{{ form.is_research(class="form-check-input") }}
|
64
|
+
{{ form.is_research.label(class="form-check-label") }}
|
65
|
+
</div>
|
66
|
+
<div class="col-md-2 mb-3 form-check">
|
67
|
+
{{ form.clinvar_submitted(class="form-check-input") }}
|
68
|
+
{{ form.clinvar_submitted.label(class="form-check-label") }}
|
69
|
+
</div>
|
70
|
+
<div class="col-md-2 mb-3 form-check">
|
71
|
+
{{ form.has_rna(class="form-check-input") }}
|
72
|
+
{{ form.has_rna.label(class="form-check-label") }}
|
73
|
+
</div>
|
74
|
+
<div class="col-md-2 mb-3 form-check">
|
75
|
+
{{ form.validation_ordered(class="form-check-input") }}
|
76
|
+
{{ form.validation_ordered.label(class="form-check-label") }}
|
77
|
+
</div>
|
78
|
+
</div>
|
79
|
+
<!-- This div contains the advances search options -->
|
80
|
+
<div id="advancesSearchOptions">
|
81
|
+
<div class="row">
|
82
|
+
<div class="col-md-4 mb-3">
|
83
|
+
{{ form.exact_pheno.label(class="form-label") }}
|
84
|
+
{{ form.exact_pheno(class="form-control", placeholder="example:HP:0001298, HP:0001250,..", pattern="[^\\\<\>\?\!\=\/]*", title="Characters \<>?!=/ are not allowed") }}
|
85
|
+
</div>
|
86
|
+
<div class="col-md-4 mb-3">
|
87
|
+
{{ form.exact_dia.label(class="form-label") }}
|
88
|
+
{{ form.exact_dia(class="form-control", placeholder="example:OMIM:125310, ORPHA:585,..", pattern="[^\\\<\>\?\!\=\/]*", title="Characters \<>?!=/ are not allowed") }}
|
89
|
+
</div>
|
90
|
+
</div>
|
91
|
+
<div class="row">
|
92
|
+
<div class="col-md-4 mb-3">
|
93
|
+
{{ form.synopsis.label(class="form-label") }}
|
94
|
+
{{ form.synopsis(class="form-control", placeholder="example:epilepsy", pattern="[^\\\<\>\?\!\=\/]*", title="Characters \<>?!=/ are not allowed") }}
|
95
|
+
</div>
|
96
|
+
<div class="col-md-4 mb-3">
|
97
|
+
{{ form.panel.label(class="form-label") }}
|
98
|
+
{{ form.panel(class="form-control", placeholder="example:NMD", pattern="[^\\\<\>\?\!\=\/]*", title="Characters \<>?!=/ are not allowed") }}
|
99
|
+
</div>
|
100
|
+
<div class="col-md-4 mb-3">
|
101
|
+
{{ form.status.label(class="form-label") }}
|
102
|
+
{{ form.status(class="form-control", placeholder="example:active", pattern="[^\\\<\>\?\!\=\/]*", title="Characters \<>?!=/ are not allowed") }}
|
103
|
+
</div>
|
104
|
+
</div>
|
105
|
+
<div class="row">
|
106
|
+
<div class="col-md-4 mb-3">
|
107
|
+
{{ form.tags.label(class="form-label") }}
|
108
|
+
{{ form.tags(class="form-control", placeholder="example:medical", pattern="[^\\\<\>\?\!\=\/]*", title="Characters \<>?!=/ are not allowed") }}
|
109
|
+
</div>
|
110
|
+
<div class="col-md-4 mb-3">
|
111
|
+
{{ form.track.label(class="form-label") }}
|
112
|
+
{{ form.track(class="form-control", placeholder="rare or cancer", pattern="[^\\\<\>\?\!\=\/]*", title="Characters \<>?!=/ are not allowed") }}
|
113
|
+
</div>
|
114
|
+
<div class="col-md-4 mb-3">
|
115
|
+
{{ form.pheno_group.label(class="form-label") }}
|
116
|
+
{{ form.pheno_group(class="form-control", placeholder="example:HP:0001166", pattern="[^\\\<\>\?\!\=\/]*", title="Characters \<>?!=/ are not allowed") }}
|
117
|
+
</div>
|
118
|
+
</div>
|
119
|
+
<div class="row">
|
120
|
+
<div class="col-md-4 mb-3">
|
121
|
+
{{ form.cohort.label(class="form-label") }}
|
122
|
+
{{ form.cohort(class="form-control", placeholder="example:pedhep", pattern="[^\\\<\>\?\!\=\/]*", title="Characters \<>?!=/ are not allowed") }}
|
123
|
+
</div>
|
124
|
+
<div class="col-md-4 mb-3">
|
125
|
+
{{ form.similar_case.label(class="form-label") }}
|
126
|
+
{{ form.similar_case(class="form-control", placeholder="example:18201", pattern="[^\\\<\>\?\!\=\/]*", title="Characters \<>?!=/ are not allowed") }}
|
127
|
+
</div>
|
128
|
+
<div class="col-md-4 mb-3">
|
129
|
+
{{ form.similar_pheno.label(class="form-label") }}
|
130
|
+
{{ form.similar_pheno(class="form-control", placeholder="example:HP:0001298, HP:0001250,..", pattern="[^\\\<\>\?\!\=\/]*", title="Characters \<>?!=/ are not allowed") }}
|
131
|
+
</div>
|
132
|
+
</div>
|
133
|
+
<div class="row">
|
134
|
+
<div class="col-md-4 mb-3">
|
135
|
+
{{ form.pinned.label(class="form-label") }}
|
136
|
+
{{ form.pinned(class="form-control", placeholder="example:POT1", pattern="[^\\\<\>\?\!\=\/]*", title="Characters \<>?!=/ are not allowed") }}
|
137
|
+
</div>
|
138
|
+
<div class="col-md-4 mb-3">
|
139
|
+
{{ form.causative.label(class="form-label") }}
|
140
|
+
{{ form.causative(class="form-control", placeholder="example:POT1", pattern="[^\\\<\>\?\!\=\/]*", title="Characters \<>?!=/ are not allowed") }}
|
141
|
+
</div>
|
142
|
+
<div class="col-md-4 mb-3">
|
143
|
+
{{ form.user.label(class="form-label") }}
|
144
|
+
{{ form.user(class="form-control", placeholder="John Doe", pattern="[^\\\<\>\?\!\=\/]*", title="Characters \<>?!=/ are not allowed") }}
|
145
|
+
</div>
|
146
|
+
</div>
|
147
|
+
</div> <!--End of advancedSearchOptions div -->
|
68
148
|
</form>
|
149
|
+
<div>
|
150
|
+
<span class="text-muted">The following search criteria were used: {{search}}</span>
|
151
|
+
</div>
|
69
152
|
{% endmacro %}
|
70
153
|
|
71
154
|
{% macro cases_table(group_name, cases) %}
|
@@ -286,16 +369,24 @@
|
|
286
369
|
{{ super() }}
|
287
370
|
<script src="{{ url_for('overview.static', filename='form_scripts.js') }}"></script>
|
288
371
|
<script>
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
}
|
372
|
+
|
373
|
+
// Show/hide advanced search options
|
374
|
+
let advSearchBlock = $('#advancesSearchOptions');
|
375
|
+
|
376
|
+
let advBlockCheckbox = $('#advanced_search');
|
377
|
+
|
378
|
+
window.onload = function(){
|
379
|
+
if(!advBlockCheckbox.is(':checked'))
|
380
|
+
advSearchBlock.hide();
|
381
|
+
}
|
382
|
+
|
383
|
+
advBlockCheckbox.on('click', function() {
|
384
|
+
if($(this).is(':checked')) {
|
385
|
+
advSearchBlock.show();
|
386
|
+
} else {
|
387
|
+
advSearchBlock.hide();
|
388
|
+
}
|
389
|
+
})
|
299
390
|
|
300
391
|
var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'))
|
301
392
|
var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) {
|