scout-browser 4.79.1__py3-none-any.whl → 4.81__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 (46) hide show
  1. scout/__version__.py +1 -1
  2. scout/adapter/mongo/variant.py +2 -2
  3. scout/constants/__init__.py +3 -0
  4. scout/constants/clinvar.py +3 -0
  5. scout/constants/indexes.py +14 -12
  6. scout/constants/variants_export.py +11 -0
  7. scout/models/case/case_loading_models.py +45 -23
  8. scout/models/clinvar.py +1 -0
  9. scout/parse/variant/variant.py +27 -23
  10. scout/server/blueprints/api/views.py +12 -0
  11. scout/server/blueprints/cases/controllers.py +72 -6
  12. scout/server/blueprints/cases/templates/cases/case.html +1 -22
  13. scout/server/blueprints/cases/templates/cases/case_bionano.html +2 -0
  14. scout/server/blueprints/cases/templates/cases/case_report.html +135 -129
  15. scout/server/blueprints/cases/templates/cases/case_sma.html +2 -0
  16. scout/server/blueprints/cases/templates/cases/collapsible_actionbar.html +12 -2
  17. scout/server/blueprints/cases/templates/cases/gene_panel.html +45 -22
  18. scout/server/blueprints/cases/templates/cases/utils.html +52 -14
  19. scout/server/blueprints/cases/views.py +22 -0
  20. scout/server/blueprints/clinvar/controllers.py +2 -0
  21. scout/server/blueprints/clinvar/form.py +5 -0
  22. scout/server/blueprints/clinvar/templates/clinvar/multistep_add_variant.html +30 -5
  23. scout/server/blueprints/diagnoses/templates/diagnoses/diagnoses.html +1 -1
  24. scout/server/blueprints/institutes/static/select2_darktheme.css +62 -0
  25. scout/server/blueprints/institutes/templates/overview/institute_settings.html +1 -1
  26. scout/server/blueprints/panels/templates/panels/panel.html +15 -1
  27. scout/server/blueprints/panels/templates/panels/panel_pdf_case_hits.html +2 -2
  28. scout/server/blueprints/panels/templates/panels/panel_pdf_simple.html +3 -3
  29. scout/server/blueprints/panels/views.py +5 -1
  30. scout/server/blueprints/variant/controllers.py +5 -2
  31. scout/server/blueprints/variant/templates/variant/variant_details.html +32 -20
  32. scout/server/blueprints/variants/controllers.py +179 -93
  33. scout/server/blueprints/variants/templates/variants/components.html +5 -4
  34. scout/server/blueprints/variants/templates/variants/fusion-variants.html +1 -1
  35. scout/server/blueprints/variants/views.py +30 -15
  36. scout/server/config.py +3 -0
  37. scout/server/templates/report_base.html +3 -3
  38. scout/server/templates/utils.html +68 -38
  39. scout/server/utils.py +25 -3
  40. {scout_browser-4.79.1.dist-info → scout_browser-4.81.dist-info}/METADATA +1 -1
  41. {scout_browser-4.79.1.dist-info → scout_browser-4.81.dist-info}/RECORD +45 -45
  42. scout/server/blueprints/cases/templates/cases/clinvar.html +0 -48
  43. {scout_browser-4.79.1.dist-info → scout_browser-4.81.dist-info}/LICENSE +0 -0
  44. {scout_browser-4.79.1.dist-info → scout_browser-4.81.dist-info}/WHEEL +0 -0
  45. {scout_browser-4.79.1.dist-info → scout_browser-4.81.dist-info}/entry_points.txt +0 -0
  46. {scout_browser-4.79.1.dist-info → scout_browser-4.81.dist-info}/top_level.txt +0 -0
scout/__version__.py CHANGED
@@ -1 +1 @@
1
- __version__ = "4.79.1"
1
+ __version__ = "4.81"
@@ -671,7 +671,7 @@ class VariantHandler(VariantLoader):
671
671
  result = self.variant_collection.delete_many(query)
