scout-browser 4.101.0__py3-none-any.whl → 4.103.0__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/adapter/mongo/case.py +26 -122
  2. scout/adapter/mongo/clinvar.py +98 -32
  3. scout/adapter/mongo/event.py +0 -47
  4. scout/adapter/mongo/hgnc.py +7 -2
  5. scout/adapter/mongo/omics_variant.py +8 -0
  6. scout/adapter/mongo/variant_loader.py +12 -4
  7. scout/build/variant/variant.py +1 -0
  8. scout/commands/load/variants.py +1 -1
  9. scout/commands/update/user.py +87 -49
  10. scout/constants/__init__.py +4 -0
  11. scout/constants/clinvar.py +10 -0
  12. scout/constants/igv_tracks.py +6 -2
  13. scout/constants/phenotype.py +1 -0
  14. scout/constants/variant_tags.py +18 -0
  15. scout/demo/NIST.trgt.stranger.vcf.gz +0 -0
  16. scout/demo/NIST.trgt.stranger.vcf.gz.tbi +0 -0
  17. scout/demo/__init__.py +1 -0
  18. scout/load/hpo.py +8 -2
  19. scout/models/clinvar.py +86 -0
  20. scout/parse/variant/coordinates.py +5 -1
  21. scout/parse/variant/gene.py +5 -9
  22. scout/parse/variant/genotype.py +66 -42
  23. scout/parse/variant/variant.py +2 -0
  24. scout/server/app.py +71 -2
  25. scout/server/blueprints/alignviewers/controllers.py +8 -6
  26. scout/server/blueprints/alignviewers/templates/alignviewers/igv_viewer.html +4 -0
  27. scout/server/blueprints/alignviewers/templates/alignviewers/utils.html +1 -1
  28. scout/server/blueprints/cases/controllers.py +57 -29
  29. scout/server/blueprints/cases/templates/cases/case_report.html +28 -90
  30. scout/server/blueprints/cases/templates/cases/matchmaker.html +1 -1
  31. scout/server/blueprints/cases/templates/cases/phenotype.html +1 -1
  32. scout/server/blueprints/cases/templates/cases/utils.html +34 -53
  33. scout/server/blueprints/cases/views.py +32 -33
  34. scout/server/blueprints/clinvar/controllers.py +235 -54
  35. scout/server/blueprints/clinvar/form.py +38 -1
  36. scout/server/blueprints/clinvar/static/form_style.css +8 -1
  37. scout/server/blueprints/clinvar/templates/clinvar/clinvar_onc_submissions.html +200 -0
  38. scout/server/blueprints/clinvar/templates/clinvar/clinvar_submissions.html +3 -2
  39. scout/server/blueprints/clinvar/templates/clinvar/components.html +198 -0
  40. scout/server/blueprints/clinvar/templates/clinvar/multistep_add_onc_variant.html +187 -0
  41. scout/server/blueprints/clinvar/templates/clinvar/multistep_add_variant.html +9 -348
  42. scout/server/blueprints/clinvar/templates/clinvar/scripts.html +193 -0
  43. scout/server/blueprints/clinvar/views.py +90 -13
  44. scout/server/blueprints/diagnoses/controllers.py +4 -8
  45. scout/server/blueprints/diagnoses/templates/diagnoses/diagnoses.html +1 -1
  46. scout/server/blueprints/diagnoses/templates/diagnoses/disease_term.html +1 -1
  47. scout/server/blueprints/diagnoses/views.py +2 -2
  48. scout/server/blueprints/institutes/controllers.py +148 -75
  49. scout/server/blueprints/institutes/forms.py +1 -0
  50. scout/server/blueprints/institutes/templates/overview/cases.html +1 -1
  51. scout/server/blueprints/institutes/templates/overview/gene_variants.html +15 -6
  52. scout/server/blueprints/institutes/templates/overview/institute_sidebar.html +28 -2
  53. scout/server/blueprints/institutes/templates/overview/utils.html +1 -1
  54. scout/server/blueprints/institutes/views.py +17 -4
  55. scout/server/blueprints/login/controllers.py +2 -1
  56. scout/server/blueprints/login/views.py +5 -2
  57. scout/server/blueprints/mme/templates/mme/mme_submissions.html +2 -2
  58. scout/server/blueprints/omics_variants/templates/omics_variants/outliers.html +2 -2
  59. scout/server/blueprints/omics_variants/views.py +2 -2
  60. scout/server/blueprints/phenotypes/controllers.py +15 -2
  61. scout/server/blueprints/phenotypes/templates/phenotypes/hpo_terms.html +1 -1
  62. scout/server/blueprints/variant/controllers.py +11 -12
  63. scout/server/blueprints/variant/templates/variant/cancer-variant.html +2 -1
  64. scout/server/blueprints/variant/templates/variant/components.html +0 -1
  65. scout/server/blueprints/variant/templates/variant/sv-variant.html +2 -1
  66. scout/server/blueprints/variant/templates/variant/utils.html +1 -1
  67. scout/server/blueprints/variant/templates/variant/variant.html +2 -2
  68. scout/server/blueprints/variant/templates/variant/variant_details.html +100 -84
  69. scout/server/blueprints/variant/utils.py +25 -0
  70. scout/server/blueprints/variants/controllers.py +11 -42
  71. scout/server/blueprints/variants/templates/variants/cancer-variants.html +5 -3
  72. scout/server/blueprints/variants/templates/variants/str-variants.html +4 -1
  73. scout/server/blueprints/variants/templates/variants/sv-variants.html +3 -3
  74. scout/server/blueprints/variants/templates/variants/utils.html +4 -0
  75. scout/server/blueprints/variants/templates/variants/variants.html +4 -4
  76. scout/server/blueprints/variants/views.py +9 -8
  77. scout/server/config.py +3 -0
  78. scout/server/extensions/beacon_extension.py +7 -2
  79. scout/server/extensions/clinvar_extension.py +2 -2
  80. scout/server/templates/bootstrap_global.html +11 -1
  81. scout/server/templates/layout.html +6 -1
  82. scout/server/utils.py +24 -3
  83. {scout_browser-4.101.0.dist-info → scout_browser-4.103.0.dist-info}/METADATA +1 -1
  84. {scout_browser-4.101.0.dist-info → scout_browser-4.103.0.dist-info}/RECORD +87 -81
  85. {scout_browser-4.101.0.dist-info → scout_browser-4.103.0.dist-info}/WHEEL +0 -0
  86. {scout_browser-4.101.0.dist-info → scout_browser-4.103.0.dist-info}/entry_points.txt +0 -0
  87. {scout_browser-4.101.0.dist-info → scout_browser-4.103.0.dist-info}/licenses/LICENSE +0 -0
