scout-browser 4.82.2__py3-none-any.whl → 4.84__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 (87) hide show
  1. scout/__version__.py +1 -1
  2. scout/adapter/client.py +1 -0
  3. scout/adapter/mongo/base.py +0 -1
  4. scout/adapter/mongo/case.py +19 -37
  5. scout/adapter/mongo/case_events.py +98 -2
  6. scout/adapter/mongo/hgnc.py +39 -22
  7. scout/adapter/mongo/institute.py +3 -9
  8. scout/adapter/mongo/panel.py +2 -1
  9. scout/adapter/mongo/variant.py +12 -2
  10. scout/adapter/mongo/variant_loader.py +156 -141
  11. scout/build/genes/hgnc_gene.py +5 -134
  12. scout/commands/base.py +1 -0
  13. scout/commands/download/ensembl.py +1 -0
  14. scout/commands/download/everything.py +1 -0
  15. scout/commands/download/exac.py +1 -0
  16. scout/commands/download/hgnc.py +1 -0
  17. scout/commands/download/hpo.py +1 -0
  18. scout/commands/download/omim.py +1 -0
  19. scout/commands/export/database.py +1 -0
  20. scout/commands/load/panel.py +1 -0
  21. scout/commands/load/report.py +1 -0
  22. scout/commands/update/case.py +10 -10
  23. scout/commands/update/individual.py +6 -1
  24. scout/commands/update/omim.py +1 -0
  25. scout/commands/update/panelapp.py +1 -0
  26. scout/constants/file_types.py +86 -13
  27. scout/export/exon.py +1 -0
  28. scout/load/__init__.py +0 -1
  29. scout/load/all.py +8 -5
  30. scout/load/hgnc_gene.py +1 -1
  31. scout/load/panel.py +8 -4
  32. scout/load/setup.py +1 -0
  33. scout/models/case/case_loading_models.py +6 -16
  34. scout/models/hgnc_map.py +50 -87
  35. scout/models/phenotype_term.py +3 -3
  36. scout/parse/case.py +0 -1
  37. scout/parse/disease_terms.py +1 -0
  38. scout/parse/omim.py +1 -0
  39. scout/parse/orpha.py +1 -0
  40. scout/parse/panel.py +40 -15
  41. scout/parse/variant/conservation.py +1 -0
  42. scout/resources/__init__.py +3 -0
  43. scout/server/app.py +4 -50
  44. scout/server/blueprints/alignviewers/controllers.py +15 -17
  45. scout/server/blueprints/alignviewers/templates/alignviewers/igv_viewer.html +13 -3
  46. scout/server/blueprints/alignviewers/views.py +10 -15
  47. scout/server/blueprints/cases/controllers.py +70 -73
  48. scout/server/blueprints/cases/templates/cases/case.html +94 -71
  49. scout/server/blueprints/cases/templates/cases/collapsible_actionbar.html +1 -1
  50. scout/server/blueprints/cases/templates/cases/phenotype.html +8 -6
  51. scout/server/blueprints/cases/templates/cases/utils.html +3 -3
  52. scout/server/blueprints/cases/views.py +8 -6
  53. scout/server/blueprints/panels/forms.py +1 -0
  54. scout/server/blueprints/variant/controllers.py +14 -19
  55. scout/server/blueprints/variant/templates/variant/acmg.html +25 -16
  56. scout/server/blueprints/variant/templates/variant/components.html +11 -6
  57. scout/server/blueprints/variant/views.py +5 -2
  58. scout/server/blueprints/variants/controllers.py +12 -28
  59. scout/server/blueprints/variants/views.py +1 -1
  60. scout/server/config.py +16 -4
  61. scout/server/extensions/__init__.py +4 -2
  62. scout/server/extensions/beacon_extension.py +1 -0
  63. scout/server/extensions/bionano_extension.py +1 -0
  64. scout/server/extensions/chanjo_extension.py +59 -0
  65. scout/server/extensions/gens_extension.py +1 -0
  66. scout/server/extensions/ldap_extension.py +5 -3
  67. scout/server/extensions/loqus_extension.py +16 -14
  68. scout/server/extensions/matchmaker_extension.py +1 -0
  69. scout/server/extensions/mongo_extension.py +1 -0
  70. scout/server/extensions/phenopacket_extension.py +1 -0
  71. scout/server/extensions/rerunner_extension.py +1 -0
  72. scout/server/links.py +4 -4
  73. scout/server/static/bs_styles.css +20 -2
  74. scout/server/utils.py +16 -2
  75. scout/utils/acmg.py +33 -20
  76. scout/utils/ensembl_rest_clients.py +1 -0
  77. scout/utils/scout_requests.py +1 -0
  78. scout/utils/sort.py +21 -0
  79. scout/utils/track_resources.py +70 -0
  80. {scout_browser-4.82.2.dist-info → scout_browser-4.84.dist-info}/METADATA +2 -5
  81. {scout_browser-4.82.2.dist-info → scout_browser-4.84.dist-info}/RECORD +85 -84
  82. {scout_browser-4.82.2.dist-info → scout_browser-4.84.dist-info}/WHEEL +1 -1
  83. {scout_browser-4.82.2.dist-info → scout_browser-4.84.dist-info}/entry_points.txt +0 -1
  84. scout/load/case.py +0 -36
  85. scout/utils/cloud_resources.py +0 -61
  86. {scout_browser-4.82.2.dist-info → scout_browser-4.84.dist-info}/LICENSE +0 -0
  87. {scout_browser-4.82.2.dist-info → scout_browser-4.84.dist-info}/top_level.txt +0 -0