672
672
  LOG.info("{0} variants deleted".format(result.deleted_count))
673
673
 
674
- def overlapping(self, variant_obj):
674
+ def overlapping(self, variant_obj, limit=30):
675
675
  """Return overlapping variants.
676
676
 
677
677
  Look at the genes that a variant overlaps to.
@@ -701,7 +701,7 @@ class VariantHandler(VariantLoader):
701
701
  }
702
702
  sort_key = [("rank_score", pymongo.DESCENDING)]
703
703
  # We collect the 30 most severe overlapping variants
704
- variants = self.variant_collection.find(query).sort(sort_key).limit(30)
704
+ variants = self.variant_collection.find(query).sort(sort_key).limit(limit)
705
705
 
706
706
  return variants
707
707
 
@@ -35,6 +35,7 @@ from .clinvar import (
35
35
  COLLECTION_METHOD,
36
36
  CONDITION_PREFIX,
37
37
  GERMLINE_CLASSIF_TERMS,
38
+ MULTIPLE_CONDITION_EXPLANATION,
38
39
  )
39
40
  from .clnsig import CLINSIG_MAP, REV_CLINSIG_MAP, TRUSTED_REVSTAT_LEVEL
40
41
  from .disease_parsing import (
@@ -99,6 +100,7 @@ from .variants_export import (
99
100
  CANCER_EXPORT_HEADER,
100
101
  EXPORT_HEADER,
101
102
  EXPORTED_VARIANTS_LIMIT,
103
+ FUSION_EXPORT_HEADER,
102
104
  MITODEL_HEADER,
103
105
  MT_COV_STATS_HEADER,
104
106
  MT_EXPORT_HEADER,
@@ -202,6 +204,7 @@ CALLERS = {
202
204
  {"id": "ascat", "name": "ASCAT"},
203
205
  {"id": "dellycnv", "name": "DellyCNV"},
204
206
  {"id": "tiddit", "name": "TIDDIT"},
207
+ {"id": "igh_dux4", "name": "IGH-DUX4 detection"},
205
208
  ],
206
209
  "mei": [{"id": "retroseq", "name": "RetroSeq"}],
207
210
  "sv": [
@@ -30,6 +30,7 @@ CLINVAR_HEADER = {
30
30
  "condition_id_type": "Condition ID type",
31
31
  "condition_id_value": "Condition ID value",
32
32
  "condition_comment": "Condition comment",
33
+ "explanation_for_multiple_conditions": "Explanation for multiple conditions",
33
34
  "clinsig": "Germline classification",
34
35
  "clinsig_comment": "Comment on classification",
35
36
  "last_evaluated": "Date last evaluated",
@@ -187,3 +188,5 @@ CONDITION_PREFIX = {
187
188
  }
188
189
 
189
190
  CLINVAR_ASSERTION_METHOD_CIT_DB_OPTIONS = {"DOI", "pmc", "PMID"}
191
+
192
+ MULTIPLE_CONDITION_EXPLANATION = ["Novel disease", "Uncertain", "Co-occurring"]
@@ -34,39 +34,41 @@ INDEXES = {
34
34
  [
35
35
  ("case_id", ASCENDING),
36
36
  ("category", ASCENDING),
37
+ ("variant_type", ASCENDING),
37
38
  ("variant_rank", ASCENDING),
39
+ ("hgnc_ids", ASCENDING),
38
40
  ],
39
- name="caseid_variantrank",
41
+ name="caseid_category_varianttype_variantrank_hgncids",
40
42
  background=True,
41
43
  ),
42
44
  IndexModel(
43
45
  [
44
- ("case_id", ASCENDING),
46
+ ("hgnc_symbols", ASCENDING),
47
+ ("rank_score", DESCENDING),
45
48
  ("category", ASCENDING),
46
49
  ("variant_type", ASCENDING),
47
- ("rank_score", DESCENDING),
48
50
  ],
49
- name="caseid_category_varianttype_rankscore",
51
+ name="hgncsymbol_rankscore_category_varianttype",
50
52
  background=True,
53
+ partialFilterExpression={"rank_score": {"$gt": 5}, "category": "snv"},
51
54
  ),
52
55
  IndexModel(
53
56
  [
54
- ("hgnc_symbols", ASCENDING),
55
- ("rank_score", DESCENDING),
57
+ ("variant_id", ASCENDING),
58
+ ("case_id", ASCENDING),
56
59
  ("category", ASCENDING),
57
- ("variant_type", ASCENDING),
58
60
  ],
59
- name="hgncsymbol_rankscore_category_varianttype",
61
+ name="variantid_caseid_category",
60
62
  background=True,
61
- partialFilterExpression={"rank_score": {"$gt": 5}, "category": "snv"},
62
63
  ),
63
64
  IndexModel(
64
65
  [
65
- ("case_id", ASCENDING),
66
66
  ("category", ASCENDING),
67
- ("variant_id", ASCENDING),
67
+ ("case_id", ASCENDING),
68
+ ("variant_type", ASCENDING),
69
+ ("rank_score", ASCENDING),
68
70
  ],
69
- name="caseid_variantid",
71
+ name="category_caseid_varianttype_rankscore",
70
72
  background=True,
71
73
  ),
72
74
  IndexModel(
@@ -15,6 +15,17 @@ EXPORT_HEADER = [
15
15
 
16
16
  CANCER_EXPORT_HEADER = EXPORT_HEADER + ["VAF TUMOR", "VAF NORMAL", "COSMIC ID"]
17
17
 
18
+ FUSION_EXPORT_HEADER = EXPORT_HEADER + [
19
+ "Fusion genes",
20
+ "Orientation",
21
+ "Frame status",
22
+ "Observed",
23
+ "Exon",
24
+ "Junction reads",
25
+ "Split reads",
26
+ "FFPM",
27
+ ]
28
+
18
29
  MT_EXPORT_HEADER = [
19
30
  "Position",
20
31
  "Change",
@@ -4,6 +4,7 @@ from datetime import datetime
4
4
  from enum import Enum
5
5
  from fractions import Fraction
6
6
  from glob import glob
7
+ from os.path import abspath, dirname, exists, isabs
7
8
  from pathlib import Path
8
9
  from typing import Any, Dict, List, Optional, Tuple, Union
9
10
 
@@ -12,7 +13,6 @@ try:
12
13
  except ImportError:
13
14
  from typing_extensions import Literal
14
15
 
15
- import path
16
16
  from pydantic import BaseModel, Field, field_validator, model_validator
17
17
 
18
18
  from scout.constants import ANALYSIS_TYPES
@@ -49,7 +49,11 @@ CASE_FILE_PATH_CHECKS = [
49
49
  "peddy_ped",
50
50
  "peddy_ped_check",
51
51
  "peddy_sex_check",
52
+ "exe_ver",
53
+ "smn_tsv",
54
+ "reference_info",
52
55
  "RNAfusion_inspector",
56
+ "RNAfusion_inspector_research",
53
57
  "RNAfusion_report",
54
58
  "RNAfusion_report_research",
55
59
  ]
@@ -86,18 +90,13 @@ class Sex(str, Enum):
86
90
  unknown = "unknown"
87
91
 
88
92
 
89
- def _get_demo_file_absolute_path(partial_path: str) -> str:
90
- """returns the absolute path to a given demo file."""
91
- APP_ROOT: str = path.abspath(path.join(path.dirname(__file__), ".."))
92
- return path.join(APP_ROOT, partial_path)
93
-
94
-
95
- def _is_string_path(string_path: str) -> bool:
96
- try:
97
- path = Path(string_path) or Path(_get_demo_file_absolute_path(partial_path=string_path))
98
- return path.is_file()
99
- except AttributeError:
100
- return False
93
+ def _resource_abs_path(string_path: str) -> str:
94
+ """Return the absolute path to a resource file."""
95
+ if not exists(string_path):
96
+ raise FileExistsError(f"Path {string_path} could not be found on disk.")
97
+ if isabs(string_path):
98
+ return string_path
99
+ return abspath(string_path)
101
100
 
102
101
 
103
102
  #### VCF files class ####
@@ -125,9 +124,8 @@ class VcfFiles(BaseModel):
125
124
  """Make sure that VCF file exists on disk."""
126
125
  for item in VCF_FILE_PATH_CHECKS:
127
126
  item_path: str = values.get(item)
128
- if item_path and _is_string_path(values[item]) is False:
129
- raise ValueError(f"{item} path '{values[item]}' is not valid.")
130
-
127
+ if item_path:
128
+ values[item] = _resource_abs_path(item_path)
131
129
  return values
132
130
 
133
131
 
@@ -145,6 +143,19 @@ class ChromographImages(BaseModel):
145
143
  upd_regions: Optional[str] = None
146
144
  upd_sites: Optional[str] = None
147
145
 
146
+ @model_validator(mode="before")
147
+ def validate_file_path(cls, config_values: Dict) -> "SampleLoader":
148
+ """Make sure that chromograph paths associated to samples exist on disk and are absolute paths. Chromograph paths are incomplete paths, containing the path to the directory containing a number of files plus the prefix of the files."""
149
+
150
+ for key in cls.model_fields:
151
+ item_path: str = config_values.get(key)
152
+ if item_path:
153
+ item_path_dirname: str = dirname(item_path)
154
+ config_values[key] = item_path.replace(
155
+ item_path_dirname, _resource_abs_path(item_path_dirname)
156
+ )
157
+ return config_values
158
+
148
159
 
149
160
  class Mitodel(BaseModel):
150
161
  discordant: Optional[int] = None
@@ -160,6 +171,15 @@ class REViewer(BaseModel):
160
171
  reference: Optional[str] = None
161
172
  reference_index: Optional[str] = None
162
173
 
174
+ @model_validator(mode="before")
175
+ def validate_file_path(cls, config_values: Dict) -> "SampleLoader":
176
+ """Make sure that REViewer paths associated to samples exist on disk and are absolute paths."""
177
+ for key in cls.model_fields:
178
+ item_path: str = config_values.get(key)
179
+ if item_path:
180
+ config_values[key] = _resource_abs_path(item_path)
181
+ return config_values
182
+
163
183
 
164
184
  class SampleLoader(BaseModel):
165
185
  alignment_path: Optional[str] = None
@@ -246,9 +266,8 @@ class SampleLoader(BaseModel):
246
266
  """Make sure that files associated to samples (mostly alignment files) exist on disk."""
247
267
  for item in SAMPLES_FILE_PATH_CHECKS:
248
268
  item_path: str = values.get(item)
249
- if item_path and _is_string_path(values[item]) is False:
250
- raise ValueError(f"{item} path '{values[item]}' is not valid.")
251
-
269
+ if item_path:
270
+ values[item] = _resource_abs_path(item_path)
252
271
  return values
253
272
 
254
273
  @field_validator("capture_kits", mode="before")
@@ -283,8 +302,9 @@ class Image(BaseModel):
283
302
  raise TypeError(
284
303
  f"Custom images should be of type: {', '.join(SUPPORTED_IMAGE_FORMATS)}"
285
304
  )
286
- if REPID not in path and _is_string_path(path) is False:
305
+ if REPID not in path and exists(path) is False:
287
306
  raise ValueError(f"Image path '{path}' is not valid.")
307
+
288
308
  return path
289
309
 
290
310
 
@@ -317,13 +337,14 @@ def set_custom_images(images: Optional[List[Image]]) -> Optional[List[Image]]:
317
337
  "description": image.description.replace(REPID, match["repid"]),
318
338
  "height": image.height,
319
339
  "format": None,
320
- "path": str(match["path"]),
340
+ "path": _resource_abs_path(str(match["path"])),
321
341
  "str_repid": match["repid"],
322
342
  "title": image.title.replace(REPID, match["repid"]),
323
343
  "width": image.width,
324
344
  }
325
345
  real_folder_images.append(Image(**new_image))
326
346
  else:
347
+ image.path = _resource_abs_path(image.path)
327
348
  real_folder_images.append(image) # append other non-repid images
328
349
 
329
350
  return real_folder_images
@@ -387,6 +408,7 @@ class CaseLoader(BaseModel):
387
408
  peddy_sex_check: Optional[str] = Field(None, alias="peddy_sex") # Soon to be deprecated
388
409
  phenotype_groups: Optional[List[str]] = None
389
410
  phenotype_terms: Optional[List[str]] = None
411
+ exe_ver: Optional[str] = None
390
412
  rank_model_version: Optional[str] = None
391
413
  rank_score_threshold: Optional[int] = 0
392
414
  reference_info: Optional[str] = None
@@ -502,8 +524,8 @@ class CaseLoader(BaseModel):
502
524
  """Make sure the files associated to the case (mostly reports) exist on disk."""
503
525
  for item in CASE_FILE_PATH_CHECKS:
504
526
  item_path: str = values.get(item)
505
- if item_path and _is_string_path(values[item]) is False:
506
- raise ValueError(f"{item} path '{values[item]}' is not valid.")
527
+ if item_path:
528
+ values[item] = _resource_abs_path(item_path)
507
529
 
508
530
  return values
509
531
 
scout/models/clinvar.py CHANGED
@@ -31,6 +31,7 @@ clinvar_variant = {
31
31
  "condition_id_type": str, # enum: "HPO", "OMIM"
32
32
  "condition_id_value": str, # example: HP:0001298;HP:0001250
33
33
  "condition_comment": str,
34
+ "explanation_for_multiple_conditions": str,
34
35
  "start": int,
35
36
  "stop": int,
36
37
  "ref": str,
@@ -271,7 +271,6 @@ def get_genmod_key(case):
271
271
  """
