scout-browser 4.92__py3-none-any.whl → 4.93.1__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 (45) hide show
  1. scout/__version__.py +1 -1
  2. scout/adapter/mongo/base.py +3 -0
  3. scout/adapter/mongo/case.py +27 -2
  4. scout/adapter/mongo/ccv.py +131 -0
  5. scout/adapter/mongo/variant.py +4 -3
  6. scout/adapter/mongo/variant_events.py +45 -1
  7. scout/build/ccv.py +59 -0
  8. scout/commands/serve.py +2 -1
  9. scout/constants/__init__.py +2 -0
  10. scout/constants/case_tags.py +2 -0
  11. scout/constants/ccv.py +244 -0
  12. scout/demo/643594.config.yaml +2 -2
  13. scout/demo/images/custom_images/1300x1000.jpg +0 -0
  14. scout/models/ccv_evaluation.py +26 -0
  15. scout/models/variant/variant.py +1 -0
  16. scout/server/blueprints/cases/templates/cases/case_report.html +45 -0
  17. scout/server/blueprints/cases/templates/cases/collapsible_actionbar.html +2 -2
  18. scout/server/blueprints/cases/templates/cases/index.html +0 -2
  19. scout/server/blueprints/institutes/templates/overview/causatives.html +1 -1
  20. scout/server/blueprints/institutes/templates/overview/utils.html +12 -1
  21. scout/server/blueprints/institutes/templates/overview/verified.html +1 -1
  22. scout/server/blueprints/institutes/views.py +4 -0
  23. scout/server/blueprints/panels/controllers.py +5 -6
  24. scout/server/blueprints/panels/templates/panels/panel.html +5 -5
  25. scout/server/blueprints/variant/controllers.py +148 -1
  26. scout/server/blueprints/variant/templates/variant/cancer-variant.html +1 -1
  27. scout/server/blueprints/variant/templates/variant/ccv.html +183 -0
  28. scout/server/blueprints/variant/templates/variant/components.html +61 -3
  29. scout/server/blueprints/variant/templates/variant/variant.html +1 -1
  30. scout/server/blueprints/variant/templates/variant/variant_details.html +29 -11
  31. scout/server/blueprints/variant/utils.py +21 -1
  32. scout/server/blueprints/variant/views.py +114 -3
  33. scout/server/blueprints/variants/controllers.py +31 -0
  34. scout/server/blueprints/variants/templates/variants/cancer-variants.html +2 -1
  35. scout/server/blueprints/variants/templates/variants/components.html +63 -73
  36. scout/server/blueprints/variants/templates/variants/indicators.html +11 -0
  37. scout/server/static/custom_images.js +19 -2
  38. scout/utils/ccv.py +201 -0
  39. {scout_browser-4.92.dist-info → scout_browser-4.93.1.dist-info}/METADATA +6 -5
  40. {scout_browser-4.92.dist-info → scout_browser-4.93.1.dist-info}/RECORD +44 -38
  41. {scout_browser-4.92.dist-info → scout_browser-4.93.1.dist-info}/WHEEL +1 -1
  42. scout/demo/images/custom_images/640x480_two.jpg +0 -0
  43. {scout_browser-4.92.dist-info → scout_browser-4.93.1.dist-info}/LICENSE +0 -0
  44. {scout_browser-4.92.dist-info → scout_browser-4.93.1.dist-info}/entry_points.txt +0 -0
  45. {scout_browser-4.92.dist-info → scout_browser-4.93.1.dist-info}/top_level.txt +0 -0
@@ -133,7 +133,7 @@
133
133
  {% endmacro %}
134
134
 
135
135
  {% macro acmg_form(institute, case, variant, ACMG_OPTIONS, selected=None) %}
136
- <label class="mt-3">ACMG classification</label>
136
+ <label class="mt-3" data-bs-toggle="tooltip" title="Richards et al 2015"><a href="https://www.acmg.net/docs/standards_guidelines_for_the_interpretation_of_sequence_variants.pdf" rel="noopener noreferrer" target="_blank" style="color: inherit; text-decoration: inherit;">ACMG classification</a></label>
137
137
  <form action="{{ url_for('variant.variant_update', institute_id=institute._id, case_name=case.display_name, variant_id=variant._id) }}" method="POST">
138
138
  <div class="d-flex justify-content-between">
139
139
  {% for option in ACMG_OPTIONS %}
@@ -145,11 +145,56 @@
145
145
  </form>
146
146
  {% endmacro %}
147
147
 