@@ -2,6 +2,8 @@
2
2
  {% from "utils.html" import comments_table, variant_related_comments_table %}
3
3
  {% from "variant/gene_disease_relations.html" import inheritance_badge %}
4
4
  {% from "variants/components.html" import fusion_variants_header, default_fusion_variant_cells %}
5
+ {% from "variant/variant_details.html" import frequencies_table, old_observations_table %}
6
+ {% from "variant/components.html" import clinsig_table %}
5
7
 
6
8
  {% extends "report_base.html" %}
7
9
 
@@ -192,7 +194,7 @@
192
194
  <ul>
193
195
  {% for pheno in case.phenotype_terms %}
194
196
  <li>
195
- {{pheno.feature}} - (<a style="text-decoration:none;" href="https://hpo.jax.org/app/browse/term/{{pheno.phenotype_id}}" target="_blank">{{pheno.phenotype_id}})</a>
197
+ {{pheno.feature}} - (<a style="text-decoration:none;" href="{{ hpo_link_url }}{{pheno.phenotype_id}}" target="_blank">{{pheno.phenotype_id}})</a>
196
198
  {% for feature_ind in pheno.individuals %} <!-- display eventual individual-level HPO terms -->
197
199
  {% for case_ind in case.individuals %}
198
200
  {% if feature_ind.individual_name == case_ind.display_name %}
@@ -588,78 +590,14 @@
588
590
  <table class="table table-sm">
589
591
  <tr>
590
592
  <td style="width:60%;">
591
- {{ genotype_table(variant) }}
593
+ {{ genotype_table(variant) }}<br>
594
+ {{ clinsig_table(variant) }}
592
595
  </td>
593
596
  <td style="width:3%"></td>
594
597
  <td>