272
272
  case_id = case["_id"]
273
273
  if "-" in case_id:
274
- LOG.debug("iCase ID contains '-'. Using display_name as case _id.")
275
274
  return case["display_name"]
276
275
  return case["_id"]
277
276
 
@@ -474,39 +473,44 @@ def add_gene_and_transcript_info_for_fusions(
474
473
  Return:
475
474
  parsed_transcripts(list)
476
475
  """
476
+
477
477
  parsed_transcripts = []
478
478
  genes = []
479
+ hgnc_ids = []
480
+ hgnc_symbols = []
481
+
479
482
  for suffix in ["a", "b"]:
480
- genes.append(
481
- {
482
- "hgnc_symbol": parsed_variant[f"gene_{suffix}"],
483
+ # If fusions have transcript information about a fusion partner
484
+ parsed_transcript = {}
485
+ if parsed_variant.get(f"transcript_id_{suffix}"):
486
+ # Add transcript info to genes if available
487
+ parsed_transcript = {
488
+ "transcript_id": parsed_variant[f"transcript_id_{suffix}"],
483
489
  "hgnc_id": parsed_variant[f"hgnc_id_{suffix}"],
484
- "transcripts": [],
490
+ "hgnc_symbol": parsed_variant[f"gene_{suffix}"],
491
+ "exon": parsed_variant[f"exon_number_{suffix}"],
485
492
  }
486
- )
487
- # If fusions have transcript information about both fusion partners
488
- if parsed_variant["transcript_id_a"] and parsed_variant["transcript_id_b"]:
489
- parsed_transcripts.append(
493
+ parsed_transcripts.append(parsed_transcript)
494
+
495
+ if parsed_variant.get(f"hgnc_id_{suffix}"):
496
+ if parsed_variant.get(f"gene_{suffix}"):
497
+ # Add hgnc_symbol to variant if available
498
+ hgnc_symbols.append(parsed_variant.get(f"gene_{suffix}"))
499
+
500
+ # Add hgnc_id to variant if available
501
+ hgnc_ids.append(parsed_variant.get(f"hgnc_id_{suffix}"))
502
+
503
+ genes.append(
490
504
  {
491
- "transcript_id": parsed_variant[f"transcript_id_{suffix}"],
492
- "hgnc_id": parsed_variant[f"hgnc_id_{suffix}"],
493
505
  "hgnc_symbol": parsed_variant[f"gene_{suffix}"],
494
- "exon": parsed_variant[f"exon_number_{suffix}"],
506
+ "hgnc_id": parsed_variant[f"hgnc_id_{suffix}"],
507
+ "transcripts": [parsed_transcript] if parsed_transcript else [],
495
508
  }
496
509
  )
497
510
 
498
- # Add transcript info to genes if available
499
- if parsed_transcripts:
500
- genes[0]["transcripts"] = [parsed_transcripts[0]]
501
- genes[1]["transcripts"] = [parsed_transcripts[1]]
502
-
503
- # Add hgnc_id to variant if available
504
- hgnc_ids = []
505
- if parsed_variant["hgnc_id_a"] and parsed_variant["hgnc_id_b"]:
506
- hgnc_ids = [gene["hgnc_id"] for gene in genes]
507
- parsed_variant["genes"] = genes
508
-
511
+ parsed_variant["genes"] = genes
509
512
  parsed_variant["hgnc_ids"] = hgnc_ids
513
+ parsed_variant["hgnc_symbols"] = hgnc_symbols
510
514
 
511
515
  return parsed_transcripts
512
516
 
@@ -67,6 +67,12 @@ def pin_variant(
67
67
 
68
68
  (institute_obj, case_obj, variant_obj) = _lookup_variant(variant_id, institute_id, case_name)
69
69
 
70
+ if not institute_id:
71
+ institute_id = variant_obj["institute"]
72
+
73
+ if not case_name:
74
+ case_name = case_obj.get("display_name")
75
+
70
76
  user_obj = store.user(current_user.email)
71
77
  link = url_for(
72
78
  "variant.variant",
@@ -88,6 +94,12 @@ def unpin_variant(
88
94
 
89
95
  (institute_obj, case_obj, variant_obj) = _lookup_variant(variant_id, institute_id, case_name)
90
96
 
97
+ if not institute_id:
98
+ institute_id = variant_obj["institute"]
99
+
100
+ if not case_name:
101
+ case_name = case_obj.get("display_name")
102
+
91
103
  user_obj = store.user(current_user.email)
92
104
  link = url_for(
93
105
  "variant.variant",
@@ -4,7 +4,7 @@ import itertools
4
4
  import json
5
5
  import logging
6
6
  import os
7
- from typing import Dict, List, Set
7
+ from typing import Dict, List, Optional, Set
8
8
 
9
9
  import query_phenomizer
10
10
  import requests
@@ -45,6 +45,8 @@ from scout.server.extensions import RerunnerError, bionano_access, gens, matchma
45
45
  from scout.server.links import disease_link
46
46
  from scout.server.utils import (
47
47
  case_has_alignments,
48
+ case_has_chanjo2_coverage,
49
+ case_has_chanjo_coverage,
48
50
  case_has_mt_alignments,
49
51
  case_has_rna_tracks,
50
52
  institute_and_case,
@@ -83,6 +85,61 @@ def phenomizer_diseases(hpo_ids, case_obj, p_value_treshold=1):
83
85
  flash("Could not establish a conection to Phenomizer", "danger")
84
86
 
85
87
 
88
+ def chanjo2_coverage_report_contents(
89
+ institute_obj: dict, case_obj: dict, panel_name: str, panel_id: Optional[str], report_type: str
90
+ ) -> Optional[str]:
91
+ """Retrieve the HTML contents of the Chanjo2 coverage report/overview for a case."""
92
+
93
+ if panel_id:
94
+ hgnc_gene_ids: List[int] = store.panel_to_genes(panel_id=panel_id, gene_format="hgnc_id")
95
+ elif panel_name == "HPO Panel":
96
+ hgnc_gene_ids: List[int] = [
97
+ gene["hgnc_id"] for gene in case_obj.get("dynamic_gene_list", [])
98
+ ]
99
+ else:
100
+ hgnc_gene_ids: List[int] = _get_default_panel_genes(store, case_obj)
101
+
102
+ if not hgnc_gene_ids:
103
+ flash("Case should have at least one default gene panel containing genes", "warning")
104
+ return
105
+
106
+ query_samples: List[dict] = []
107
+ analysis_types: List[str] = []
108
+
109
+ for ind in case_obj.get("individuals", []):
110
+ if not ind.get("d4_file"):
111
+ continue
112
+ query_samples.append(
113
+ {
114
+ "name": ind.get("display_name"),
115
+ "coverage_file_path": ind.get("d4_file"),
116
+ "case_name": case_obj["display_name"],
117
+ "analysis_date": case_obj["analysis_date"].isoformat(),
118
+ }
119
+ )
120
+ analysis_types.append(ind.get("analysis_type"))
121
+
122
+ interval_type = "genes"
123
+ if "wes" in analysis_types:
124
+ interval_type = "transcripts"
125
+ elif "wts" in analysis_types:
126
+ interval_type = "exons"
127
+
128
+ report_query: dict = {
129
+ "build": "GRCh38" if "38" in case_obj.get("genome_build", "37") else "GRCh37",
130
+ "default_level": institute_obj.get("coverage_cutoff"),
131
+ "interval_type": interval_type,
132
+ "panel_name": panel_name,
133
+ "case_display_name": case_obj["display_name"],
134
+ "hgnc_gene_ids": hgnc_gene_ids,
135
+ "samples": query_samples,
136
+ }
137
+
138
+ report_url: str = "/".join([current_app.config.get("CHANJO2_URL"), report_type])
139
+ response = requests.post(report_url, json=report_query)
140
+ return response.text
141
+
142
+
86
143
  def coverage_report_contents(base_url, institute_obj, case_obj):
87
144
  """Capture the contents of a case coverage report (chanjo-report), to be displayed in the general case report
88
145
 
@@ -298,9 +355,11 @@ def case(store, institute_obj, case_obj):
298
355
  store.user(user_id=user_id) for user_id in case_obj.get("assignees", [])
299
356
  ]
