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.
Files changed (59) hide show
  1. scout/__version__.py +1 -1
  2. scout/adapter/mongo/base.py +1 -1
  3. scout/adapter/mongo/case.py +185 -117
  4. scout/adapter/mongo/omics_variant.py +19 -0
  5. scout/build/case.py +1 -0
  6. scout/commands/load/variants.py +121 -40
  7. scout/commands/update/case.py +56 -10
  8. scout/constants/case_tags.py +5 -0
  9. scout/constants/disease_parsing.py +2 -2
  10. scout/constants/igv_tracks.py +2 -2
  11. scout/constants/indexes.py +8 -1
  12. scout/demo/643594.config.yaml +1 -0
  13. scout/demo/panel_1.txt +2 -0
  14. scout/demo/resources/ensembl_exons_37_reduced.txt +135 -0
  15. scout/demo/resources/ensembl_exons_38_reduced.txt +166 -0
  16. scout/demo/resources/ensembl_genes_37_reduced.txt +2 -0
  17. scout/demo/resources/ensembl_genes_38_reduced.txt +2 -0
  18. scout/demo/resources/ensembl_transcripts_37_reduced.txt +27 -0
  19. scout/demo/resources/ensembl_transcripts_38_reduced.txt +36 -0
  20. scout/demo/resources/hgnc_reduced_set.txt +2 -0
  21. scout/log/handlers.py +2 -1
  22. scout/log/log.py +48 -61
  23. scout/models/case/case_loading_models.py +2 -0
  24. scout/parse/omim.py +2 -2
  25. scout/server/app.py +23 -7
  26. scout/server/blueprints/alignviewers/controllers.py +46 -23
  27. scout/server/blueprints/cases/controllers.py +21 -47
  28. scout/server/blueprints/cases/templates/cases/case_report.html +4 -1
  29. scout/server/blueprints/cases/templates/cases/case_sma.html +19 -0
  30. scout/server/blueprints/cases/templates/cases/collapsible_actionbar.html +7 -7
  31. scout/server/blueprints/cases/templates/cases/individuals_table.html +1 -1
  32. scout/server/blueprints/clinvar/form.py +1 -1
  33. scout/server/blueprints/clinvar/templates/clinvar/clinvar_submissions.html +2 -2
  34. scout/server/blueprints/clinvar/templates/clinvar/multistep_add_variant.html +9 -3
  35. scout/server/blueprints/institutes/controllers.py +11 -38
  36. scout/server/blueprints/institutes/forms.py +18 -4
  37. scout/server/blueprints/institutes/templates/overview/cases.html +137 -46
  38. scout/server/blueprints/login/views.py +16 -12
  39. scout/server/blueprints/panels/controllers.py +4 -1
  40. scout/server/blueprints/panels/templates/panels/panel.html +1 -1
  41. scout/server/blueprints/panels/templates/panels/panels.html +5 -5
  42. scout/server/blueprints/public/templates/public/index.html +18 -10
  43. scout/server/blueprints/variant/templates/variant/components.html +0 -1
  44. scout/server/blueprints/variant/templates/variant/gene_disease_relations.html +1 -1
  45. scout/server/config.py +3 -0
  46. scout/server/extensions/__init__.py +2 -0
  47. scout/server/extensions/chanjo2_extension.py +46 -0
  48. scout/server/extensions/chanjo_extension.py +44 -1
  49. scout/server/extensions/matchmaker_extension.py +0 -1
  50. scout/server/links.py +11 -2
  51. scout/server/templates/report_base.html +1 -0
  52. scout/utils/convert.py +1 -1
  53. scout/utils/date.py +1 -1
  54. {scout_browser-4.88.1.dist-info → scout_browser-4.89.dist-info}/METADATA +1 -1
  55. {scout_browser-4.88.1.dist-info → scout_browser-4.89.dist-info}/RECORD +59 -58
  56. {scout_browser-4.88.1.dist-info → scout_browser-4.89.dist-info}/LICENSE +0 -0
  57. {scout_browser-4.88.1.dist-info → scout_browser-4.89.dist-info}/WHEEL +0 -0
  58. {scout_browser-4.88.1.dist-info → scout_browser-4.89.dist-info}/entry_points.txt +0 -0
  59. {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 RerunnerError, bionano_access, gens, matchmaker, rerunner, store
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
- # if chanjo connection is established, include MT vs AUTOSOME coverage stats
764
- if current_app.config.get("chanjo_report"):
765
- coverage_stats = mt_coverage_stats(samples)
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>&nbsp;<span>{{ disease_term.description }}&nbsp;{{ disease_term.inheritance }}</span></li>
716
+ <li class="d-flex align-items-baseline"><span class="badge bg-secondary m-1">{{ disease_term._id}}</span>&nbsp;<span>{{ disease_term.description }}&nbsp;{% 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 and case.default_genes %}
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 and ind.analysis_type|upper == "WGS" %}
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 clinical significance")
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 fa-lg" aria-hidden="true"></span></button></td>
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 fa-lg" aria-hidden="true"></span></button></td>
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">{{variant_data.var_obj.classification|replace("_"," ")|upper if variant_data.var_obj.classification else "n.a."}}</span>
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
- {{variant_data.var_form.clinsig(class="form-control, btn-secondary")}}
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="Required only if you select 'drug response' or 'other' as clinical significance"><strong>?</strong></span>
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 = {"search_terms": CASE_SEARCH_TERMS}
432
+ data = {}
439
433
  institute_obj = institute_and_case(store, institute_id)
440
434
  data["institute"] = institute_obj
441
- name_query = None
442
- if request.args.get("search_term"):
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
- data["form"] = populate_case_filter_form(request.args)
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.args.get("skip_assigned"),
527
- is_research=request.args.get("is_research"),
528
- has_rna_data=request.args.get("has_rna"),
529
- verification_pending=request.args.get("validation_ordered"),
530
- has_clinvar_submission=request.args.get("clinvar_submitted"),
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 in cases page"""
161
-
162
- search_type = SelectField("Search by", [validators.Optional()], choices=CASE_SEARCH_KEY)
163
- search_term = StringField("Search cases", default="")
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
- <div class="col-2">
44
- <div class="form-check">
45
- {{ form.skip_assigned(class="form-check-input") }}
46
- {{ form.skip_assigned.label(class="form-check-label") }}
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="form-check">
49
- {{ form.is_research(class="form-check-input") }}
50
- {{ form.is_research.label(class="form-check-label") }}
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="form-check">
53
- {{ form.has_rna(class="form-check-input") }}
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
- </div>
57
- <div class="col-2">
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.validation_ordered(class="form-check-input") }}
64
- {{ form.validation_ordered.label(class="form-check-label") }}
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
- var caseSearchTerms = {{ search_terms|safe }};
290
- var searchTerm = document.getElementById('search_term'); // Case search term label
291
- var sel = document.getElementById('search_type'); // Case search term select
292
- document.getElementById("search_type").onchange = function() {
293
- // Change case search term placeholder
294
- selectedKey = sel.options[sel.selectedIndex].value.slice(0, -1);
295
- searchTerm.placeholder=caseSearchTerms[selectedKey]["placeholder"];
296
- // Reset search
297
- searchTerm.value="";
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) {