595
- <table id="panel-table" class="table table-sm" style="background-color: transparent">
596
- <thead>
597
- <tr>
598
- <th>Pop. frequency
599
- {% if variant.frequency == 'common' %}
600
- <span class="badge bg-danger">{{variant.frequency}}</span>
601
- {% elif variant.frequency == 'uncommon' %}
602
- <span class="badge bg-warning text-dark">{{variant.frequency}}</span>
603
- {% else %}
604
- <span class="badge bg-success">{{variant.frequency}}</span>
605
- {% endif %}
606
- </th>
607
- <th>
608
- </th>
609
- </tr>
610
- </thead>
611
- <tbody>
612
- <tr>
613
- <td>
614
- {% if variant.dbsnp_id %}
615
- <a style="text-decoration:none;" href="{{ variant.thousandg_link }}" rel="noopener" target="_blank">1000G</a>
616
- {% else %}
617
- 1000G
618
- {% endif %}
619
- </td>
620
- <td>
621
- {% if variant.max_thousand_genomes_frequency %}
622
- {{ variant.max_thousand_genomes_frequency|human_decimal }} <small>(max)</small> |
623
- {% endif %}
624
- {{ variant.thousand_genomes_frequency|human_decimal if variant.thousand_genomes_frequency }}
625
- {% if not variant.max_thousand_genomes_frequency and not variant.thousand_genomes_frequency %}
626
- <span class="text-muted">Not annotated</span>
627
- {% endif %}
628
- </td>
629
- </tr>
630
- <tr>
631
- <td><a style="text-decoration:none;" title="Exome Aggregation Consortium" rel="noopener" target="_blank" href="{{ variant.exac_link }}">ExAC</a></td>
632
- <td>
633
- {% if variant.max_exac_frequency %}
634
- {{ variant.max_exac_frequency|human_decimal }} <small>(max)</small> |
635
- {% endif %}
636
- {{ variant.exac_frequency|human_decimal if variant.exac_frequency }}
637
- {% if not variant.max_exac_frequency and not variant.exac_frequency %}
638
- <span class="text-muted">Not annotated</span>
639
- {% endif %}
640
- </td>
641
- </tr>
642
- <tr>
643
- <td><a style="text-decoration:none;" title="genome Aggregation Database" rel="noopener" target="_blank" href="{{ variant.gnomad_link }}">gnomAD</a></td>
644
- <td>
645
- {% if 'gnomad_frequency' in variant%}
646
- {% if variant.max_gnomad_frequency %}
647
- {{ variant.max_gnomad_frequency|human_decimal }}
648
- <small>(max)</small> |
649
- {% endif %}
650
- {{ variant.gnomad_frequency|human_decimal if variant.gnomad_frequency }}
651
- {% else %}
652
- <span class="text-muted">Not annotated</span>
653
- {% endif %}
654
- </td>
655
- <td>
656
- </td>
657
- </tr>
658
- </tbody>
659
- </table>
598
+ {{ frequencies_table(variant) }}
599
+ {{ old_observations_table(variant) }}
660
600
  </td>
661
- </tr>
662
- </table>
663
601
  <table id="panel-table" class="table table-sm" style="background-color: transparent">
664
602
  <thead>
665
603
  <tr>
@@ -762,11 +700,28 @@
762
700
  <tbody>
763
701
  <tr>
764
702
  <td>{{ variant.sift_predictions|join(', ') or '-'}}</td>
765
- <td>{{ variant.revel or '-' }}</td>
703
+ <td>
704
+ {% if variant.revel %}
705
+ <span class="badge bg-{{variant.revel|get_label_or_color_by_score('revel', 'color')}}">
706
+ {{ variant.revel }}
707
+ </span>
708
+ {% else %}
709
+ <span class="badge bg-secondary">–</span>
710
+ {% endif %}
711
+ </td>
766
712
  <td>{{ variant.revel_score or '-' }}</td>
767
713
  <td>{{ variant.polyphen_predictions|join(', ') or '-' }}</td>
768
714
  <td>{{ variant.spidex|spidex_human if variant.spidex else none|spidex_human }}</td>
769
- <td>{{ variant.spliceai_scores|join(', ') or '-' }}</td>
715
+ <td>
716
+ {% set spliceai_highest = variant.spliceai_scores | spliceai_max %}
717
+ {% if spliceai_highest %}
718
+ <span class="badge bg-{{ spliceai_highest | get_label_or_color_by_score('spliceai', 'color') }}">
719
+ {{ variant.spliceai_scores|join(', ') }}
720
+ </span>
721
+ {% else %}
722
+ <span class="badge bg-secondary">–</span>
723
+ {% endif %}
724
+ </td>
770
725
  </tr>
771
726
  </tbody>
772
727
  </table>
@@ -1074,25 +1029,8 @@
1074
1029
  </td>
1075
1030
  <td style="width:3%"></td>
1076
1031
  <td>
1077
- <table id="panel-table" class="table table-sm" style="background-color: transparent">
1078
- <thead>
1079
- <th colspan=2>Pop. frequency</th>
1080
- </thead>
1081
- <tbody>
1082
- {% for freq_name, value, link in variant.frequencies %}
1083
- <tr>
1084
- <td>{{ freq_name }}</td>
1085
- <td>
1086
- {% if value %}
1087
- <span class="badge bg-secondary">{{ value|human_decimal }}</span>
1088
- {% else %}
1089
- <span class="text-muted">Not annotated</span>
1090
- {% endif %}
1091
- </td>
1092
- </tr>
1093
- {% endfor %}
1094
- </tbody>
1095
- </table>
1032
+ {{ frequencies_table(variant) }}
1033
+ {{ old_observations_table(variant) }}
1096
1034
  </td>
1097
1035
  </tr>
1098
1036
  </table>
@@ -85,7 +85,7 @@
85
85
  <ul class="list-group list-group-flush">
86
86
  {% for hpo in case.mme_submission.features %}
87
87
  <li class="list-group-item">