300
357
 
301
- # Provide basic info on alignment files availability for this case
358
+ # Provide basic info on alignment files & coverage data availability for this case
302
359
  case_has_alignments(case_obj)
303
360
  case_has_mt_alignments(case_obj)
361
+ case_has_chanjo_coverage(case_obj)
362
+ case_has_chanjo2_coverage(case_obj)
304
363
 
305
364
  case_groups = {}
306
365
  case_group_label = {}
@@ -324,10 +383,18 @@ def case(store, institute_obj, case_obj):
324
383
  partial_causatives = _get_partial_causatives(store, case_obj)
325
384
  _populate_assessments(partial_causatives)
326
385
 
327
- case_obj["default_genes"] = _get_default_panel_genes(store, case_obj)
386
+ case_obj["clinvar_variants"] = store.case_to_clinVars(case_obj["_id"])
328
387
 
329
- # Sort panels alphabetically on display name
330
- case_obj["panels"] = sorted(case_obj.get("panels", []), key=lambda d: d["display_name"])
388
+ # check for variants submitted to clinVar but not present in suspects for the case
389
+ clinvar_variants_not_in_suspects = [
390
+ store.variant(variant_id) or variant_id
391
+ for variant_id in case_obj["clinvar_variants"]
392
+ if variant_id not in [entry.get("_id") for entry in suspects]
393
+ ]
394
+
395
+ case_obj["clinvar_variants_not_in_suspects"] = clinvar_variants_not_in_suspects
396
+
397
+ case_obj["default_genes"] = _get_default_panel_genes(store, case_obj)
331
398
 