@@ -65,7 +65,7 @@
65
65
  <span class="badge rounded-pill bg-info text-body" data-bs-toggle="tooltip" data-bs-placement="left" title="Matching causatives and managed variants displayed on case page are NOT filtered by gene panel. Use caution to avoid incidental findings.">off</span>
66
66
  {% endif %}
67
67
  </div>
68
- </div> <!--end of div class="row" -->
68
+ </div> <!--end of row -->
69
69
  <div class="row">
70
70
  <form id="case_status_form" method="POST" action="{{ url_for('cases.status', institute_id=institute._id, case_name=case.display_name) }}">
71
71
  <div class="btn-toolbar ms-2" role="toolbar">
@@ -105,7 +105,7 @@
105
105
  {{ 'Inactive' if case.status == 'ignored' else 'Ignored' }}
106
106
  </button>
107
107
  </div>
108
- <div class="btn-group ms-2 role="group">
108
+ <div class="btn-group ms-2" role="group">
109
109
  <select name="tags" id="status_tags_case" multiple class="selectpicker" data-style="btn btn-secondary">
110
110
  {% for tag, data in case_tag_options.items() %}
111
111
  <option {% if 'tags' in case and tag~"" in case.tags %} selected {% endif %} value="{{ tag }}" title="{{ data.label }}">
@@ -119,87 +119,65 @@
119
119
  </div>
120
120
  </div>
121
121
 
122
+ {{ matching_variants() }}
123
+
122
124
  <div class="card panel-default" >
123
- {% if other_causatives|length > 0%}
124
- <div class="row mt-0">
125
- <div class="col-xs-12 col-md-12">{{ matching_causatives(other_causatives, institute, case) }}</div>
126
- </div>
127
- {% endif %}
128
- {% if default_other_causatives|length > 0%}
129
- <div class="row mt-0">
130
- <div class="col-xs-12 col-md-12">{{ matching_causatives(default_other_causatives, institute, case, default=True) }}</div>
125
+ <div class="row">
126
+ <div class="col">{{ causatives_list(causatives, partial_causatives, evaluated_variants, institute, case, manual_rank_options, cancer_tier_options) }}</div>
127
+ <div class="col">{{ suspects_list(suspects, institute, case, manual_rank_options, cancer_tier_options) }}</div>
128
+ <!-- end of data sharing panels -->
131
129
  </div>
132
- {% endif %}
133
130
 
134
- {% if managed_variants|length > 0%}
135
- <div class="row mt-0">
136
- <div class="col-sm-12 col-md-12">{{ matching_managed_variants(managed_variants, institute, case) }}</div>
131
+ <div class="row">
132
+ {% if case.track == 'cancer' %}
133
+ <div class="col-sm-12 col-md-12 mt-3">{{ cancer_individuals_table(case, institute, tissue_types, gens_info) }}</div>
134
+ {% else %}
135
+ <div class="mt-3 col-sm-8 col-md-{{"8" if case.madeline_info and case.individuals|length > 1 else "12"}}">{{ individuals_table(case, institute, tissue_types, display_rerunner, gens_info) }}</div>
136
+ {% if case.madeline_info and case.individuals|length > 1 %}
137
+ <div class="col-sm-4">
138
+ {{ pedigree_panel(case) }}
139
+ </div>
140
+ {% endif %}
141
+ {% endif %}
137
142
  </div>
138
- {% endif %}
139
143
 
140
- {% if default_managed_variants|length > 0%}
141
- <div class="row mt-0">
142
- <div class="col-sm-12 col-md-12">{{ matching_managed_variants(default_managed_variants, institute, case, default=True) }}</div>
144
+ <div class="row mt-3">
145
+ <div class="col-6">{{ synopsis_panel() }}</div>
146
+ <div class="col-6">{{ comments_panel(institute, case, current_user, comments) }}</div>
143
147
  </div>