88
- <a class="text-white" target="_blank" href="http://hpo.jax.org/app/browse/term/{{hpo.id}}">
88
+ <a class="text-white" target="_blank" href="{{hpo_link_url}}{{hpo.id}}">
89
89
  <span class="badge bg-sm bg-info">{{hpo.id}}</span>
90
90
  </a>{{hpo.label}}
91
91
  </li>
@@ -191,7 +191,7 @@
191
191
  {% endmacro %}
192
192
 
193
193
  {% macro hpo_panel(case, institute, config) %}
194
- {% set url = 'https://hpo.jax.org/app/' %}
194
+ {% set url = 'https://hpo.jax.org/' %}
195
195
  <div id="phenotypes_panel" class="panel-heading">
196
196
  <h6 class="mt-3"><span class="fa fa-stethoscope"></span>&nbsp;Phenotype terms (<a target="_blank" class="" href="{{ url }}" rel="noopener">HPO</a>)</h6>
197
197
  </div>
@@ -293,8 +293,18 @@
293
293
 
294
294
  {% if variant.category %}
295
295
  {% if variant.category in "str" %}
296
- {{ variant.str_repid }} {{ variant.alternative | replace("<", " ") | replace(">", "")}}
297
-
296
+ {% if variant.str_repid %}
297
+ {{ variant.str_repid }}
298
+ {% elif variant.str_trid %}
299
+ {{ variant.str_trid }}
300
+ {% else %}
301
+ {% for gene in variant.genes %} {{ gene.symbol }} {% endfor %}
302
+ {% endif %}
303
+ {% if variant.str_mc %}
304
+ STR{{ variant.str_mc }}
305
+ {% else %}
306
+ {{ variant.alternative|truncate(20,True) }}
307
+ {% endif %}
298
308
  {% elif variant.category in ("snv", "cancer") %}
299
309
  {% set display_genes = [] %}
300
310
  {% for gene in variant.genes %}
@@ -339,18 +349,24 @@
339
349
  {% macro pretty_link_variant(variant, case) %}
340
350
  {# Returns human readable links to the corresponding variant page #}
341
351
 
342
- {% if variant.category in ("mei", "str", "snv", "cancer") %}
343
- {% if variant.category == "cancer" %}
344
- <a href='{{ url_for('variant.cancer_variant',
352
+ {% if variant.category in ("mei", "snv") %}
353
+ <a href='{{ url_for('variant.variant',
345
354
  institute_id=variant.institute,
346
355
  case_name=case.display_name,
347
356
  variant_id=variant._id) }}' target='_blank'>
348
- {% else %}
357
+ {% elif variant.category == "str" %}
349
358
  <a href='{{ url_for('variant.variant',
359
+ institute_id=variant.institute,
360
+ case_name=case.display_name,
361
+ variant_id=variant._id) }}'
362
+ data-bs-toggle='tooltip'
363
+ title='{{ variant.alternative | replace("<", " ") | replace(">", "")}}'
364
+ target='_blank'>
365
+ {% elif variant.category == "cancer" %}
366
+ <a href='{{ url_for('variant.cancer_variant',
350
367
  institute_id=variant.institute,
351
368
  case_name=case.display_name,
352
369
  variant_id=variant._id) }}' target='_blank'>
353
- {% endif %}
354
370
  {% elif variant.category == "outlier" %}
355
371
  <a href='{{ url_for('omics_variants.outliers',
356
372
  institute_id=variant.institute, case_name=case.display_name) }}' target='_blank'>
@@ -360,7 +376,7 @@
360
376
  case_name=case.display_name,
361
377
  variant_id=variant._id) }}' target='_blank'>
362
378
  {% endif %}
363
- {{ pretty_variant(variant) }}
379
+ {{ pretty_variant(variant) }}
364
380
  </a>
365
381
  {% endmacro %}
366
382
 
@@ -442,21 +458,7 @@
442
458
  <div class="row">
443
459
  <div class="col">
444
460
  <i class="far fa-check-circle"></i>
445
- {% if variant.category == "snv" %}
446
- <a href="{{ url_for('variant.variant',
447
- institute_id=variant.institute,
448
- case_name=case.display_name,
449
- variant_id=variant._id) }}">
450
- {{ variant.hgnc_symbols|join(', ') }} (partial causative)
451
- {% else %}
452
- <a href="{{ url_for('variant.sv_variant',
453
- institute_id=variant.institute,
454
- case_name=case.display_name,
455
- variant_id=variant._id) }}">
456
- {{ variant.sub_category|upper }}({{ variant.chromosome }}{{ variant.cytoband_start }}-{{ variant.chromosome }}{{ variant.cytoband_end }})
457
- (partial causative)
458
- {% endif %}
459
- </a>
461
+ {{ pretty_link_variant(variant, case) }} (partial causative)
460
462
  </div>
461
463
  <div class="col-2" style="width:fit-content;">