148
+ {% macro ccv_classification_item(variant, data) %}
149
+ {% set current_variant = (data.variant_specific == variant._id) %}
150
+ <li class="list-group-item {{ 'list-group-item-info' if current_variant }}">
151
+ <div class="d-flex">
152
+ <span>
153
+ <a href="{{ url_for('variant.ccv_evaluation', evaluation_id=data._id) }}">
154
+ {{ data.ccv_classification.label }}
155
+ </a>
156
+ <span class="badge bg-info">{{ data.ccv_classification.short }}</span>
157
+ </span>
158
+ <span>
159
+ {% if not current_variant %}
160
+ <small>{{ data.case.display_name }}</small>
161
+ {% endif %}
162
+ </span>
163
+ </div>
164
+ <span>
165
+ <small class="text-muted">
166
+ {{ data.user_name }} on {{ data.created_at.date() }}
167
+ {% if current_variant %}
168
+ <form action="{{ url_for('variant.ccv_evaluation', evaluation_id=data._id) }}" method="POST" style="display: inline-block;">
169
+ <button class="btn btn-xs btn-link" >Delete</button>
170
+ </form>
171
+ {% if data.ccv_criteria %}
172
+ <a class="btn btn-xs btn-link" href="{{ url_for('variant.ccv_evaluation', evaluation_id=data._id, edit=True) }}" data-bs-toggle="tooltip" title="Editing this classification will result in a new classification">Edit</a>
173
+ {% endif %}
174
+ {% endif %}
175
+ </small>
176
+ </span>
177
+ </li>
178
+ {% endmacro %}
148
179
 
149
- {% macro panel_classify(variant, institute, case, ACMG_OPTIONS, manual_rank_options, cancer_tier_options, dismiss_variant_options, mosaic_variant_options, evaluations) %}
180
+
181
+ {% macro ccv_form(institute, case, variant, CCV_OPTIONS, selected=None) %}
182
+ <label class="mt-3" data-bs-toggle="tooltip" title="Horak et al 2022"><a href="https://doi.org/10.1016/j.gim.2022.01.001" rel="noopener noreferrer" target="_blank" style="color: inherit; text-decoration: inherit;">ClinGen-CGC-VICC Oncogenicity classification</a></label>
183
+ <form action="{{ url_for('variant.variant_update', institute_id=institute._id, case_name=case.display_name, variant_id=variant._id) }}" method="POST">
184
+ <div class="d-flex justify-content-between">
185
+ {% for option in CCV_OPTIONS %}
186
+ <button class="btn btn-{{ option.color if (option.code == selected or not selected) else 'outline-secondary' }} form-control {{ 'me-1' if not loop.last }}" name="ccv_classification" value="{{ option.code }}" title="{{ option.label }}">
187
+ {{ option.short }}
188
+ </button>
189
+ {% endfor %}
190
+ </div>
191
+ </form>
192
+ {% endmacro %}
193
+
194
+ {% macro panel_classify(variant, institute, case, ACMG_OPTIONS, CCV_OPTIONS, manual_rank_options, cancer_tier_options, dismiss_variant_options, mosaic_variant_options, evaluations, ccv_evaluations) %}
150
195
  <div class="card panel-default">
151
196
  <div class="panel-heading">Classify</div>
152
- <div class="card-body">
197
+ <div class="card-body" style="margin-top: -30px">
153
198
  {{ variant_tag_button(variant, institute, case, manual_rank_options) }}
154
199
  {% if case.track == "cancer" %}
155
200
  {{ variant_tier_button(variant, institute, case, cancer_tier_options) }}
@@ -169,6 +214,19 @@
169
214
  {% endfor %}
170
215
  </div>
171
216
  {% endif %}
217
+ {% if case.track == "cancer" %}
218
+ {{ ccv_form(institute, case, variant, CCV_OPTIONS, variant.ccv_classification.code if variant.ccv_classification) }}
219
+ <div class="mt-3">
220
+ <a href="{{ url_for('variant.variant_ccv', institute_id=institute._id, case_name=case.display_name, variant_id=variant._id) }}" class="btn btn-outline-secondary form-control">Classify</a>
221
+ </div>
222
+ {% if ccv_evaluations %} <!-- scrollable previous ClinGen-CGC-VICC evaluations div-->
223
+ <div class="list-group mt-3" style="max-height:200px;overflow-y: scroll;">
224
+ {% for ccv_evaluation in ccv_evaluations %}
225
+ {{ ccv_classification_item(variant, ccv_evaluation) }}
226
+ {% endfor %}
227
+ </div>
228
+ {% endif %}
229
+ {% endif %}
172
230
  </div>
173
231
  </div>
174
232
  {% endmacro %}
@@ -84,7 +84,7 @@
84
84
 
85
85
  <div class="row">
86
86
  <div class="col-lg-3 col-md-6">
87
- {{ panel_classify(variant, institute, case, ACMG_OPTIONS, manual_rank_options, cancer_tier_options, dismiss_variant_options, mosaic_variant_options, evaluations) }}
87
+ {{ panel_classify(variant, institute, case, ACMG_OPTIONS, CCV_OPTIONS, manual_rank_options, cancer_tier_options, dismiss_variant_options, mosaic_variant_options, evaluations, ccv_evaluations) }}
88
88
  </div>