144
- {% endif %}
145
- </div>
146
-
147
- <div class="row">
148
- <div class="col">{{ causatives_list(causatives, partial_causatives, evaluated_variants, institute, case, manual_rank_options, cancer_tier_options) }}</div>
149
- <div class="col">{{ suspects_list(suspects, institute, case, manual_rank_options, cancer_tier_options) }}</div>
150
- <!-- end of data sharing panels -->
151
- </div>
152
-
153
- <div class="row">
154
- {% if case.track == 'cancer' %}
155
- <div class="col-sm-12 col-md-12 mt-3">{{ cancer_individuals_table(case, institute, tissue_types, gens_info) }}</div>
156
- {% else %}
157
- <div class="mt-3 col-sm-8 col-md-{{"8" if case.madeline_info and case.individuals|length > 1 else "12"}}">{{ individuals_table(case, institute, tissue_types, display_rerunner, gens_info) }}</div>
158
- {% if case.madeline_info and case.individuals|length > 1 %}
159
- <div class="col-sm-4">
160
- {{ pedigree_panel(case) }}
161
- </div>
162
- {% endif %}
163
- {% endif %}
164
- </div>
165
148
 
166
- <div class="row mt-3">
167
- <div class="col-6">{{ synopsis_panel() }}</div>
168
- <div class="col-6">{{ comments_panel(institute, case, current_user, comments) }}</div>
169
- </div>
170
-
171
- <div class="row">
172
- <div class="col-sm-12">
173
- {{ insert_multi_image_panel() }}
149
+ <div class="row">
150
+ <div class="col-sm-12">
151
+ {{ insert_multi_image_panel() }}
152
+ </div>
174
153
  </div>
175
- </div>
176
154
 
177
- <div class="row">
178
- <div class="col-sm-12">
179
- {{ custom_image_panels() }}
155
+ <div class="row">
156
+ <div class="col-sm-12">
157
+ {{ custom_image_panels() }}
158
+ </div>
180
159
  </div>
181
- </div>
182
160
 
183
- <!-- CASE DIAGNOSES AND PHENOTYPES -->
184
- <div class="panel-default">
185
- <div class="panel-heading"><span class="fa fa-user-md"></span>&nbsp;Phenotypes & diagnoses</div>
186
- <div class="row">
187
- <div class="col-sm-6 ">
188
- <div class="card h-100">
189
- <div class="card-body">
190
- {{ hpo_panel(case, institute, config) }}
161
+ <!-- CASE DIAGNOSES AND PHENOTYPES -->
162
+ <div class="panel-default">
163
+ <div class="panel-heading"><span class="fa fa-user-md"></span>&nbsp;Phenotypes & diagnoses</div>
164
+ <div class="row">
165
+ <div class="col-sm-6 ">
166
+ <div class="card h-100">
167
+ <div class="card-body">
168
+ {{ hpo_panel(case, institute, config) }}
169
+ </div>
191
170
  </div>
192
171
  </div>
193
- </div>
194
- <div class="col-sm-6">
195
- <div class="card h-100">
196
- <div class="card-body">
197
- {{ hpo_genelist_panel(case, institute, config) }}
172
+ <div class="col-sm-6">
173
+ <div class="card h-100">
174
+ <div class="card-body">
175
+ {{ hpo_genelist_panel(case, institute, config) }}
176
+ </div>
198
177
  </div>
199
178
  </div>
200
- </div>
201
- </div> <!--end of <div class="row">-->
202
- </div>
179
+ </div> <!--end of row>-->
180
+ </div> <!--end of card panel-default -->
203
181
 
204
182
  <!-- diagnoses-related code-->
205
183
  {% if not case.track == 'cancer' %}
@@ -243,8 +221,8 @@
243
221
  {{ reanalysis_modal(institute, case) }}
244
222
  {{ beacon_modal(institute, case) }}
245
223
  {{ matchmaker_modal(institute, case, suspects, mme_nodes) }}
246
- </div><!-- end of <div containter> -->
247
- </div><!-- end of <div col> -->
224
+ </div><!-- end of containter -->
225
+ </div><!-- end of col -->
248
226
  {% endmacro %}
249
227
 
250
228
  {% macro variants_buttons() %}
@@ -382,7 +360,7 @@
382
360
  </div>
383
361
  {% endfor %}
384
362
  {% endif %}
385
- </div> <!-- end of <div class="list-group" style="max-height:200px; overflow-y: scroll;" -->
363
+ </div> <!-- end of list-group -->
386
364
  </div>
387
365
  </div>
388
366
  {% endmacro %}
@@ -558,6 +536,51 @@
558
536
  </div>
559
537
  {% endmacro %}
560
538
 