462
464
  {{ remove_form(url_for('cases.mark_causative',
@@ -495,7 +497,7 @@
495
497
  </div>
496
498
  <div class="col-8">
497
499
  {% for hpo in variant_phenotypes.hpo_terms %}
498
- <a target="_blank" href="http://hpo.jax.org/app/browse/term/{{hpo.phenotype_id}}">
500
+ <a target="_blank" href="https://hpo.jax.org/browse/term/{{hpo.phenotype_id}}">
499
501
  <span class="badge badge-sm bg-info">{{hpo.phenotype_id}}</span>
500
502
  </a>
501
503
  {% else %}
@@ -615,42 +617,21 @@
615
617
  </li>
616
618
  {% endfor %}
617
619
  </ul>
618
- {{ clinvar_vars_summary(suspects, case, institute) }}
619
620
  </div>
620
621
  </div>
621
622
  {% endmacro %}
622
623
 
623
624
  {% macro clinvar_var_status(variant, case, institute) %}
624
- {% if variant._id and variant.category != 'str' %}
625
+ {% if case.clinvar_variants and variant._id in case.clinvar_variants.keys() %}
626
+ (included in a ClinVar submission)
627
+ {% elif variant._id and variant.category in ["snv", "sv"] %}
625
628
  <form id="clinvar_submit" class="d-flex justify-content-center" action="{{ url_for('clinvar.clinvar_add_variant', institute_id=institute._id, case_name=case.display_name) }}" method="POST">
626
- {% if case.clinvar_variants and variant._id in case.clinvar_variants.keys() %}
627
- (included in ClinVar submission)
628
- {% elif variant.category in ('cancer', 'cancer_sv') %}
629
- <button data-bs-toggle='tooltip' data-bs-placement="bottom" title="Submission of somatic variants from Scout is temporarily suspended. We are working to harmonize submissions with changes introduced by ClinVar for this variant category." disabled >Add to ClinVar submission</button>
630
- {% elif variant.category in ('snv', 'sv') %}
631
- <button type="submit" name="var_id" value="{{variant._id}}" class="btn btn-secondary btn-sm" style="float: right;">Add to ClinVar submission</button>
632
- {% endif %}
629
+ <button type="submit" name="var_id" value="{{variant._id}}" class="btn btn-secondary btn-sm" style="float: right;">Add to ClinVar submission</button>
630
+ </form>
631
+ {% elif variant.category in ('cancer') %}
632
+ <form id="clinvar_onc_submit" class="d-flex justify-content-center" action="{{ url_for('clinvar.clinvar_add_onc_variant', institute_id=institute._id, case_name=case.display_name) }}" method="POST">
633
+ <button type="submit" name="var_id" value="{{variant._id}}" class="btn btn-secondary btn-sm" style="float: right;">Add to ClinVar submission</button>
633
634
  </form>
634
- {% endif %}
635
- {% endmacro %}
636
-
637
- {% macro clinvar_vars_summary(suspects, case, institute) %}
638
- {% if case.clinvar_variants %}
639
- {% if case.clinvar_variants_not_in_suspects | length >0 %}
640
- <div class="d-flex align-content-center flex-wrap">
641
- <div class="col-auto d-flex align-items-center wrap">
642
- <h6 class="m-2">&nbsp;Variants present in a ClinVar submission, no longer pinned:</h6>
643
- </div>
644
- {% for entry in case.clinvar_variants_not_in_suspects %}
645
- <div class="m-2">{{ pretty_link_variant(entry, case) }}</div>
646
- {% endfor %}
647
- </div>
648
- {% endif %}
649
- <div class="card-footer d-flex justify-content-center pb-0" style="background-color:inherit;">
650
- <a href="{{url_for('clinvar.clinvar_submissions', institute_id=institute._id)}}"
651
- class="btn btn-secondary btn-sm text-white mx-3" target="_blank" rel="noopener noreferrer">View ClinVar
652
- submissions <i class="fas fa-arrow-circle-right"></i></a>
653
- </div>
654
635
  {% endif %}
655
636
  {% endmacro %}
656
637
 
@@ -36,6 +36,7 @@ from scout.server.utils import (
36
36
  html_to_pdf_file,
37
37
  institute_and_case,
38
38
  jsonconverter,
39
+ safe_redirect_back,
39
40
  templated,
40
41
  user_cases,
41
42
  user_institutes,
@@ -148,7 +149,7 @@ def beacon_add_variants(institute_id, case_name):
148
149
  store, institute_id, case_name
149
150
  ) # This function checks if user has permissions to access the case
150
151
  beacon.add_variants(store, case_obj, request.form)
151
- return redirect(request.referrer)
152
+ return safe_redirect_back(request)
152
153
 
153
154
 
154
155
  @cases_bp.route("/beacon_remove_variants/<institute_id>/<case_name>", methods=["GET"])
@@ -158,7 +159,7 @@ def beacon_remove_variants(institute_id, case_name):
158
159
  store, institute_id, case_name
159
160
  ) # This function checks if user has permissions to access the case
160
161
  beacon.remove_variants(store, institute_id, case_obj)
161
- return redirect(request.referrer)
162
+ return safe_redirect_back(request)
162
163
 
163
164
 
164
165
  @cases_bp.route("/<institute_id>/<case_name>/mme_matches", methods=["GET", "POST"])
@@ -175,7 +176,7 @@ def matchmaker_match(institute_id, case_name, target):
175
176
  """Starts an internal match or a match against one or all MME external nodes"""
176
177
  institute_obj, case_obj = institute_and_case(store, institute_id, case_name)
177
178
  match_results = controllers.matchmaker_match(request, target, institute_id, case_name)
178
- return redirect(request.referrer)
179
+ return safe_redirect_back(request)
179
180
 
180
181
 
181
182
  @cases_bp.route("/<institute_id>/<case_name>/mme_add", methods=["POST"])
@@ -183,7 +184,7 @@ def matchmaker_add(institute_id, case_name):
183
184
  """Add or update a case in MatchMaker"""
184
185
  # Call matchmaker_delete controller to add a patient to MatchMaker
185
186
  controllers.matchmaker_add(request, institute_id, case_name)
186
- return redirect(request.referrer)
187
+ return safe_redirect_back(request)
187
188
 
188
189
 
189
190
  @cases_bp.route("/<institute_id>/<case_name>/mme_delete", methods=["POST"])
@@ -191,7 +192,7 @@ def matchmaker_delete(institute_id, case_name):
191
192
  """Remove a case from MatchMaker"""
192
193
  # Call matchmaker_delete controller to delete a patient from MatchMaker
193
194
  controllers.matchmaker_delete(request, institute_id, case_name)
194
- return redirect(request.referrer)
195
+ return safe_redirect_back(request)
195
196
 
196
197
 
197
198
  @cases_bp.route("/<institute_id>/<case_name>/individuals", methods=["POST"])
@@ -212,7 +213,7 @@ def update_individual(institute_id, case_name):
212
213
  age=age,
213
214
  tissue=tissue,
214
215
  )
215
- return redirect(request.referrer)
216
+ return safe_redirect_back(request)
216
217
 
217
218
 
218
219
  @cases_bp.route("/<institute_id>/<case_name>/samples", methods=["POST"])
@@ -237,7 +238,7 @@ def update_cancer_sample(institute_id, case_name):
237
238
  tumor_type=tumor_type,
238
239
  tumor_purity=tumor_purity,
239
240
  )
240
- return redirect(request.referrer)
241
+ return safe_redirect_back(request)
241
242
 
242
243
 
243
244
  @cases_bp.route("/<institute_id>/<case_name>/synopsis", methods=["POST"])
@@ -247,7 +248,7 @@ def case_synopsis(institute_id, case_name):
247
248
  user_obj = store.user(current_user.email)
248
249
  new_synopsis = request.form.get("synopsis")
249
250
  controllers.update_synopsis(store, institute_obj, case_obj, user_obj, new_synopsis)
250
- return redirect(request.referrer)
251
+ return safe_redirect_back(request)
251
252
 
252
253
 
253
254
  @cases_bp.route("/api/v1/<institute_id>/<case_name>/case_report", methods=["GET"])
@@ -344,7 +345,7 @@ def mt_report(institute_id, case_name):
344
345
  shutil.rmtree(temp_excel_dir)
345
346
 
346
347
  flash("No MT report excel file could be exported for this sample", "warning")
347
- return redirect(request.referrer)
348
+ return safe_redirect_back(request)
348
349
 
349
350
 
350
351
  @cases_bp.route("/<institute_id>/<case_name>/diagnose", methods=["POST"])
@@ -366,7 +367,7 @@ def case_diagnosis(institute_id, case_name):
366
367
  affected_inds=affected_inds,
367
368
  remove=True if request.args.get("remove") == "yes" else False,
368
369
  )
