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
@@ -0,0 +1,26 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ scout.models.ccv_evaluation
4
+ ~~~~~~~~~~~~~~~~~~
5
+
6
+ Define a document to describe a ClinGen-CGC-VIGG evaluation
7
+
8
+ Evaluations are stored in its own collection
9
+
10
+ """
11
+
12
+ from datetime import datetime
13
+
14
+ ccv_evaluation = dict(
15
+ variant_specific=str, # md5 document id
16
+ variant_id=str, # md5 variant id
17
+ institute_id=str, # Institute _id, required
18
+ case_id=str, # case_id, required
19
+ classification=str, # What did the evaluation end up in?
20
+ # All evaluations will have an author
21
+ user_id=str, # user email, required
22
+ user_name=str, # user name
23
+ criteria=list, # List of dictionaries with criterias
24
+ # timestamps
25
+ created_at=datetime,
26
+ )
@@ -94,6 +94,7 @@ variant = dict(
94
94
  manual_rank=int, # choices=[0, 1, 2, 3, 4, 5]
95
95
  dismiss_variant=list,
96
96
  acmg_classification=str, # choices=ACMG_TERMS
97
+ ccv_classification=str, # choices=CCV_TERMS
97
98
  )
98
99
 
99
100
  compound = dict(
@@ -40,6 +40,11 @@
40
40
  <li class="nav-item">
41
41
  <a class="nav-link link-secondary" style="text-decoration: none !important;" href="#acmg_variants">ACMG-classified variants</a>
42
42
  </li>
43
+ {% if cancer %}
44
+ <li class="nav-item">
45
+ <a class="nav-link link-secondary" style="text-decoration: none !important;" href="#ccv_variants">ClinGen-CGC-VICC-classified variants</a>
46
+ </li>
47
+ {% endif %}
43
48
  <li class="nav-item">
44
49
  <a class="nav-link link-secondary" style="text-decoration: none !important;" href="#manual_ranked_variants">Manual ranked</a>
45
50
  </li>
@@ -64,6 +69,7 @@
64
69
  {{ causatives_panel()}}
65
70
  {{ pinned_panel() }}
66
71
  {{ classified_panel() }}
72
+ {% if cancer %}{{ ccv_classified_panel() }}{% endif %}
67
73
  {{ tagged_panel() }}
68
74
  {{ commented_panel() }}
69
75
  {{ dismissed_panel() }}
@@ -392,6 +398,33 @@
392
398
  </div>
393
399
  {% endmacro %}
394
400
 
401
+ {% macro ccv_classified_panel() %}
402
+ <div class="card border-warning mb-3" style="border-width: 5px; display: block;">
403
+ <div class="card-header bg-warning text-dark" style="border-top-left-radius: 0; border-top-right-radius: 0;">
404
+ <a id="ccv_variants"><strong>Other ClinGen-CGC-VICC-classified Variants</strong></a>
405
+ </div>
406
+ <div class="card-body">
407
+ {% set duplicated_variants = [] %}
408
+ {% if variants.ccv_classified_detailed %}
409
+ {% for variant in variants.ccv_classified_detailed|sort(attribute='variant_rank') %}
410
+ {% if variant['_id'] not in printed_vars %}
411
+ {% do printed_vars.append(variant['_id']) %}
412
+ {{ variant_content(variant, loop.index) }}
413
+ <br>
414
+ {% else %}
415
+ {% do duplicated_variants.append(variant['_id']) %}
416
+ {% endif %}
417
+ {% endfor %}
418
+ {% else %}
419
+ No ClinGen-CGC-VICC-classified variants available for this case
420
+ {% endif %}
421
+ {% if variants.ccv_classified_detailed and duplicated_variants|length == variants.ccv_classified_detailed|length %}
422
+ All ClinGen-CGC-VICC-classified variants are already described in the previous views
423
+ {% endif %}
424
+ </div>
425
+ </div>
426
+ {% endmacro %}
427
+
395
428
  {% macro tagged_panel() %}
396
429
  <div class="card border-warning mb-3" style="border-width: 5px; display: block;">
397
430
  <div class="card-header bg-warning text-dark" style="border-top-left-radius: 0; border-top-right-radius: 0;">
@@ -635,6 +668,9 @@
635
668
  <th>Inheritance models</th>
636
669
  {% endif %}
637
670
  <th>ACMG classification</th>
671
+ {% if cancer %}
672
+ <th>ClinGen-CGC-VICC classification</th>
673
+ {% endif %}
638
674
  </tr>
639
675
  </thead>
640
676
  <tbody>
@@ -683,6 +719,15 @@
683
719
  -
684
720
  {% endif %}
685
721
  </td>
722
+ {% if cancer %}
723
+ <td>
724
+ {% if variant.ccv_classification %}
725
+ <span class="badge rounded-pill bg-{{variant.ccv_classification['color'] if variant.ccv_classification['color'] else 'secondary'}}" title="{{variant.ccv_classification['code']}}">{{variant.ccv_classification['code'] }}</span>
726
+ {% else %}
727
+ -
728
+ {% endif %}
729
+ </td>
730
+ {% endif %}
686
731
  </tr>
687
732
  </tbody>
688
733
  </table>
@@ -372,7 +372,7 @@
372
372
  <select class="form-control form-control-sm" name="collaborator">
373
373
  <option selected disabled value="">Select institute</option>
374
374
  {% for collab_id, collab_name in collaborators %}
375
- <option value="{{ collab_id }}">{{ collab_name }}</option>
375
+ <option value="{{ collab_id }}">{{ collab_name }} ({{ collab_id }})</option>
376
376
  {% endfor %}
377
377
  </select>
378
378
  <span class="input-group-btn">
@@ -389,7 +389,7 @@
389
389
  <select class="form-control form-control-sm" name="collaborator">
390
390
  <option>Institute</option>
391
391
  {% for collab_id, collab_name in case.o_collaborators %}
392
- <option value="{{ collab_id }}">{{ collab_name }}</option>
392
+ <option value="{{ collab_id }}">{{ collab_name }} ({{ collab_id }})</option>
393
393
  {% endfor %}
394
394
  </select>
395
395
  <div class="input-group-btn">
@@ -25,9 +25,7 @@
25
25
  <a href="{{ url_for('overview.cases', institute_id=institute._id) }}">
26
26
  {{ institute.display_name }}
27
27
  </a>
28
- {% if current_user.is_admin %}
29
28
  <span class="text-muted">({{ institute._id }})</span>
30
- {% endif %}
31
29
  <span class="badge bg-secondary rounded-pill float-end">{{ case_count }} cases </span>
32
30
  </li>
33
31
  {% endfor %}
@@ -29,7 +29,7 @@
29
29
  <div class="container-float">
30
30
  <div class="row" id="body-row"> <!--sidebar and main container are on the same row-->
31
31
  <div class="col-12">
32
- {{ variant_list_content(institute, causatives, acmg_map, callers, inherit_palette) }}
32
+ {{ variant_list_content(institute, causatives, acmg_map, ccv_map, callers, inherit_palette) }}
33
33
  </div>
34
34
  </div> <!-- end of div id body-row -->
35
35
  </div>
@@ -446,7 +446,7 @@
446
446
  {% endmacro %}
447
447
 
448
448
 
449
- {% macro variant_list_content(institute, variants, acmg_map, callers, inherit_palette) %}
449
+ {% macro variant_list_content(institute, variants, acmg_map, ccv_map, callers, inherit_palette) %}
450
450
  <div class="card mt-3">
451
451
  <div class="card-body overflow-auto">
452
452
  <table id="variants_table" class="table display table-sm">
@@ -469,6 +469,7 @@
469
469
  <th data-bs-toggle='tooltip' data-bs-container='body' title="ref/alt-GQ">Zygosity</th>
470
470
  <th>Inheritance</th>
471
471
  <th>ACMG</th>
472
+ <th>ClinGen-CGC-VICC</th>
472
473
  <th>Case</th>
473
474
  <th>Analysis type</th>
474
475
  <th>Validated status</th>
@@ -608,6 +609,16 @@
608
609
  {% endif %}
609
610
  </a>
610
611
  </td>
612
+ <td><!-- Clingen-CGC-VIGG -->
613
+ <a href="#" data-bs-toggle="tooltip" title="Clingen-CGC-VIGG classification assigned by Scout users"
614
+ style="text-decoration: none; color: #000;">
615
+ {% if 'ccv_classification' in variant %}
616
+ <span class="badge bg-{{ccv_map[variant.ccv_classification].color}}">{{ccv_map[variant.ccv_classification].short}}</span>
617
+ {% else %}
618
+ -
619
+ {% endif %}
620
+ </a>
621
+ </td>
611
622
  <td><!-- Case -->
612
623
  <a href="{{ url_for('cases.case',
613
624
  institute_id=institute._id,
@@ -58,7 +58,7 @@
58
58
  {{validated_chart()}}
59
59
  </div>
60
60
  <div class="row">
61
- <div class="col-12">{{ variant_list_content(institute, verified, acmg_map, callers, inherit_palette) }}</div>
61
+ <div class="col-12">{{ variant_list_content(institute, verified, acmg_map, ccv_map, callers, inherit_palette) }}</div>
62
62
  </div>
63
63
  </div>
64
64
  </div>
@@ -10,6 +10,8 @@ from scout.constants import (
10
10
  ACMG_COMPLETE_MAP,
11
11
  ACMG_MAP,
12
12
  CALLERS,
13
+ CCV_COMPLETE_MAP,
14
+ CCV_MAP,
13
15
  INHERITANCE_PALETTE,
14
16
  VERBS_ICONS_MAP,
15
17
  VERBS_MAP,
@@ -72,6 +74,7 @@ def verified(institute_id):
72
74
  return dict(
73
75
  acmg_map={key: ACMG_COMPLETE_MAP[value] for key, value in ACMG_MAP.items()},
74
76
  callers=CALLERS,
77
+ ccv_map={key: CCV_COMPLETE_MAP[value] for key, value in CCV_MAP.items()},
75
78
  inherit_palette=INHERITANCE_PALETTE,
76
79
  institute=institute_obj,
77
80
  verified=verified_vars,
@@ -87,6 +90,7 @@ def causatives(institute_id):
87
90
  acmg_map={key: ACMG_COMPLETE_MAP[value] for key, value in ACMG_MAP.items()},
88
91
  callers=CALLERS,
89
92
  causatives=controllers.causatives(institute_obj, request),
93
+ ccv_map={key: CCV_COMPLETE_MAP[value] for key, value in CCV_MAP.items()},
90
94
  institute=institute_obj,
91
95
  inherit_palette=INHERITANCE_PALETTE,
92
96
  )
@@ -160,7 +160,6 @@ def update_panel(store, panel_name, csv_lines, option):
160
160
  Returns:
161
161
  panel_obj(dict)
162
162
  """