539
+ {% macro matching_variants() %}
540
+ <div class="card mt-3">
541
+ <div class="row mt-0 ms-3">
542
+ <div class="col-sm-12 col-md-12">
543
+ <div data-bs-toggle='tooltip' class="panel-heading" title="Check if there are any variants in this case
544
+ marked as causative in another case for this institute, or are on the managed variants list.">
545
+ <strong>
546
+ {% if hide_matching == false %}
547
+ <a href="{{ url_for('cases.case', institute_id=institute._id, case_name=case.display_name, hide_matching='True') }}" class="text-body"><span class="me-1 fa fa-caret-down"></span>Search for matching causatives and managed variants</a>
548
+ {% else %}
549
+ <a href="{{ url_for('cases.case', institute_id=institute._id, case_name=case.display_name, hide_matching='False') }}" class="text-body"><span class="me-1 fa fa-caret-right"></span>Search for matching causatives and managed variants</a>
550
+ {% endif %}
551
+ </strong>
552
+ </div>
553
+ </div>
554
+ {% if hide_matching == false %}
555
+ {% if other_causatives|length > 0 %}
556
+ <div class="row mt-0 ms-3">
557
+ <div class="col-xs-12 col-md-12">{{ matching_causatives(other_causatives, institute, case) }}</div>
558
+ </div>
559
+ {% endif %}
560
+ {% if default_other_causatives|length > 0%}
561
+ <div class="row mt-0 ms-3">
562
+ <div class="col-xs-12 col-md-12">{{ matching_causatives(default_other_causatives, institute, case, default=True) }}</div>
563
+ </div>
564
+ {% endif %}
565
+ {% if managed_variants|length > 0%}
566
+ <div class="row mt-0 ms-3">
567
+ <div class="col-sm-12 col-md-12">{{ matching_managed_variants(managed_variants, institute, case) }}</div>
568
+ </div>
569
+ {% endif %}
570
+ {% if default_managed_variants|length > 0%}
571
+ <div class="row mt-0 ms-3">
572
+ <div class="col-sm-12 col-md-12">{{ matching_managed_variants(default_managed_variants, institute, case, default=True) }}</div>
573
+ </div>
574
+ {% endif %}
575
+ {% if other_causatives|length == 0 and default_other_causatives|length == 0 and managed_variants|length == 0 and default_managed_variants|length == 0%}
576
+ <div class="row mt-0 ms-3">
577
+ <div class="col-sm-12 col-md-12">No matching causatives or managed variants found</div>
578
+ </div>
579
+ {% endif %}
580
+ {% endif %}
581
+ </div>
582
+ </div>
583
+ {% endmacro %}
561
584
 
562
585
  {% block scripts %}
563
586
  {{ super() }}
@@ -108,7 +108,7 @@
108
108
  {% endif %}
109
109
 
110
110
  <!-- Display mtDNA report for non-cancer cases -->
111
- {% if case.track != "cancer" %}
111
+ {% if case.mtdna_report %}
112
112
  <div href="#" class="bg-dark list-group-item text-white">
113
113
  <div class="d-flex flex-row flex-fill bd-highlight">
114
114
  <div>
@@ -236,11 +236,13 @@
236
236
  <!-- Display and remove added HPO terms -->
237
237
  <div class="row mt-3">
238
238
  <div class="col-12 ms-3">
239
- {% for hpo_term in case.phenotype_terms %}
240
- {{ hpo_item(hpo_term, case) }}
241
- {% else %}
242
- <span class="text-mute">No phenotypes added yet</span>
243
- {% endfor %}
239
+ {% if "phenotype_terms" in case and case.phenotype_terms|length > 0 %}
240
+ {% for hpo_term in case.phenotype_terms %}
241
+ {{ hpo_item(hpo_term, case) }}
242
+ {% endfor %}
243
+ {% else %}
244
+ <span class="text-mute">No phenotypes added yet</span>
245
+ {% endif %}
244
246
  </div>
245
247
  </div>
246
248
 
@@ -251,7 +253,7 @@
251
253
  <input class="ms-3" name="min_match" type="number" min="0" step="1" placeholder="Min matches" style="width:130px"/>
252
254
  {% if config.PHENOMIZER_USERNAME %}
253
255
  <button class="btn btn-secondary btn-sm" type="submit" name="action" value="PHENOMIZER"
254
- {%if case.phenotype_terms|length == 0 %} disabled {%endif%}>Phenomizer</button>
256
+ {% if "phenotype_terms" not in case or case.phenotype_terms|length == 0 %} disabled {% endif %}>Phenomizer</button>
255
257
  {% endif %}
256
258
  </div>
257
259
  <div class="col-1">
@@ -633,8 +633,8 @@
633
633
 
634
634
  {% macro matching_causatives(other_causatives, institute, case, default=False) %}
635
635
  <div data-bs-toggle='tooltip' class="panel-heading" title="If there are any variants in this case
636
- that have been marked as causative in another case for this insitute. {% if default %}Variants in default panels for the case only.{% endif %}">
637
- <strong><a data-bs-toggle="collapse" href="#matchingCausatives{% if default %}Default{% endif %}" class="text-body">Matching causatives {% if default %}in case default panels{% endif %}({{other_causatives|length}})</a></strong>
636
+ matching a causative in another case for this institute. {% if default %}Variants in default panels for the case only.{% endif %}">
637
+ <strong><a data-bs-toggle="collapse" href="#matchingCausatives{% if default %}Default{% endif %}" class="text-body" aria-expanded="false"><span class="collapse-icon me-1"></span>Matching causatives {% if default %}in case default panels{% endif %}({{other_causatives|length}})</a></strong>
638
638
  </div>