369
- return redirect("#".join([link, "disease_assign"]))
370
+ return safe_redirect_back(request, "#".join([link, "disease_assign"]))
370
371
 
371
372
 
372
373
  @cases_bp.route("/<institute_id>/<case_name>/phenotypes", methods=["POST"])
@@ -414,7 +415,7 @@ def phenotypes(institute_id, case_name, phenotype_id=None):
414
415
  )
415
416
  return redirect(case_url)
416
417
 
417
- return redirect("#".join([case_url, "phenotypes_panel"]))
418
+ return safe_redirect_back(request, "#".join([case_url, "phenotypes_panel"]))
418
419
 
419
420
 
420
421
  @cases_bp.route("/<institute_id>/<case_name>/phenotype_export", methods=["POST"])
@@ -526,7 +527,7 @@ def phenotypes_actions(institute_id, case_name):
526
527
  hgnc_ids = [result[0] for result in results if result[1] >= hpo_count]
527
528
  store.update_dynamic_gene_list(case_obj, hgnc_ids=hgnc_ids, phenotype_ids=hpo_ids)
528
529
 
529
- return redirect("#".join([case_url, "phenotypes_panel"]))
530
+ return safe_redirect_back(request, "#".join([case_url, "phenotypes_panel"]))
530
531
 