89
89
  <div class="col-lg-5 col-md-6">
90
90
  {{ panel_summary() }}
@@ -200,6 +200,20 @@
200
200
  </div>
201
201
  {% endmacro %}
202
202
 
203
+ {% macro observation_badge(observed_case) %}
204
+
205
+ {% if observed_case.variant and observed_case.variant.category == "snv"%}
206
+ <a class="badge rounded-pill bg-light text-dark" target="_blank" href="{{ url_for('variant.variant', institute_id=observed_case.case.owner, case_name=observed_case.case.display_name, variant_id=observed_case.variant._id) }}">{{ observed_case.case.display_name }}</a>
207
+ {% elif observed_case.variant and observed_case.variant.category == "sv"%}
208
+ <a class="badge rounded-pill bg-light text-dark" target="_blank" href="{{ url_for('variant.sv_variant', institute_id=observed_case.case.owner, case_name=observed_case.case.display_name, variant_id=observed_case.variant._id) }}">{{ observed_case.case.display_name }}</a>
209
+ {% else %}
210
+ <span data-bs-toggle="tooltip" title="Missing link, this might be caused by variants not loaded after a rerun or inexact SV matching.">
211
+ <span class="ml-3 badge rounded-pill bg-light text-dark">{{ observed_case.case.display_name }}</span>
212
+ </span>
213
+ {% endif %}
214
+
215
+ {% endmacro %}
216
+
203
217
  {% macro observations_panel(variant, observations, case) %}
204
218
  <div class="card panel-default">
205
219
  <div class="panel-heading d-flex justify-content-between">
@@ -240,19 +254,23 @@
240
254
  </span>
241
255
  {% endif %}
242
256
  {% endif %}
243
- {% for data in obs.cases %}
244
- {% if data.variant and data.variant.category == "snv"%}
245
- <a class="badge rounded-pill bg-light text-dark" target="_blank" href="{{ url_for('variant.variant', institute_id=data.case.owner, case_name=data.case.display_name, variant_id=data.variant._id) }}">{{ data.case.display_name }}</a>
246
- {% elif data.variant and data.variant.category == "sv"%}
247
- <a class="badge rounded-pill bg-light text-dark" target="_blank" href="{{ url_for('variant.sv_variant', institute_id=data.case.owner, case_name=data.case.display_name, variant_id=data.variant._id) }}">{{ data.case.display_name }}</a>
248
- {% else %}
249
- <span data-bs-toggle="tooltip" title="Missing link, this might be caused by variants not loaded after a rerun or inexact SV matching.">
250
- <span class="ml-3 badge rounded-pill bg-light text-dark">{{ data.case.display_name }}</span>
251
- </span>
252
- {% endif %}
257
+ {% for observed_case in obs.cases %}
258
+ {% if loop.index <= 10 %}
259
+ {{ observation_badge(observed_case) }}
260
+ {% endif %}
253
261
  {% endfor %}
262
+
254
263
  {% if obs.cases|length > 10 %}
255
- + more
264
+ <span class="collapse" id="additionalCases_{{ loqusid }}">
265
+ {% for observed_case in obs.cases %}
266
+ {% if loop.index > 10 %}
267
+ {{ observation_badge(observed_case) }}
268
+ {% endif %}
269
+ {% endfor %}
270
+ </span>
271
+ <button class="btn btn-link p-0 toggle-button" type="button" data-bs-toggle="collapse" href="#additionalCases_{{ loqusid }}">
272
+ <span class="toggle-text">Show more/less</span>
273
+ </button>
256
274
  {% endif %}
257
275
  </td>
258
276
  </tr>
@@ -2,7 +2,14 @@ import logging
2
2
  from typing import Dict, List, Optional, Tuple
3
3
 
4
4
  from scout.adapter import MongoAdapter
5
- from scout.constants import ACMG_COMPLETE_MAP, CALLERS, CLINSIG_MAP, SO_TERMS, VARIANT_FILTERS
5
+ from scout.constants import (
6
+ ACMG_COMPLETE_MAP,
7
+ CALLERS,
8
+ CCV_COMPLETE_MAP,
9
+ CLINSIG_MAP,
10
+ SO_TERMS,
11
+ VARIANT_FILTERS,
12
+ )
6
13
  from scout.server.links import add_gene_links, add_tx_links
7
14
 
8
15
  LOG = logging.getLogger(__name__)
@@ -472,6 +479,19 @@ def evaluation(store, evaluation_obj):
472
479
  return evaluation_obj
473
480
 
474
481
 