639
639
  <ul class="list-group collapse" id="matchingCausatives{% if default %}Default{% endif %}">
640
640
  {% for variant in other_causatives %}
@@ -650,7 +650,7 @@
650
650
  {% macro matching_managed_variants(managed_variants, institute, case, default=False) %}
651
651
  <div data-bs-toggle='tooltip' class="panel-heading" title="Any variants in this case
652
652
  that have been marked as managed. {% if default %}Variants in default panels for the case only.{% endif %}">
653
- <strong><a data-bs-toggle="collapse" class="text-body" href="#matchingManaged{% if default %}Default{% endif %}">Managed variants {% if default %}in case default panels{% endif %}({{managed_variants|length}})</a></strong>
653
+ <strong><a data-bs-toggle="collapse" class="text-body" href="#matchingManaged{% if default %}Default{% endif %}" aria-expanded="false"><span class="collapse-icon me-1"></span>Managed variants {% if default %}in case default panels{% endif %}({{managed_variants|length}})</a></strong>
654
654
  </div>
655
655
  <ul class="list-group">
656
656
  {% for variant in managed_variants %}
@@ -4,6 +4,7 @@ import json
4
4
  import logging
5
5
  import os.path
6
6
  import shutil
7
+ from ast import literal_eval
7
8
  from io import BytesIO
8
9
  from operator import itemgetter
9
10
  from typing import Generator, Optional, Union