332
399
  for hpo_term in itertools.chain(
333
400
  case_obj.get("phenotype_groups") or [], case_obj.get("phenotype_terms") or []
@@ -355,7 +422,6 @@ def case(store, institute_obj, case_obj):
355
422
  if institute_obj.get("collaborators")
356
423
  and collab["_id"] in institute_obj.get("collaborators")
357
424
  ]
358
- case_obj["clinvar_variants"] = store.case_to_clinVars(case_obj["_id"])
359
425
 
360
426
  # if updated_at is a list, set it to the last update datetime
361
427
  if case_obj.get("updated_at") and isinstance(case_obj["updated_at"], list):
@@ -5,7 +5,6 @@
5
5
  {% from "cases/individuals_table.html" import cancer_individuals_table, individuals_table %}
6
6
  {% from "cases/phenotype.html" import hpo_item, cohort_panel, diagnosis_phenotypes, phenotype_groups_panel, add_phenotype_terms_panel, hpo_panel %}
7
7
  {% from "cases/gene_panel.html" import genepanels_table, hpo_genelist_panel %}
8
- {% from "cases/clinvar.html" import clinvar_vars %}
9
8
 
10
9
  {% block title %}
11
10
  {{ super() }} - {{ institute.display_name }} - {{ case.display_name }}