531
532
 
532
533
  @cases_bp.route("/<institute_id>/<case_name>/events", methods=["POST"])
@@ -567,7 +568,7 @@ def events(institute_id, case_name, event_id=None):
567
568
  # create a case comment
568
569
  store.comment(institute_obj, case_obj, user_obj, link, content=content)
569
570
 
570
- return redirect(request.referrer)
571
+ return safe_redirect_back(request)
571
572
 
572
573
 
573
574
  @cases_bp.route("/<institute_id>/<case_name>/status", methods=["POST"])
@@ -588,7 +589,7 @@ def status(institute_id, case_name):
588
589
  if tags or case_obj.get("tags") and tags != case_obj.get("tags"):
589
590
  store.tag_case(institute_obj, case_obj, user_obj, tags, link)
590
591
 
591
- return redirect(request.referrer)
592
+ return safe_redirect_back(request)
592
593
 
593
594
 
594
595
  @cases_bp.route("/<institute_id>/<case_name>/assign", methods=["POST"])
@@ -605,7 +606,7 @@ def assign(institute_id, case_name, user_id=None, inactivate=False):
605
606
  store.unassign(institute_obj, case_obj, user_obj, link, inactivate)
606
607
  else:
607
608
  store.assign(institute_obj, case_obj, user_obj, link)
608
- return redirect(request.referrer)
609
+ return safe_redirect_back(request)
609
610
 
610
611
 
611
612
  @cases_bp.route("/api/v1/cases", defaults={"institute_id": None})
@@ -688,7 +689,7 @@ def pin_variant(institute_id, case_name, variant_id):
688
689
  store.pin_variant(institute_obj, case_obj, user_obj, link, variant_obj)
689
690
  elif request.form["action"] == "DELETE":
690
691
  store.unpin_variant(institute_obj, case_obj, user_obj, link, variant_obj)
691
- return redirect(request.referrer or link)
692
+ return safe_redirect_back(request, request.referrer or link)
692
693
 
693
694
 
694
695
  @cases_bp.route("/<institute_id>/<case_name>/<variant_id>/validate", methods=["POST"])
@@ -705,7 +706,7 @@ def mark_validation(institute_id, case_name, variant_id):
705
706
  variant_id=variant_id,
706
707
  )
707
708
  store.validate(institute_obj, case_obj, user_obj, link, variant_obj, validate_type)
708
- return redirect(request.referrer or link)
709
+ return safe_redirect_back(request, request.referrer or link)
709
710
 
710
711
 