163
- new_genes = []
164
163
  panel_obj = store.gene_panel(panel_name)
165
164
  if panel_obj is None:
166
165
  return None
@@ -202,11 +201,11 @@ def update_panel(store, panel_name, csv_lines, option):
202
201
  )
203
202
 
204
203
  info_data = {
205
- "disease_associated_transcripts": new_gene["transcripts"],
206
- "reduced_penetrance": new_gene["reduced_penetrance"],
207
- "mosaicism": new_gene["mosaicism"],
208
- "inheritance_models": new_gene["inheritance_models"],
209
- "database_entry_version": new_gene["database_entry_version"],
204
+ "disease_associated_transcripts": new_gene.get("transcripts", []),
205
+ "reduced_penetrance": new_gene.get("reduced_penetrance", ""),
206
+ "mosaicism": new_gene.get("mosaicism", ""),
207
+ "inheritance_models": new_gene.get("inheritance_models", []),
208
+ "database_entry_version": new_gene.get("database_entry_version", ""),
210
209
  }
211
210
  if (
212
211
  option == "replace"
@@ -223,15 +223,15 @@
223
223
  <span data-bs-toggle="tooltip" data-bs-placement="right" data-bs-html="true" title="
224
224
  <div class='text-left'>
225
225
  <strong>Transcripts</strong>:
226
- {{ gene.info.disease_associated_transcripts|join(',') }} <br>
226
+ {{ gene.info.disease_associated_transcripts|join(',') if gene.info.disease_associated_transcripts }} <br>
227
227
  <strong>Reduced penetrance</strong>:
228
- {{ 'yes' if gene.info.reduced_penetrance else 'no' }} <br>
228
+ {{ gene.info.reduced_penetrance if gene.info.reduced_penetrance }} <br>
229
229
  <strong>Mosaicism</strong>:
230
- {{ 'yes' if gene.info.mosaicism else 'no' }} <br>
230
+ {{ gene.info.mosaicism if gene.info.mosaicism }} <br>
231
231
  <strong>Inheritance models</strong>:
232
- {{ gene.info.inheritance_models|join(',') }} <br>
232
+ {{ gene.info.inheritance_models|join(',') if gene.info.inheritance_models }} <br>
233
233
  <strong>Entry version</strong>:
234
- {{ gene.info.database_entry_version }} <br>
234
+ {{ gene.info.database_entry_version if gene.info.database_entry_version }} <br>
235
235
  </div>
236
236
  ">
237
237
  {{ gene.symbol }}
@@ -16,6 +16,10 @@ from scout.constants import (
16
16
  CANCER_SPECIFIC_VARIANT_DISMISS_OPTIONS,
17
17
  CANCER_TIER_OPTIONS,
18
18
  CASE_TAGS,
19
+ CCV_COMPLETE_MAP,
20
+ CCV_CRITERIA,
21
+ CCV_MAP,
22
+ CCV_OPTIONS,
19
23
  DISMISS_VARIANT_OPTIONS,
20
24
  IGV_TRACKS,
21
25
  INHERITANCE_PALETTE,
@@ -41,6 +45,7 @@ from scout.server.utils import (
41
45
  from .utils import (
42
46
  add_gene_info,
43
47
  associate_variant_genes_with_case_panels,
48
+ ccv_evaluation,
44
49
  clinsig_human,
45
50
  default_panels,
46
51
  end_position,
@@ -194,9 +199,11 @@ def variant(
194
199
  'cancer_tier_options': CANCER_TIER_OPTIONS,
195
200
  'dismiss_variant_options': DISMISS_VARIANT_OPTIONS,
196
201
  'ACMG_OPTIONS': ACMG_OPTIONS,
202
+ 'CCV_OPTIONS': CCV_OPTIONS,
197
203
  'igv_tracks': IGV_TRACKS,
198
204
  'gens_info': <dict>,
199
205
  'evaluations': <list(evaluations)>,
206
+ 'ccv_evaluations': <list(evaluations)>,
200
207
  'rank_score_results': <list(rank_score_results)>,
201
208
  }
202
209
 
@@ -340,6 +347,17 @@ def variant(
340
347
  evaluation(store, evaluation_obj)
341
348
  evaluations.append(evaluation_obj)
342
349
 
350
+ # Prepare classification information for visualisation
351
+ ccv_classification = variant_obj.get("ccv_classification")
352
+ if isinstance(ccv_classification, int):
353
+ ccv_code = CCV_MAP[variant_obj["ccv_classification"]]
354
+ variant_obj["ccv_classification"] = CCV_COMPLETE_MAP[ccv_code]
355
+
356
+ ccv_evaluations = []
357
+ for evaluation_obj in store.get_ccv_evaluations(variant_obj):
358
+ ccv_evaluation(store, evaluation_obj)
359
+ ccv_evaluations.append(evaluation_obj)
360
+
343
361
  case_clinvars = store.case_to_clinVars(case_obj.get("display_name"))
344
362
 
345
363
  if variant_id in case_clinvars:
@@ -379,12 +397,14 @@ def variant(
379
397
  "dismiss_variant_options": dismiss_options,
380
398
  "mosaic_variant_options": MOSAICISM_OPTIONS,
381
399
  "ACMG_OPTIONS": ACMG_OPTIONS,
400
+ "CCV_OPTIONS": CCV_OPTIONS,
382
401
  "case_tag_options": CASE_TAGS,
383
402
  "inherit_palette": INHERITANCE_PALETTE,
384
403
  "igv_tracks": get_igv_tracks("38" if variant_obj["is_mitochondrial"] else genome_build),
385
404
  "has_rna_tracks": case_has_rna_tracks(case_obj),
386
405
  "gens_info": gens.connection_settings(genome_build),
387
406
  "evaluations": evaluations,
407
+ "ccv_evaluations": ccv_evaluations,
388
408
  "rank_score_results": variant_rank_scores(store, case_obj, variant_obj),
389
409
  }
390
410
 
@@ -442,7 +462,7 @@ def get_loqusdb_obs_cases(
442
462
  obs_cases = []
443
463
  user_institutes_ids = set([inst["_id"] for inst in user_institutes(store, current_user)])
444
464
  for i, case_id in enumerate(obs_families):
445
- if len(obs_cases) == 10:
465
+ if len(obs_cases) == 50:
446
466
  break
447
467
  if case_id == variant_obj["case_id"]:
448
468
  continue
@@ -708,3 +728,130 @@ def variant_acmg_post(
708
728
  criteria=criteria,
709
729
  )
710
730
  return classification
731
+
732
+
733
+ def variant_ccv(store: MongoAdapter, institute_id: str, case_name: str, variant_id: str):
734
+ """Collect data relevant for rendering ClinGen-CCV-VIGG classification form.
735
+
736
+ Args:
737
+ store(scout.adapter.MongoAdapter)
738
+ institute_id(str): institute_obj['_id']
739
+ case_name(str): case_obj['display_name']
740
+ variant_id(str): variant_obj['document_id']
741
+
742
+ Returns:
743
+ data(dict): Things for the template
744
+ """
745
+ variant_obj = store.variant(variant_id)
746
+
747
+ if not variant_obj:
748
+ return abort(404)
749
+
750
+ institute_obj, case_obj = variant_institute_and_case(
751
+ store, variant_obj, institute_id, case_name
752
+ )
753
+
754
+ return dict(
755
+ institute=institute_obj,
756
+ case=case_obj,
757
+ variant=variant_obj,
758
+ CRITERIA=CCV_CRITERIA,
759
+ CCV_OPTIONS=CCV_OPTIONS,
760
+ )
761
+
762
+
763
+ def check_reset_variant_ccv_classification(
764
+ store: MongoAdapter, evaluation_obj: dict, link: str
765
+ ) -> bool:
766
+ """Check if this was the last ClinGen-CGC-VIGG evaluation left on the variant.
767
+ If there is still a classification we want to remove the classification.
768
+
769
+ Args:
770
+ stores(cout.adapter.MongoAdapter)
771
+ evaluation_obj(dict): ClinGen-CGC-VIGG evaluation object
772
+ link(str): link for event
773
+
774
+ Returns: reset(bool) - True if classification reset was attempted
775
+
776
+ """
777
+
778
+ if list(store.get_ccv_evaluations_case_specific(evaluation_obj["variant_specific"])):
779
+ return False
780
+
781
+ variant_obj = store.variant(document_id=evaluation_obj["variant_specific"])
782
+
783
+ if not variant_obj:
784
+ return abort(404)
785
+
786
+ ccv_classification = variant_obj.get("ccv_classification")
787
+
788
+ if not isinstance(ccv_classification, int):
789
+ return False
790
+
791
+ institute_obj, case_obj = variant_institute_and_case(
792
+ store,
793
+ variant_obj,
794
+ evaluation_obj["institute"]["_id"],
795
+ evaluation_obj["case"]["display_name"],
796
+ )
797
+ user_obj = store.user(current_user.email)
798
+
799
+ new_ccv = None
800
+ store.submit_ccv_evaluation(
801
+ variant_obj=variant_obj,
802
+ user_obj=user_obj,
803
+ institute_obj=institute_obj,
804
+ case_obj=case_obj,
805
+ link=link,
806
+ classification=new_ccv,
807
+ )
808
+ return True
809
+
810
+
811
+ def variant_ccv_post(
812
+ store: MongoAdapter,
813
+ institute_id: str,
814
+ case_name: str,
815
+ variant_id: str,
816
+ user_email: str,
817
+ criteria: list,
818
+ ) -> dict:
819
+ """Calculate an ClinGen-CGC-VICC classification based on a list of criteria.
820
+
821
+ Args:
822
+ store(scout.adapter.MongoAdapter)
823
+ institute_id(str): institute_obj['_id']
824
+ case_name(str): case_obj['display_name']
825
+ variant_id(str): variant_obj['document_id']
826
+ user_mail(str)
827
+ criteria()
828
+
829
+ Returns:
830
+ data(dict): Things for the template
831
+
832
+ """
833
+ variant_obj = store.variant(variant_id)
834
+
835
+ if not variant_obj:
836
+ return abort(404)
837
+
838
+ institute_obj, case_obj = variant_institute_and_case(
839
+ store, variant_obj, institute_id, case_name
840
+ )
841
+
842
+ user_obj = store.user(user_email)
843
+ variant_link = url_for(
844
+ "variant.variant",
845
+ institute_id=institute_id,
846
+ case_name=case_name,
847
+ variant_id=variant_id,
848
+ )
849
+ classification = store.submit_ccv_evaluation(
850
+ institute_obj=institute_obj,
851
+ case_obj=case_obj,
852
+ variant_obj=variant_obj,
853
+ user_obj=user_obj,
854
+ link=variant_link,
855
+ criteria=criteria,
856
+ )
857
+ return classification
@@ -74,7 +74,7 @@
74
74
  {{ matching_variants(managed_variant, causatives, variant.matching_ranked, variant.matching_tiered) }}
75
75
  <div class="row">
76
76
  <div class="col-sm-3">
77
- {{ panel_classify(variant, institute, case, ACMG_OPTIONS, manual_rank_options, cancer_tier_options, dismiss_variant_options, mosaic_variant_options, evaluations) }}
77
+ {{ panel_classify(variant, institute, case, ACMG_OPTIONS, CCV_OPTIONS, manual_rank_options, cancer_tier_options, dismiss_variant_options, mosaic_variant_options, evaluations, ccv_evaluations) }}
78
78
  </div>
79
79
  <div class="col-sm-4">{{ panel_summary() }}</div>
80
80
  <div class="col-sm-3">
@@ -0,0 +1,183 @@
1
+ {% extends "layout.html" %}
2
+
3
+ {% block title %}
4
+ {{ super() }} - {{ institute.display_name }} - {{ case.display_name }} - {{ variant.display_name }} - Classify
5
+ {% endblock %}
6
+
7
+ {% block top_nav %}
8
+ {{ super() }}
9
+ <li class="nav-item">
10
+ <a class="nav-link text-nowrap" href="{{ url_for('cases.index') }}">Institutes</a>
11
+ </li>
12
+ <li class="nav-item">
13
+ <a class="nav-link text-nowrap" href="{{ url_for('overview.cases', institute_id=institute._id) }}">
14
+ {{ institute.display_name }}
15
+ </a>
16
+ </li>
17
+ <li class="nav-item">
18
+ <a class="nav-link text-nowrap" href="{{ url_for('cases.case', institute_id=institute._id, case_name=case.display_name) }}">
19
+ {{ case.display_name }}
20
+ </a>
21
+ </li>
22
+ <li class="nav-item active d-flex align-items-center">
23
+ <span class="navbar-text">{{ variant.display_name }}</span>
24
+ </li>
25
+ {% endblock %}
26
+
27
+ {% block content_main %}
28
+ <div class="container mt-3 mb-5">
29
+ <form action="{{ url_for('variant.variant_ccv', institute_id=institute._id, case_name=case.display_name, variant_id=variant._id) }}" method="POST">
30
+ <div class="card panel-default mt-3">
31
+ <div class="card-body">
32
+ {% if evaluation %}
33
+ <h4>
34
+ {{ evaluation.ccv_classification.label }}
35
+ <span class="badge bg-info">{{ evaluation.ccv_classification.short|safe }}</span>
36
+ </h4>
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>
44
+ {% endif %}
45
+ <div id="conflicts_div" class="bg-warning"></div>
46
+ </div>
47
+ </div>
48
+
49
+ <div class="d-flex flex-row justify-content-between mt-3">
50
+ {% for category, criteria_group in CRITERIA.items() %}
51
+ <div class="flex-column {{ 'me-3' if not loop.last }}">
52
+ <h4>Evidence of {{ category }}</h4>
53
+ {% for evidence, criteria in criteria_group.items() %}
54
+ <ul class="list-group mt-3">
55
+ <li class="list-group-item">{{ evidence }}</li>
56
+ {% for criterion_code, criterion in criteria.items() %}
57
+ {% if evaluation and evaluation.ccv_criteria.get(criterion_code).comment %}
58
+ {% set comment = evaluation.ccv_criteria.get(criterion_code).comment %}
59
+ {% endif %}
60
+ {% if evaluation and evaluation.ccv_criteria.get(criterion_code).links %}
61
+ {% set link = evaluation.ccv_criteria.get(criterion_code).links[0] %}
62
+ {% endif %}
63
+ {% if evaluation and evaluation.ccv_criteria.get(criterion_code).modifier %}
64
+ {% set modifier = evaluation.ccv_criteria.get(criterion_code).modifier %}
65
+ {% endif %}
66
+ <div class="list-group-item">
67
+ <div class="d-flex justify-content-between">
68
+ <div data-bs-toggle="tooltip" data-bs-placement="top" title="{{ criterion.description|safe }}">
69
+ <span class="badge bg-info me-1">{{ criterion_code }}</span>
70
+ {{ criterion.short|safe}}
71
+ </div>
72
+ <div class="form-check form-switch">
73
+ <input type="checkbox" class="form-check-input" id="checkbox-{{ criterion_code }}" name="criteria" value="{{ criterion_code }}" {{ 'checked' if evaluation and criterion_code in evaluation.ccv_criteria }} {{ 'disabled' if evaluation and edit is false }}>
74
+ <label class="form-check-label" for="checkbox-{{ criterion_code }}"></label>
75
+ </div>
76
+ </div>
77
+ <div id="comment-{{ criterion_code }}" class="{{ 'collapse' if not (comment or link or modifier) }} mt-2">
78
+ {% if criterion.documentation %}<div class="row"><span class="me-1 fw-light text-wrap">{{ criterion.documentation | safe }}</span></div>{% endif %}
79
+ <div class="row">
80
+ <select {{ 'disabled' if evaluation and edit is false}} id="modifier-{{ criterion_code }}" name="modifier-{{ criterion_code }}" class="form-control form-select">
81
+ <option value="" {% if not modifier %}selected{% endif %}>Strength modifier...</option>
82
+ {% for level in "Strong", "Moderate", "Supporting" %}
83
+ {% if(level != evidence) %}
84
+ <option id="{{ criterion_code }}-{{ level }}" value="{{ level }}" {% if modifier == level %}selected{% endif %}>{{ level }}</option>
85
+ {% endif %}
86
+ {% endfor %}
87
+ </select>
88
+ </div>
89
+ <div class="row">
90
+ <textarea {{ 'disabled' if evaluation and edit is false }} class="form-control"
91
+ name="comment-{{ criterion_code }}" rows="3" placeholder="Comment (optional)">{{ comment }}</textarea>
92
+ </div>
93
+ <input type="url" {{ 'disabled' if evaluation and edit is false }} class="form-control" name="link-{{ criterion_code }}"
94
+ placeholder="{{ link or 'Supporting link (optional)' }}" value="">
95
+ </div>
96
+ </div>
97
+ {% endfor %}
98
+ </li>
99
+ </ul>
100
+ {% endfor %}
101
+ </div>
102
+ {% endfor %}
103
+ </div>
104
+ <!-- classification preview in the footer-->
105
+ <div class="mt-3 fixed-bottom bg-light border">
106
+ <div class="row">
107
+ <div class="col-3" style="font-size:1.5em">
108
+ <a href="https://doi.org/10.1016/j.gim.2022.01.001" data-bs-toggle="tooltip" title="Suggested classification based on Horak et al 2022." rel="noopener noreferrer" target="_blank"><span id="temperature_span" class="badge"></span></a>
109
+ </div>
110
+ <div class="col-6 text-center">
111
+ {% for option in CCV_OPTIONS %}
112
+ <a id="ccv-{{ option.code }}" href="https://doi.org/10.1016/j.gim.2022.01.001" class="btn ccv-preview" data-bs-toggle="tooltip" title="Suggested classification based on Horak et al 2022." rel="noopener noreferrer" target="_blank">{{ option.label }}</a>
113
+ {% endfor %}
114
+ </div>
115
+ </div>
116
+ </div>
117
+ </form>
118
+ </div>
119
+ {% endblock %}
120
+
121
+ {% block scripts %}
122
+ {{ super() }}
123
+
124
+ <script>
125
+
126
+ window.onload=function() {
127
+ update_classification();
128
+ }
129
+
130
+ $(function () {
131
+ $('[data-bs-toggle="tooltip"]').tooltip();
132
+
133
+ $('.form-switch').change(function(event) {
134
+ var el = $('#comment-' + event.target.value);
135
+ if (event.target.checked) {
136
+ el.collapse('show');
137
+ } else {
138
+ el.collapse('hide');
139
+ }
140
+ update_classification()
141
+ });
142
+
143
+ $('.form-select').change(function(event) {
144
+ update_classification()
145
+ });
146
+ });
147
+
148
+
149
+ function update_classification() {
150
+ var criteria = $(':checked').map(function(idx, elem) {
151
+ const modifiers = ["Strong", "Moderate", "Supporting"];
152
+ for (possible_modifier of modifiers) {
153
+ var modifier_option;
154
+ if(elem.value !== null && elem.value !== '') {
155
+ modifier_option = document.getElementById(elem.value + '-' + possible_modifier)
156
+ }
157
+ if(modifier_option) {
158
+ if (modifier_option.selected) {
159
+ return 'criterion=' + elem.value + '_' + possible_modifier
160
+ }
161
+ }
162
+ }
163
+ return 'criterion=' + elem.value
164
+ });
165
+
166
+
167
+ $.getJSON('/api/v1/ccv?' + criteria.toArray().join('&'), function(data) {
168
+ // reset the selection
169
+ $('.ccv-preview').removeClass('btn-primary');
170
+ // add new selection
171
+ $('#ccv-' + data.classification).addClass('btn-primary');
172
+
173
+ var temperature_span = document.getElementById("temperature_span");
174
+ temperature_span.innerHTML = 'Score ' + data.points + ' <span class="fa ' + data.temperature_icon + '"></span> ' + data.temperature + ' (' + data.point_classification + ')'
175
+ temperature_span.className = 'badge bg-' + data.temperature_class
176
+
177
+ // Update any classification conflicts
178
+ var conflicts_div = document.getElementById("conflicts_div");
179
+ conflicts_div.innerHTML = data.conflicts.join("<br>");
180
+ });
181
+ }
182
+ </script>
183
+ {% endblock %}