@@ -148,9 +147,6 @@
148
147
  <div class="row">
149
148
  <div class="col">{{ causatives_list(causatives, partial_causatives, evaluated_variants, institute, case, manual_rank_options, cancer_tier_options) }}</div>
150
149
  <div class="col">{{ suspects_list(suspects, institute, case, manual_rank_options, cancer_tier_options) }}</div>
151
- {% if case.clinvar_variants or case.suspects %}
152
- <div class="col">{{ clinvar_vars(institute, case, suspects) }}</div>
153
- {% endif %}
154
150
  <!-- end of data sharing panels -->
155
151
  </div>
156
152
 
@@ -516,24 +512,6 @@
516
512
  </form>
517
513
  {% endmacro %}
518
514
 
519
- {% macro remove_form(url, hidden_input=None, button_name=None, button_value=None) %}
520
- <form action="{{ url }}" method="POST">
521
- {% if hidden_input %}
522
- <input type="hidden"
523
- name="{{ hidden_input[0] }}"
524
- value="{{ hidden_input[1] }}">
525
- {% endif %}
526
- <div class="float-end">
527
- <button class="btn btn-link btn-sm"
528
- name="{{ button_name if button_name }}"
529
- value="{{ button_value if button_value }}"
530
- type="submit">
531
- <em class="fa fa-remove"></em>
532
- </button>
533
- </div>
534
- </form>
535
- {% endmacro %}
536
-
537
515
  {% macro solve_modal(institute, case, case_tag_options) %}
538
516
  <div class="modal fade" id="solve_modal" tabindex="-1" role="dialog" aria-labelledby="solveCaseLabel" aria-hidden="true">
539
517
  <div class="modal-dialog" role="document">
@@ -623,6 +601,7 @@
623
601
  paging: false,
624
602
  searching: false,
625
603
  ordering: true,
604
+ order:[[1, 'desc'],[0, 'asc']],
626
605
  info: false
627
606
  });
628
607
  {% endif %}
@@ -42,7 +42,9 @@
42
42
  </div>
43
43
  <div class="col-md-8">
44
44
  {{ synopsis_panel() }}
45
+ <div class="panel-default">
45
46
  {{ comments_panel(institute, case, current_user, comments) }}
47
+ </div>
46
48
  </div>
47
49
  </div> <!-- end of div row -->
48
50