711
712
  @cases_bp.route(
@@ -748,8 +749,7 @@ def mark_causative(institute_id, case_name, variant_id, partial_causative=False)
748
749
  store.unmark_causative(institute_obj, case_obj, user_obj, link, variant_obj)
749
750
 
750
751
  # send the user back to the case that was marked as solved
751
- case_url = url_for(".case", institute_id=institute_id, case_name=case_name)
752
- return redirect(request.referrer)
752
+ return safe_redirect_back(request)
753
753
 
754
754
 
755
755
  @cases_bp.route("/<institute_id>/<case_name>/check-case", methods=["POST"])
@@ -761,7 +761,7 @@ def check_case(institute_id, case_name):
761
761
  store.case_collection.find_one_and_update(
762
762
  {"_id": case_obj["_id"]}, {"$set": {"needs_check": False}}
763
763
  )
764
- return redirect(request.referrer)
764
+ return safe_redirect_back(request)
765
765
 
766
766
 
767
767
  @cases_bp.route("/<institute_id>/<case_name>/report/<report_type>")
@@ -835,7 +835,7 @@ def share(institute_id, case_name):
835
835
  except ValueError as ex:
836
836
  flash(str(ex), "warning")
837
837
 
838
- return redirect(request.referrer)
838
+ return safe_redirect_back(request)
839
839
 
840
840
 
841
841
  @cases_bp.route("/<institute_id>/<case_name>/update_rerun_status")
@@ -846,7 +846,7 @@ def update_rerun_status(institute_id, case_name):
846
846
  link = url_for("cases.case", institute_id=institute_id, case_name=case_name)
847
847
 
848
848
  store.update_rerun_status(institute_obj, case_obj, user_obj, link)
849
- return redirect(link)
849
+ return safe_redirect_back(request, link)
850
850
 
851
851
 
852
852
  @cases_bp.route("/<institute_id>/<case_name>/monitor", methods=["POST"])
@@ -860,7 +860,7 @@ def rerun_monitor(institute_id, case_name):
860
860
  else:
861
861
  store.unmonitor(institute_obj, case_obj, user_obj, link)
862
862
 
863
- return redirect(request.referrer)
863
+ return safe_redirect_back(request)
864
864
 
865
865
 
866
866
  @cases_bp.route("/<institute_id>/<case_name>/reanalysis", methods=["POST"])
@@ -876,7 +876,7 @@ def reanalysis(institute_id, case_name):
876
876
  LOG.error(msg)
877
877
  flash(msg, "danger")
878
878
 
879
- return redirect(request.referrer)
879
+ return safe_redirect_back(request)
880
880
 
881
881
 
882
882
  @cases_bp.route("/<institute_id>/<case_name>/research", methods=["POST"])
@@ -886,7 +886,7 @@ def research(institute_id, case_name):
886
886
  user_obj = store.user(current_user.email)
887
887
  link = url_for(".case", institute_id=institute_id, case_name=case_name)
888
888
  store.open_research(institute_obj, case_obj, user_obj, link)
889
- return redirect(request.referrer)
889
+ return safe_redirect_back(request)
890
890
 
891
891
 
892
892
  @cases_bp.route("/<institute_id>/<case_name>/reset_research", methods=["GET"])
@@ -895,7 +895,7 @@ def reset_research(institute_id, case_name):
895
895
  user_obj = store.user(current_user.email)
896
896
  link = url_for(".case", institute_id=institute_id, case_name=case_name)
897
897
  store.reset_research(institute_obj, case_obj, user_obj, link)
898
- return redirect(request.referrer)
898
+ return safe_redirect_back(request)
899
899
 
900
900
 
901
901
  @cases_bp.route("/<institute_id>/<case_name>/cohorts", methods=["POST"])
@@ -909,7 +909,7 @@ def cohorts(institute_id, case_name):
909
909
  store.remove_cohort(institute_obj, case_obj, user_obj, link, cohort_tag)
910
910
  else:
911
911
  store.add_cohort(institute_obj, case_obj, user_obj, link, cohort_tag)
912
- return redirect("#".join([request.referrer, "cohorts"]))
912
+ return safe_redirect_back(request, "#".join([request.referrer, "cohorts"]))
913
913
 
914
914
 
915
915
  @cases_bp.route("/<institute_id>/<case_name>/default-panels", methods=["POST"])
@@ -917,7 +917,7 @@ def default_panels(institute_id, case_name):
917
917
  """Update default panels for a case."""
918
918
  panel_ids = request.form.getlist("panel_ids")
919
919
  controllers.update_default_panels(store, current_user, institute_id, case_name, panel_ids)
920
- return redirect(request.referrer)
920
+ return safe_redirect_back(request)
921
921
 
922
922
 
923
923
  @cases_bp.route("/<institute_id>/<case_name>/update-clinical-filter-hpo", methods=["POST"])
@@ -928,7 +928,7 @@ def update_clinical_filter_hpo(institute_id, case_name):
928
928
  controllers.update_clinical_filter_hpo(
929
929
  store, current_user, institute_obj, case_obj, hpo_clinical_filter
930
930
  )
931
- return redirect(request.referrer)
931
+ return safe_redirect_back(request)
932
932
 
933
933
 
934
934
  @cases_bp.route("/<institute_id>/<case_name>/add_case_group", methods=["GET", "POST"])
@@ -943,8 +943,7 @@ def add_case_group(institute_id, case_name):
943
943
  case_name = request.form.get("other_case_name")
944
944
 
945
945
  controllers.add_case_group(store, current_user, institute_id, case_name, group_id)
946
-
947
- return redirect(request.referrer + "#case_groups")
946
+ return safe_redirect_back(request, request.referrer + "#case_groups")
948
947
 
949
948
 
950
949
  @cases_bp.route("/<institute_id>/<case_name>/<case_group>/remove_case_group", methods=["GET"])
@@ -952,7 +951,7 @@ def remove_case_group(institute_id, case_name, case_group):
952
951
  """Unbind a case group from a case. Remove the group if it is no longer in use."""
953
952
  controllers.remove_case_group(store, current_user, institute_id, case_name, case_group)
954
953
 
955
- return redirect(request.referrer + "#case_groups")
954
+ return safe_redirect_back(request, request.referrer + "#case_groups")
956
955
 
957
956
 
958
957
  @cases_bp.route("/<case_group>/case_group_update_label", methods=["POST"])
@@ -962,7 +961,7 @@ def case_group_update_label(case_group):
962
961
 
963
962
  controllers.case_group_update_label(store, case_group, label)
964
963
 
965
- return redirect(request.referrer + "#case_groups")
964
+ return safe_redirect_back(request, request.referrer + "#case_groups")
966
965
 
967
966
 
968
967
  @cases_bp.route("/<institute_id>/<case_name>/download-hpo-genes/<category>", methods=["GET"])