482
+ def ccv_evaluation(store, evaluation_obj):
483
+ """Fetch and fill-in evaluation object."""
484
+ evaluation_obj["institute"] = store.institute(evaluation_obj["institute_id"])
485
+ evaluation_obj["case"] = store.case(evaluation_obj["case_id"])
486
+ evaluation_obj["variant"] = store.variant(evaluation_obj["variant_specific"])
487
+ evaluation_obj["ccv_criteria"] = {
488
+ criterion["term"]: criterion for criterion in evaluation_obj["ccv_criteria"]
489
+ }
490
+ evaluation_obj["ccv_classification"] = CCV_COMPLETE_MAP.get(
491
+ evaluation_obj["ccv_classification"]
492
+ )
493
+
494
+
475
495
  def transcript_str(transcript_obj, gene_name=None):
476
496
  """Generate amino acid change as a string.
477
497
 
@@ -13,13 +13,26 @@ 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, ACMG_OPTIONS
17
- from scout.server.blueprints.variant.controllers import check_reset_variant_classification
16
+ from scout.constants import (
17
+ ACMG_CRITERIA,
18
+ ACMG_MAP,
19
+ ACMG_OPTIONS,
20
+ CCV_CRITERIA,
21
+ CCV_MAP,
22
+ CCV_OPTIONS,
23
+ )
24
+ from scout.server.blueprints.variant.controllers import ccv_evaluation as ccv_evaluation_controller
25
+ from scout.server.blueprints.variant.controllers import (
26
+ check_reset_variant_ccv_classification,
27
+ check_reset_variant_classification,
28
+ )
18
29
  from scout.server.blueprints.variant.controllers import evaluation as evaluation_controller
19
30
  from scout.server.blueprints.variant.controllers import observations, str_variant_reviewer
20
31
  from scout.server.blueprints.variant.controllers import variant as variant_controller
21
32
  from scout.server.blueprints.variant.controllers import variant_acmg as acmg_controller
22
33
  from scout.server.blueprints.variant.controllers import variant_acmg_post
34
+ from scout.server.blueprints.variant.controllers import variant_ccv as ccv_controller
35
+ from scout.server.blueprints.variant.controllers import variant_ccv_post
23
36
  from scout.server.blueprints.variant.verification_controllers import (
24
37
  MissingVerificationRecipientError,
25
38
  variant_verification,
@@ -27,6 +40,7 @@ from scout.server.blueprints.variant.verification_controllers import (
27
40
  from scout.server.extensions import loqusdb, store
28
41
  from scout.server.utils import institute_and_case, public_endpoint, templated
29
42
  from scout.utils.acmg import get_acmg, get_acmg_conflicts, get_acmg_temperature
43
+ from scout.utils.ccv import get_ccv, get_ccv_conflicts, get_ccv_temperature
30
44
  from scout.utils.ensembl_rest_clients import EnsemblRestApiClient
31
45
 
32
46
  LOG = logging.getLogger(__name__)
@@ -205,9 +219,40 @@ def variant_acmg(institute_id, case_name, variant_id):
205
219
  )
206
220
 
207
221
 
222
+ @variant_bp.route("/<institute_id>/<case_name>/<variant_id>/ccv", methods=["GET", "POST"])
223
+ @templated("variant/ccv.html")
224
+ def variant_ccv(institute_id, case_name, variant_id):
225
+ """ClinGen-CCG-VICC classification form."""
226
+ if request.method == "GET":
227
+ data = ccv_controller(store, institute_id, case_name, variant_id)
228
+ return data
229
+
230
+ criteria = []
231
+ criteria_terms = request.form.getlist("criteria")
232
+ for term in criteria_terms:
233
+ criteria.append(
234
+ dict(
235
+ term=term,
236
+ comment=request.form.get("comment-{}".format(term)),
237
+ links=[request.form.get("link-{}".format(term))],
238
+ )
239
+ )
240
+ ccv = variant_ccv_post(store, institute_id, case_name, variant_id, current_user.email, criteria)
241
+ flash("classified as: {}".format(ccv), "info")
242
+ return redirect(
243
+ url_for(
244
+ ".variant",
245
+ institute_id=institute_id,
246
+ case_name=case_name,
247
+ variant_id=variant_id,
248
+ )
249
+ )
250
+
251
+
208
252
  @variant_bp.route("/<institute_id>/<case_name>/<variant_id>/update", methods=["POST"])
209
253
  def variant_update(institute_id, case_name, variant_id):
210
- """Update user-defined information about a variant: manual rank & ACMG."""
254
+ """Update user-defined information about a variant: manual rank, cancer tier,
255
+ CLinGen-CGC-VICC classification, dismissal & mosaic tags."""
211
256
  institute_obj, case_obj = institute_and_case(store, institute_id, case_name)
212
257
  variant_obj = store.variant(variant_id)
213
258
  user_obj = store.user(current_user.email)
@@ -250,6 +295,7 @@ def variant_update(institute_id, case_name, variant_id):
250
295
  flash("Variant tag was updated", "info")
251
296
  else:
252
297
  flash("Variant tag was reset", "info")
298
+
253
299
  elif request.form.get("acmg_classification"):
254
300
  new_acmg = request.form["acmg_classification"]
255
301
  acmg_classification = variant_obj.get("acmg_classification")
@@ -268,6 +314,24 @@ def variant_update(institute_id, case_name, variant_id):
268
314
  )
269
315
  flash("updated ACMG classification: {}".format(new_acmg), "info")
270
316
 
317
+ elif request.form.get("ccv_classification"):
318
+ new_ccv = request.form["ccv_classification"]
319
+ ccv_classification = variant_obj.get("ccv_classification")
320
+ # If there already is a classification and the same one is sent again this means that
321
+ # We want to remove the classification
322
+ if isinstance(ccv_classification, int) and (new_ccv == CCV_MAP[ccv_classification]):
323
+ new_ccv = None
324
+
325
+ store.submit_ccv_evaluation(
326
+ variant_obj=variant_obj,
327
+ user_obj=user_obj,
328
+ institute_obj=institute_obj,
329
+ case_obj=case_obj,
330
+ link=link,
331
+ classification=new_ccv,
332
+ )
333
+ flash("updated ClinGen-CGC-VIGG classification: {}".format(new_ccv), "info")
334
+
271
335
  new_dismiss = request.form.getlist("dismiss_variant")
272
336
  if new_dismiss:
273
337
  store.update_dismiss_variant(
@@ -353,6 +417,53 @@ def acmg():
353
417
  return jsonify({"classification": classification, "conflicts": acmg_conflicts, **acmg_bayesian})
354
418
 
355
419
 
420
+ @variant_bp.route("/ccv_evaluations/<evaluation_id>", methods=["GET", "POST"])
421
+ @templated("variant/ccv.html")
422
+ def ccv_evaluation(evaluation_id):
423
+ """Show, edit or delete an ClinGen-CGC-VIGG evaluation."""
424
+
425
+ evaluation_obj = store.get_ccv_evaluation(evaluation_id)
426
+ if evaluation_obj is None:
427
+ flash("Evaluation was not found in database", "warning")
428
+ return redirect(request.referrer)
429
+ ccv_evaluation_controller(store, evaluation_obj)
430
+ if request.method == "POST":
431
+ link = url_for(
432
+ ".variant",
433
+ institute_id=evaluation_obj["institute"]["_id"],
434
+ case_name=evaluation_obj["case"]["display_name"],
435
+ variant_id=evaluation_obj["variant_specific"],
436
+ )
437
+ store.delete_ccv_evaluation(evaluation_obj)
438
+
439
+ if check_reset_variant_ccv_classification(store, evaluation_obj, link):
440
+ flash("Cleared ClinGen-CGC-VIGG classification.", "info")
441
+
442
+ return redirect(link)
443
+
444
+ return dict(
445
+ evaluation=evaluation_obj,
446
+ edit=bool(request.args.get("edit")),
447
+ institute=evaluation_obj["institute"],
448
+ case=evaluation_obj["case"],
449
+ variant=evaluation_obj["variant"],
450
+ CRITERIA=CCV_CRITERIA,
451
+ CCV_OPTIONS=CCV_OPTIONS,
452
+ )
453
+
454
+
455
+ @variant_bp.route("/api/v1/ccv")
456
+ @public_endpoint
457
+ def ccv():
458
+ """Calculate an ClinGen-CVC-VIGG classification from submitted criteria."""
459
+ criteria = request.args.getlist("criterion")
460
+ classification = get_ccv(criteria)
461
+
462
+ ccv_bayesian = get_ccv_temperature(criteria)
463
+ ccv_conflicts = get_ccv_conflicts(criteria)
464
+ return jsonify({"classification": classification, "conflicts": ccv_conflicts, **ccv_bayesian})
465
+
466
+
356
467
  @variant_bp.route(
357
468
  "/<institute_id>/<case_name>/<variant_id>/<order>",
358
469
  methods=["POST"],
@@ -17,6 +17,8 @@ from scout.constants import (
17
17
  CANCER_EXPORT_HEADER,
18
18
  CANCER_SPECIFIC_VARIANT_DISMISS_OPTIONS,
19
19
  CANCER_TIER_OPTIONS,
20
+ CCV_COMPLETE_MAP,
21
+ CCV_MAP,
20
22
  CHROMOSOMES,
21
23
  CHROMOSOMES_38,
22
24
  CLINSIG_MAP,
@@ -362,6 +364,7 @@ def get_manual_assessments(variant_obj):
362
364
  ## display manual input of interest: classified, commented, tagged, mosaicism or dismissed.
363
365
  assessment_keywords = [
364
366
  "acmg_classification",
367
+ "ccv_classification",
365
368
  "manual_rank",
366
369
  "cancer_tier",
367
370
  "dismiss_variant",
@@ -402,6 +405,16 @@ def get_manual_assessments(variant_obj):
402
405
  assessment["label"] = classification["short"]
403
406
  assessment["display_class"] = classification["color"]
404
407
 
408
+ if assessment_type == "ccv_classification":
409
+ ccv_classification = variant_obj[assessment_type]
410
+ if isinstance(ccv_classification, int):
411
+ ccv_code = CCV_MAP[ccv_classification]
412
+ ccv_classification = CCV_COMPLETE_MAP[ccv_code]
413
+
414
+ assessment["title"] = "ClinGen-CGC-VIGG: {}".format(ccv_classification["label"])
415
+ assessment["label"] = ccv_classification["short"]
416
+ assessment["display_class"] = ccv_classification["color"]
417
+
405
418
  if assessment_type == "dismiss_variant":
406
419
  dismiss_variant_options = {
407
420
  **DISMISS_VARIANT_OPTIONS,
@@ -918,6 +931,11 @@ def parse_variant(
918
931
  acmg_code = ACMG_MAP[variant_obj["acmg_classification"]]
919
932
  variant_obj["acmg_classification"] = ACMG_COMPLETE_MAP[acmg_code]
920
933
 
934
+ ccv_classification = variant_obj.get("ccv_classification")
935
+ if isinstance(ccv_classification, int):
936
+ ccv_code = CCV_MAP[variant_obj["ccv_classification"]]
937
+ variant_obj["ccv_classification"] = CCV_COMPLETE_MAP[ccv_code]
938
+
921
939
  # convert length for SV variants
922
940
  variant_length = variant_obj.get("length")
923
941
  variant_obj["length"] = {100000000000: "inf", -1: "n.d."}.get(variant_length, variant_length)
@@ -1409,6 +1427,19 @@ def cancer_variants(store, institute_id, case_name, variants_query, variant_coun
1409
1427
  secondary_gene = gene
1410
1428
  variant_obj["second_rep_gene"] = secondary_gene
1411
1429
  variant_obj["clinical_assessments"] = get_manual_assessments(variant_obj)
1430
+
1431
+ evaluations = []
1432
+ # Get previous ClinGen-CGC-VIGG evaluations of the variant from other cases
1433
+ for evaluation_obj in store.get_ccv_evaluations(variant_obj):
1434
+ if evaluation_obj["case_id"] == case_obj["_id"]:
1435
+ continue
1436
+
1437
+ ccv_classification = evaluation_obj["ccv_classification"]
1438
+
1439
+ evaluation_obj["ccv_classification"] = CCV_COMPLETE_MAP.get(ccv_classification)
1440
+ evaluations.append(evaluation_obj)
1441
+ variant_obj["ccv_evaluations"] = evaluations
1442
+
1412
1443
  variants_list.append(variant_obj)
1413
1444
 
1414
1445
  data = dict(
@@ -2,7 +2,7 @@
2
2
 
3
3
  {% from "variants/components.html" import external_scripts, external_stylesheets, gene_cell, frequency_cell_general, observed_cell_general, variant_funct_anno_cell %}
4
4
  {% from "variants/utils.html" import cancer_filters, cell_rank, pagination_footer, pagination_hidden_div, dismiss_variants_block, filter_form_footer, filter_script_main, update_stash_filter_button_status, callers_cell %}
5
- {% from "variants/indicators.html" import pin_indicator, causative_badge, clinical_assessments_badge, comments_badge, dismissals_badge, evaluations_badge, group_assessments_badge, matching_manual_rank, other_tiered_variants, research_assessments_badge %}
5
+ {% from "variants/indicators.html" import pin_indicator, causative_badge, clinical_assessments_badge, comments_badge, dismissals_badge, evaluations_badge, ccv_evaluations_badge, group_assessments_badge, matching_manual_rank, other_tiered_variants, research_assessments_badge %}
6
6
 
7
7
  {% block title %}
8
8
  {{ variant_type|capitalize }} somatic variants
@@ -109,6 +109,7 @@
109
109
  {{ comments_badge(case, institute, variant) }}
110
110
  {{ causative_badge(variant, case) }}
111
111
  {{ other_tiered_variants(variant) }}
112
+ {{ ccv_evaluations_badge(variant) }}
112
113
  </td>
113
114
  <td>{{ rank_cell(variant) }}</td>
114
115
  <td>{{ cadd_cell(variant) }}</td>
@@ -161,94 +161,84 @@
161
161
 
162
162
  {% macro gene_cell(variant, inherit_palette) %}
163
163
  <div class="align-items-center">
164
- {% if variant.category == "cancer" or variant.category == "sv_cancer" %}
165
- <a data-bs-toggle="tooltip" data-bs-html="true" title="
164
+
165
+ {% macro gene_tooltip(gene) %}
166
+ <div>
166
167
  <div>
168
+ <strong>{{ gene.hgnc_symbol }}</strong>: {{ gene.description }}
169
+ </div>
170
+ {% if gene.inheritance %}
167
171
  <div>
168
- <strong>{{ variant.first_rep_gene.hgnc_symbol }}</strong>: {{ variant.first_rep_gene.description }}
172
+ <strong>Models</strong>: {{ gene.inheritance|join(',') }}
169
173
  </div>
170
- {% if variant.first_rep_gene.inheritance %}
171
- <div>
172
- <strong>Models</strong>: {{ variant.first_rep_gene.inheritance|join(',') }}
173
- </div>
174
- {% endif %}
175
- {% if variant.first_rep_gene.phenotypes %}
174
+ {% endif %}
175
+ {% if gene.phenotypes %}
176
176
  <div><strong>OMIM disease</strong>
177
- {% for disease in variant.first_rep_gene.phenotypes %}
178
- <div>
179
- {{ disease.description }}
180
- </div>
177
+ {% for disease in gene.phenotypes %}
178
+ <div>{{ disease.description }}</div>
181
179
  {% endfor %}
182
180
  </div>
183
181
  {% endif %}
184
- </div>"
185
- {% if variant.first_rep_gene %}
186
- href="{{ url_for('genes.gene', hgnc_id=variant.first_rep_gene.hgnc_id) }}"
187
- {% endif %}>
188
- {{ variant.first_rep_gene.hgnc_symbol or variant.first_rep_gene.hgnc_id }}
189
- {% if variant.secondary_gene %}
190
- <span class="text-muted">
191
- ({{ variant.second_rep_gene.hgnc_symbol or variant.second_rep_gene.hgnc_id }})
192
- </span>
193
- {% endif %}
182
+ </div>
183
+ {% endmacro %}
184
+
185
+ {% macro gene_link(gene) %}
186
+ <a
187
+ data-bs-toggle="tooltip"
188
+ data-bs-html="true"
189
+ title="{{ gene_tooltip(gene) }}"
190
+ href="{{ url_for('genes.gene', hgnc_id=gene.hgnc_id) }}"
191
+ >
192
+ {{ gene.hgnc_symbol or gene.hgnc_id }}
194
193
  </a>
194
+ {% endmacro %}
195
+
196
+ {% macro panel_badge(variant, gene_id=0) %}
197
+
198
+ {% set matching_panels = [] %}
199
+ {% for panel in variant.case_panels|rejectattr('removed')|list %}
200
+ {% if gene_id in panel.hgnc_ids %}
201
+ {% set _ = matching_panels.append(panel) %}
202
+ {% endif %}
203
+ {% endfor %}
195
204
 
196
- {% set panel_count = variant.case_panels|rejectattr('removed')|list|length %}
197
- {% if panel_count > 0%}
198
- <a
199
- class="badge bg-secondary text-white"
200
- data-bs-toggle="popover"
201
- data-bs-html="true"
202
- data-bs-trigger="hover click"
203
- title="Overlapping gene panels"
204
- data-bs-content="{% for panel in variant.case_panels|sort(attribute='panel_name',case_sensitive=False)|rejectattr('removed') %}
205
- {{ panel.panel_name|safe }}<br>
206
- {% endfor %}"
207
- >{{panel_count}}
208
- </a>
205
+ {% set panel_count = matching_panels|length %}
206
+ <a
207
+ class="badge bg-secondary text-white"
208
+ data-bs-toggle="popover"
209
+ data-bs-html="true"
210
+ data-bs-trigger="hover click"
211
+ title="Overlapping gene panels"
212
+ data-bs-content="{% for panel in matching_panels %}
213
+ {{ panel.panel_name|safe }}<br>
214
+ {% endfor %}"
215
+ >{{ panel_count }}</a>
216
+ {% endmacro %}
217
+
218
+ {% if variant.category in ["cancer", "sv_cancer"] %}
219
+ {% if variant.first_rep_gene %}
220
+ {{ gene_link(variant.first_rep_gene) }}
221
+ {% endif %}
222
+ {% if variant.secondary_gene %}
223
+ <span class="text-muted">
224
+ ({{ variant.second_rep_gene.hgnc_symbol or variant.second_rep_gene.hgnc_id }})
225
+ </span>
226
+ {% endif %}
227
+ {% if variant.first_rep_gene or variant.second_rep_gene %}
228
+ {{ panel_badge(variant, variant.first_rep_gene.hgnc_id or variant.second_rep_gene.hgnc_id) }}
209
229
  {% endif %}
210
230
  {% else %}
211
- {% set panel_count = variant.case_panels|rejectattr('removed')|list|count %}
212
231
  {% for gene in variant.genes %}
213
232
  <div>
214
- <a data-bs-toggle="tooltip" data-bs-html="true" title="
215
- <div>
216
- <div>
217
- <strong>{{ gene.hgnc_symbol }}</strong>: {{ gene.description }}
218
- </div>
219
- {% if gene.inheritance %}
220
- <div>
221
- <strong>Models</strong>: {{ gene.inheritance|join(',') }}
222
- </div>
223
- {% endif %}
224
- {% if gene.phenotypes %}
225
- <div><strong>OMIM disease</strong>
226
- {% for disease in gene.phenotypes %}
227
- <div>
228
- {{ disease.description }}
229
- </div>
230
- {% endfor %}
231
- </div>
232
- {% endif %}
233
- </div>"
234
- href="{{ url_for('genes.gene', hgnc_id=gene.hgnc_id) }}">{{ gene.hgnc_symbol or gene.hgnc_id }}
235
- {% for model in gene.inheritance %} {{ inheritance_badge(model,inherit_palette) }}{% endfor %}</a>
236
- {% if panel_count > 0 %}
237
- <a
238
- class="badge bg-secondary text-white"
239
- data-bs-toggle="popover"
240
- data-bs-html="true"
241
- data-bs-trigger="hover click"
242
- data-bs-content="{% for panel in variant.case_panels|sort(attribute='panel_name',case_sensitive=False)|rejectattr('removed') %}
243
- {{ panel.panel_name|safe }}<br>
244
- {% endfor %}"
245
- title="Overlapping gene panels">{{panel_count}}
246
- </a>
247
- {% endif %}
248
- </div>
233
+ {{ gene_link(gene) }}
234
+ {% for model in gene.inheritance %}
235
+ {{ inheritance_badge(model, inherit_palette) }}
236
+ {% endfor %}
237
+ {{ panel_badge(variant, gene.hgnc_id) }}
238
+ </div>
249
239
  {% endfor %}
250
-
251
240
  {% endif %}
241
+
252
242
  </div>
253
243
  {% endmacro %}
254
244
 
@@ -74,6 +74,17 @@
74
74
  {% endif %}
75
75
  {% endmacro %}
76
76
 
77
+ {% macro ccv_evaluations_badge(variant) %}
78
+ {% if variant.ccv_evaluations %}
79
+ {% for evaluation in (variant.ccv_evaluations or []) %}
80
+ <span class="badge bg-secondary" style="margin-left:1px" data-bs-toggle="tooltip" data-bs-placement="right"
81
+ title="Previously classified as {{ evaluation.ccv_classification.label }}">
82
+ {{ evaluation.ccv_classification.short }}
83
+ </span>
84
+ {% endfor %}
85
+ {% endif %}
86
+ {% endmacro %}
87
+
77
88
  {% macro dismissals_badge(variant) %}
78
89
  {% if variant.dismissals %}
79
90
  <span class="badge bg-secondary" style="margin-left:1px" data-bs-toggle="tooltip" data-bs-html="true" data-bs-placement="top"
@@ -1,3 +1,11 @@
1
+ const imgMaxWidth = window.innerWidth*0.7;
2
+ const imgMaxHeight = window.innerHeight*0.7;
3
+
4
+ function calculateAspectRatioFit(srcWidth, srcHeight, maxWidth, maxHeight) {
5
+ let ratio = Math.min(maxWidth / srcWidth, maxHeight / srcHeight);
6
+ return [srcWidth*ratio, srcHeight*ratio];
7
+ }
8
+
1
9
  /**
2
10
  * This function fetches an image from disk and displays it in a specified div
3
11
  * @param {string} imageUrl - The URL of the image to fetch
@@ -9,15 +17,24 @@
9
17
  .then(blob => {
10
18
  const img = document.createElement("img");
11
19
  img.src = URL.createObjectURL(blob);
20
+ if (width > imgMaxWidth || height > imgMaxHeight) {
21
+ const resized_pixels = calculateAspectRatioFit(width, height, imgMaxWidth, imgMaxHeight);
22
+ width = resized_pixels[0];
23
+ height = resized_pixels[1];
24
+ }
12
25
  if (width) {
13
26
  img.style.width = width+'px';
14
27
  }
15
28
  if (height) {
16
29
  img.style.height = height+'px';
17
30
  }
18
- // Display the image in the specified div
31
+ // Add the image to a link and add the link to the div
32
+ const link = document.createElement('a');
33
+ link.appendChild(img);
34
+ link.href = img.src;
35
+ link.setAttribute('target', "_blank");
19
36
  const div = document.getElementById(divId);
20
- div.appendChild(img);
37
+ div.appendChild(link);
21
38
  })
22
39
  .catch(error => console.error(error));
23
40
  }