@@ -86,7 +87,6 @@ def case(
86
87
  So do case_id, but we still call institute_and_case again to fetch institute
87
88
  and reuse its user access verification.
88
89
  """
89
-
90
90
  if case_id:
91
91
  case_obj = store.case(case_id=case_id, projection={"display_name": 1, "owner": 1})
92
92
 
@@ -102,7 +102,12 @@ def case(
102
102
  flash("Case {} does not exist in database!".format(case_name))
103
103
  return redirect(request.referrer)
104
104
 
105
- data = controllers.case(store, institute_obj, case_obj)
105
+ hide_matching = (
106
+ literal_eval(request.args.get("hide_matching"))
107
+ if request.args.get("hide_matching")
108
+ else True
109
+ )
110
+ data = controllers.case(store, institute_obj, case_obj, hide_matching)
106
111
 
107
112
  return dict(
108
113
  **data,
@@ -271,10 +276,7 @@ def pdf_case_report(institute_id, case_name):
271
276
  store=store, institute_obj=institute_obj, case_obj=case_obj
272
277
  )
273
278
  # add coverage report on the bottom of this report
274
- if (
275
- current_app.config.get("SQLALCHEMY_DATABASE_URI")
276
- and case_obj.get("track", "rare") != "cancer"
277
- ):
279
+ if current_app.config.get("chanjo_report") and case_obj.get("track", "rare") != "cancer":
278
280
  data["coverage_report"] = controllers.coverage_report_contents(
279
281
  request.url_root, institute_obj, case_obj
280
282
  )
@@ -1,4 +1,5 @@
1
1
  """Code for panel gene form"""
2
+
2
3
  from flask_wtf import FlaskForm
3
4
  from wtforms import BooleanField, SelectMultipleField, StringField
4
5
 
@@ -28,7 +28,7 @@ from scout.server.blueprints.variant.utils import (
28
28
  update_variant_case_panels,
29
29
  )
30
30
  from scout.server.blueprints.variants.utils import update_case_panels
31
- from scout.server.extensions import LoqusDB, cloud_tracks, gens
31
+ from scout.server.extensions import LoqusDB, config_igv_tracks, gens
32
32
  from scout.server.links import disease_link, get_variant_links
33
33
  from scout.server.utils import (
34
34
  case_has_alignments,
@@ -142,9 +142,9 @@ def get_igv_tracks(build: str = "37") -> set:
142
142
  # Collect hardcoded tracks, common for all Scout instances
143
143
  for track in IGV_TRACKS.get(build, []):
144
144
  igv_tracks.add(track.get("name"))
145
- # Collect instance-specif cloud public tracks, if available
146
- if hasattr(cloud_tracks, "public_tracks"):
147
- for track in cloud_tracks.public_tracks.get(build, []):
145
+ # Collect instance-specific public tracks, if available
146
+ if hasattr(config_igv_tracks, "tracks"):
147
+ for track in config_igv_tracks.tracks.get(build, []):
148
148
  igv_tracks.add(track.get("name"))
149
149
  return igv_tracks
150
150
 
@@ -372,7 +372,7 @@ def variant(
372
372
  "ACMG_OPTIONS": ACMG_OPTIONS,
373
373
  "case_tag_options": CASE_TAGS,
374
374
  "inherit_palette": INHERITANCE_PALETTE,
375
- "igv_tracks": get_igv_tracks(genome_build),
375
+ "igv_tracks": get_igv_tracks("38" if variant_obj["is_mitochondrial"] else genome_build),
376
376
  "has_rna_tracks": case_has_rna_tracks(case_obj),
377
377
  "gens_info": gens.connection_settings(genome_build),
378
378
  "evaluations": evaluations,
@@ -486,26 +486,21 @@ def observations(store: MongoAdapter, loqusdb: LoqusDB, variant_obj: dict) -> Di
486
486
  loqus_query = loqusdb.get_loqus_query(variant_obj, category)
487
487
 
488
488
  for loqus_id in inst_loqus_ids: # Loop over all loqusdb instances of an institute
489
- obs_data[loqus_id] = {}
490
- loqus_settings = loqusdb.loqusdb_settings.get(loqus_id)
489
+ # collect observation on that loqusdb instance
490
+ obs_data[loqus_id] = loqusdb.get_variant(loqus_query, loqusdb_id=loqus_id)
491
491
 
492
- if loqus_settings is None: # An instance might have been renamed or removed
492
+ if obs_data[loqus_id] is None:
493
493
  flash(
494
- f"Could not connect to the preselected loqusdb '{loqus_id}' instance",
494
+ f"Could not find a Loqus instance with id:{loqus_id}",
495
495
  "warning",
496
496
  )
497
- obs_data[loqus_id]["total"] = "N/A"
497
+ obs_data[loqus_id]["observations"] = "N/A"
498
498
  continue
499
- obs_data[loqus_id] = loqusdb.get_variant(
500
- loqus_query, loqusdb_id=loqus_id
501
- ) # collect observation on that loqus instance
502
-
503
- if not obs_data[loqus_id]: # data is an empty dictionary
504
- # Collect count of variants in variant's case
505
- obs_data[loqus_id] = loqusdb.get_variant(loqus_query, loqusdb_id=loqus_id)
506
- if obs_data[loqus_id].get("total"):
507
- obs_data[loqus_id]["observations"] = 0
499
+
500
+ if obs_data[loqus_id] == {}: # Variant was not found
501
+ obs_data[loqus_id]["observations"] = 0
508
502
  continue
503
+
509
504
  # Check if the current case is represented in the loqusdb instance
510
505
  obs_data[loqus_id]["case_match"] = variant_obj["case_id"] in obs_data[loqus_id].get(
511
506
  "families", []
@@ -29,22 +29,27 @@
29
29
  <form action="{{ url_for('variant.variant_acmg', institute_id=institute._id, case_name=case.display_name, variant_id=variant._id) }}" method="POST">
30
30
  <div class="card panel-default mt-3">
31
31
  <div class="card-body">
32
- {% if not evaluation %}
33
- <div class="mt-3 fixed-bottom bg-light border">
34
- <div class="text-center">
35
- {% for option in ACMG_OPTIONS %}
36
- <a id="acmg-{{ option.code }}" class="btn acmg-preview">{{ option.label }}</a>
37
- {% endfor %}
38
- </div>
39
- </div>
40
- <button class="btn btn-primary form-control">Submit</button>
41
- {% else %}
42
- <h4>
32
+ {% if evaluation %}
33
+ <h4>
43
34
  {{ evaluation.classification.label }}
44
35
  <span class="badge bg-info">{{ evaluation.classification.short }}</span>
45
36
  </h4>
46
37
  By {{ evaluation.user_name }} on {{ evaluation.created_at.date() }}
38
+ {% if edit %}
39
+ <br><br>
40
+ <button class="btn btn-primary form-control" data-bs-toggle="tooltip" title="Editing this classification will result in a new classification">Reclassify</button>
41
+ {% endif %}
42
+ {% elif not evaluation %}
43
+ <button class="btn btn-primary form-control">Submit</button>
47
44
  {% endif %}
45
+ <!-- classification preview in the footer-->
46
+ <div class="mt-3 fixed-bottom bg-light border">
47
+ <div class="text-center">
48
+ {% for option in ACMG_OPTIONS %}
49
+ <a id="acmg-{{ option.code }}" class="btn acmg-preview">{{ option.label }}</a>
50
+ {% endfor %}
51
+ </div>
52
+ </div>
48
53
  </div>
49
54
  </div>
50
55
 
@@ -72,14 +77,14 @@
72
77
  {{ criterion.short }}
73
78
  </div>
74
79
  <div class="form-check form-switch">
75
- <input type="checkbox" class="form-check-input" id="checkbox-{{ criterion_code }}" name="criteria" value="{{ criterion_code }}" {{ 'checked' if evaluation and criterion_code in evaluation.criteria }} {{ 'disabled' if evaluation }}>
80
+ <input type="checkbox" class="form-check-input" id="checkbox-{{ criterion_code }}" name="criteria" value="{{ criterion_code }}" {{ 'checked' if evaluation and criterion_code in evaluation.criteria }} {{ 'disabled' if evaluation and edit is false}}>
76
81
  <label class="form-check-label" for="checkbox-{{ criterion_code }}"></label>
77
82
  </div>
78
83
  </div>
79
84
  <div id="comment-{{ criterion_code }}" class="{{ 'collapse' if not (comment or link or modifier) }} mt-2">
80
85
  {% if criterion.documentation %}<div class="row"><span class="me-1 fw-light text-wrap">{{ criterion.documentation | safe}}</span></div>{% endif %}
81
86
  <div class="row">
82
- <select {{ 'disabled' if evaluation }} id="modifier-{{ criterion_code }}" name="modifier-{{ criterion_code }}" class="form-control form-select">
87
+ <select {{ 'disabled' if evaluation and edit is false}} id="modifier-{{ criterion_code }}" name="modifier-{{ criterion_code }}" class="form-control form-select">
83
88
  <option value="" {% if not modifier %}selected{% endif %}>Strength modifier...</option>
84
89
  {% for level in "Strong", "Moderate", "Supporting" %}
85
90
  {% if(level != evidence) %}
@@ -89,10 +94,10 @@
89
94
  </select>
90
95
  </div>
91
96
  <div class="row">
92
- <textarea {{ 'disabled' if evaluation }} class="form-control"
97
+ <textarea {{ 'disabled' if evaluation and edit is false }} class="form-control"
93
98
  name="comment-{{ criterion_code }}" rows="3" placeholder="Comment (optional)">{{ comment }}</textarea>
94
99
  </div>
95
- <input type="url" {{ 'disabled' if evaluation }} class="form-control" name="link-{{ criterion_code }}"
100
+ <input type="url" {{ 'disabled' if evaluation and edit is false }} class="form-control" name="link-{{ criterion_code }}"
96
101
  placeholder="{{ link or 'Supporting link (optional)' }}" value="">
97
102
  </div>
98
103
  </div>
@@ -120,6 +125,11 @@
120
125
  {{ super() }}
121
126
 
122
127
  <script>
128
+
129
+ window.onload=function() {
130
+ update_classification();
131
+ }
132
+
123
133
  $(function () {
124
134
  $('[data-bs-toggle="tooltip"]').tooltip();
125
135
 
@@ -130,7 +140,6 @@
130
140
  } else {
131
141
  el.collapse('hide');
132
142
  }
133
-
134
143
  update_classification()
135
144
  });
136
145
 
@@ -49,14 +49,19 @@
49
49
  {% endif %}
50
50
  </span>
51
51
  </div>
52
- <small class="text-muted">
53
- <form action="{{ url_for('variant.evaluation', evaluation_id=data._id) }}" method="POST">
52
+ <span>
53
+ <small class="text-muted">
54
54
  {{ data.user_name }} on {{ data.created_at.date() }}
55
55
  {% if current_variant %}
56
- <button class="btn btn-xs btn-link">Delete</button>
56
+ <form action="{{ url_for('variant.evaluation', evaluation_id=data._id) }}" method="POST" style="display: inline-block;">
57
+ <button class="btn btn-xs btn-link" >Delete</button>
58
+ </form>
59
+ {% if data.criteria %}
60
+ <a class="btn btn-xs btn-link" href="{{ url_for('variant.evaluation', evaluation_id=data._id, edit=True) }}" data-bs-toggle="tooltip" title="Editing this classification will result in a new classification">Edit</a>
61
+ {% endif %}
57
62
  {% endif %}
58
- </form>
59
- </small>
63
+ </small>
64
+ </span>
60
65
  </li>
61
66
  {% endmacro %}
62
67
 
@@ -90,7 +95,7 @@
90
95
  {% endif %}
91
96
  </div>
92
97
  {% endfor %}
93
- {% if config.SQLALCHEMY_DATABASE_URI %}
98
+ {% if config.chanjo_report %}
94
99
  <div class="d-flex flex-wrap ms-1">
95
100
  {% for gene in variant.genes %}
96
101
  <a class="btn btn-sm btn-secondary text-white" rel="noopener noreferrer" target="_blank" href="{{ url_for('report.gene', gene_id=gene.hgnc_id, sample_id=variant.samples|map(attribute='sample_id')|list) }}">
@@ -13,7 +13,7 @@ from flask import (
13
13
  from flask_login import current_user
14
14
  from markupsafe import Markup
15
15
 
16
- from scout.constants import ACMG_CRITERIA, ACMG_MAP
16
+ from scout.constants import ACMG_CRITERIA, ACMG_MAP, ACMG_OPTIONS
17
17
  from scout.server.blueprints.variant.controllers import check_reset_variant_classification
18
18
  from scout.server.blueprints.variant.controllers import evaluation as evaluation_controller
19
19
  from scout.server.blueprints.variant.controllers import observations, str_variant_reviewer
@@ -308,7 +308,7 @@ def variant_update(institute_id, case_name, variant_id):
308
308
  @variant_bp.route("/evaluations/<evaluation_id>", methods=["GET", "POST"])
309
309
  @templated("variant/acmg.html")
310
310
  def evaluation(evaluation_id):
311
- """Show or delete an ACMG evaluation."""
311
+ """Show, edit or delete an ACMG evaluation."""
312
312
 
313
313
  evaluation_obj = store.get_evaluation(evaluation_id)
314
314
  if evaluation_obj is None:
@@ -328,12 +328,15 @@ def evaluation(evaluation_id):
328
328
  flash("Cleared ACMG classification.", "info")
329
329
 
330
330
  return redirect(link)
331
+
331
332
  return dict(
332
333
  evaluation=evaluation_obj,
334
+ edit=bool(request.args.get("edit")),
333
335
  institute=evaluation_obj["institute"],
334
336
  case=evaluation_obj["case"],
335
337
  variant=evaluation_obj["variant"],
336
338
  CRITERIA=ACMG_CRITERIA,
339
+ ACMG_OPTIONS=ACMG_OPTIONS,
337
340
  )
338
341
 
339
342
 
@@ -3,12 +3,10 @@ import logging
3
3
  import re
4
4
  from typing import Any, Dict, List, Optional
5
5
 
6
- import bson
7
6
  from flask import Response, flash, session, url_for
8
7
  from flask_login import current_user
9
8
  from markupsafe import Markup
10
9
  from pymongo.cursor import CursorType
11
- from pymongo.errors import DocumentTooLarge
12
10
  from werkzeug.datastructures import Headers, ImmutableMultiDict, MultiDict
13
11
  from wtforms import DecimalField
14
12
 
@@ -513,7 +511,7 @@ def update_variant_genes(store, variant_obj, genome_build):
513
511
 
514
512
  if variant_genes is not None:
515
513
  for gene_obj in variant_genes:
516
- # If there is no hgnc id there is nothin we can do
514
+ # If there is no hgnc id there is nothing we can do
517
515
  if not gene_obj["hgnc_id"]:
518
516
  continue
519
517
  # Else we collect the gene object and check the id
@@ -530,27 +528,6 @@ def update_variant_genes(store, variant_obj, genome_build):
530
528
  return has_changed
531
529
 
532
530
 
533
- def update_variant_store(store, variant_obj):
534
- """
535
- Update variants in db store if anything changed.
536
- Args:
537
- store(scout.adapter.MongoAdapter)
538
- variant_obj(scout.models.Variant)
539
-
540
- update(boolean): upsert only if this is set true
541
-
542
- get_compounds(bool): if compounds should be added to added to the returned variant object
543
-
544
- """
545
- try:
546
- variant_obj = store.update_variant(variant_obj)
547
- except DocumentTooLarge:
548
- flash(
549
- f"An error occurred while updating info for variant: {variant_obj['_id']} (pymongo_errors.DocumentTooLarge: {len(bson.BSON.encode(variant_obj))})",
550
- "warning",
551
- )
552
-
553
-
554
531
  def _compound_follow_filter_freq(compound, compound_var_obj, query_form):
555
532
  """When compound follow filter is selected, apply relevant settings from the query filter onto dismissing compounds.
556
533
 
@@ -895,11 +872,18 @@ def parse_variant(
895
872
  compounds_have_changed = False
896
873
  if get_compounds:
897
874
  compounds_have_changed = update_compounds(store, variant_obj, case_dismissed_vars)
875
+ if update and compounds_have_changed:
876
+ store.variant_update_field(
877
+ variant_id=variant_obj["_id"],
878
+ field_name="compounds",
879
+ field_value=variant_obj["compounds"],
880
+ )
898
881
 
899
882
  genes_have_changed = update_variant_genes(store, variant_obj, genome_build)
900
-
901
- if update and (compounds_have_changed or genes_have_changed):
902
- update_variant_store(store, variant_obj)
883
+ if update and genes_have_changed:
884
+ store.variant_update_field(
885
+ variant_id=variant_obj["_id"], field_name="genes", field_value=variant_obj["genes"]
886
+ )
903
887
 
904
888
  variant_obj["comments"] = store.events(
905
889
  institute_obj,
@@ -1996,7 +1980,7 @@ def activate_case(store, institute_obj, case_obj, current_user):
1996
1980
  store.update_status(institute_obj, case_obj, user_obj, "active", case_link)
1997
1981
 
1998
1982
 
1999
- def reset_all_dimissed(store, institute_obj, case_obj):
1983
+ def reset_all_dismissed(store, institute_obj, case_obj):
2000
1984
  """Reset all dismissed variants for a case.
2001
1985
 
2002
1986
  Args:
@@ -47,7 +47,7 @@ variants_bp = Blueprint(
47
47
  def reset_dismissed(institute_id, case_name):
48
48
  """Reset all dismissed variants for a case"""
49
49
  institute_obj, case_obj = institute_and_case(store, institute_id, case_name)
50
- controllers.reset_all_dimissed(store, institute_obj, case_obj)
50
+ controllers.reset_all_dismissed(store, institute_obj, case_obj)
51
51
  return redirect(request.referrer)
52
52
 
53
53