scout-browser 4.83__py3-none-any.whl → 4.85__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 (58) hide show
  1. scout/__version__.py +1 -1
  2. scout/adapter/mongo/case.py +5 -1
  3. scout/adapter/mongo/cytoband.py +13 -0
  4. scout/adapter/mongo/hgnc.py +1 -1
  5. scout/adapter/mongo/variant.py +9 -0
  6. scout/adapter/mongo/variant_loader.py +73 -71
  7. scout/build/genes/hgnc_gene.py +5 -134
  8. scout/commands/download/ensembl.py +1 -0
  9. scout/commands/download/everything.py +1 -0
  10. scout/commands/download/exac.py +1 -0
  11. scout/commands/download/hgnc.py +1 -0
  12. scout/commands/download/hpo.py +1 -0
  13. scout/commands/download/omim.py +1 -0
  14. scout/commands/export/database.py +1 -0
  15. scout/commands/load/panel.py +1 -0
  16. scout/commands/load/report.py +1 -0
  17. scout/commands/update/genes.py +9 -13
  18. scout/commands/update/omim.py +1 -0
  19. scout/commands/update/panelapp.py +1 -0
  20. scout/constants/file_types.py +86 -17
  21. scout/export/exon.py +1 -0
  22. scout/load/all.py +5 -1
  23. scout/load/hgnc_gene.py +40 -7
  24. scout/models/hgnc_map.py +50 -87
  25. scout/models/phenotype_term.py +3 -3
  26. scout/parse/hgnc.py +1 -0
  27. scout/parse/orpha.py +1 -0
  28. scout/parse/variant/conservation.py +1 -0
  29. scout/parse/variant/transcript.py +1 -1
  30. scout/parse/variant/variant.py +10 -4
  31. scout/server/blueprints/cases/controllers.py +15 -1
  32. scout/server/blueprints/cases/templates/cases/case.html +96 -89
  33. scout/server/blueprints/cases/templates/cases/collapsible_actionbar.html +1 -1
  34. scout/server/blueprints/cases/templates/cases/gene_panel.html +27 -41
  35. scout/server/blueprints/cases/templates/cases/utils.html +1 -1
  36. scout/server/blueprints/panels/forms.py +1 -0
  37. scout/server/blueprints/variant/controllers.py +9 -14
  38. scout/server/blueprints/variants/controllers.py +11 -27
  39. scout/server/extensions/bionano_extension.py +1 -0
  40. scout/server/extensions/chanjo_extension.py +10 -9
  41. scout/server/extensions/gens_extension.py +1 -0
  42. scout/server/extensions/ldap_extension.py +5 -3
  43. scout/server/extensions/loqus_extension.py +16 -14
  44. scout/server/extensions/matchmaker_extension.py +1 -0
  45. scout/server/extensions/mongo_extension.py +1 -0
  46. scout/server/extensions/rerunner_extension.py +1 -0
  47. scout/server/links.py +4 -4
  48. scout/server/static/bs_styles.css +5 -5
  49. scout/server/templates/utils.html +1 -1
  50. scout/utils/ensembl_rest_clients.py +1 -0
  51. scout/utils/scout_requests.py +1 -0
  52. scout/utils/sort.py +21 -0
  53. {scout_browser-4.83.dist-info → scout_browser-4.85.dist-info}/METADATA +3 -6
  54. {scout_browser-4.83.dist-info → scout_browser-4.85.dist-info}/RECORD +58 -57
  55. {scout_browser-4.83.dist-info → scout_browser-4.85.dist-info}/WHEEL +1 -1
  56. {scout_browser-4.83.dist-info → scout_browser-4.85.dist-info}/entry_points.txt +0 -1
  57. {scout_browser-4.83.dist-info → scout_browser-4.85.dist-info}/LICENSE +0 -0
  58. {scout_browser-4.83.dist-info → scout_browser-4.85.dist-info}/top_level.txt +0 -0
scout/load/all.py CHANGED
@@ -3,6 +3,7 @@ import logging
3
3
 
4
4
  from scout.constants import FILE_TYPE_MAP
5
5
  from scout.exceptions.config import ConfigError
6
+ from scout.utils.sort import get_load_priority
6
7
 
7
8
  LOG = logging.getLogger(__name__)
8
9
 
@@ -62,7 +63,10 @@ def load_region(adapter, case_id, hgnc_id=None, chrom=None, start=None, end=None
62
63
  (FILE_TYPE_MAP[file_type]["variant_type"], FILE_TYPE_MAP[file_type]["category"])
63
64
  )
64
65
 
65
- for variant_type, category in case_file_types:
66
+ for variant_type, category in sorted(
67
+ case_file_types,
68
+ key=lambda tup: get_load_priority(variant_type=tup[0], category=tup[1]),
69
+ ):
66
70
  if variant_type == "research" and not case_obj["is_research"]:
67
71
  continue
68
72
 
scout/load/hgnc_gene.py CHANGED
@@ -1,5 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  import logging
3
+ from typing import Dict
3
4
 
4
5
  from click import progressbar
5
6
 
@@ -16,6 +17,22 @@ from scout.utils.scout_requests import (
16
17
  LOG = logging.getLogger(__name__)
17
18
 
18
19
 
20
+ def set_missing_gene_coordinates(gene_data: dict, cytoband_coords: Dict[str, dict]):
21
+ """Attempt at collecting gene coordinates from cytoband for genes missing Ensembl ID."""
22
+
23
+ if gene_data.get("ensembl_gene_id") not in [
24
+ "",
25
+ None,
26
+ ]: # Coordinates are present, since they're collected from the Ensembl file
27
+ return
28
+ gene_data["ensembl_gene_id"] = None
29
+ cytoband_coord: dict = cytoband_coords.get(gene_data["location"])
30
+ if cytoband_coord:
31
+ gene_data["chromosome"]: str = cytoband_coord["chromosome"]
32
+ gene_data["start"]: int = cytoband_coord["start"]
33
+ gene_data["end"]: int = cytoband_coord["stop"]
34
+
35
+
19
36
  def load_hgnc_genes(
20
37
  adapter,
21
38
  genes=None,
@@ -36,7 +53,7 @@ def load_hgnc_genes(
36
53
  Args:
37
54
  adapter(scout.adapter.MongoAdapter)
38
55
  genes(dict): If genes are already parsed
39
- ensembl_lines(iterable(str)): Lines formated with ensembl gene information
56
+ ensembl_lines(iterable(str)): Lines formatted with ensembl gene information
40
57
  hgnc_lines(iterable(str)): Lines with gene information from genenames.org
41
58
  exac_lines(iterable(str)): Lines with information pLi-scores from ExAC
42
59
  mim2gene(iterable(str)): Lines with map from omim id to gene symbol
@@ -78,20 +95,36 @@ def load_hgnc_genes(
78
95
  genemap_lines=genemap_lines,
79
96
  )
80
97
 
81
- non_existing = 0
98
+ without_coords = 0
82
99
  nr_genes = len(genes)
83
100
  LOG.info(f"Building info for {nr_genes} genes")
101
+
102
+ cytoband_coords: Dict[str, dict] = adapter.cytoband_to_coordinates(build=build)
103
+
84
104
  with progressbar(genes.values(), label="Building genes", length=nr_genes) as bar:
85
105
  for gene_data in bar:
106
+ set_missing_gene_coordinates(gene_data=gene_data, cytoband_coords=cytoband_coords)
107
+
86
108
  if not gene_data.get("chromosome"):
87
- non_existing += 1
109
+ without_coords += 1
88
110
  continue
111
+ gene_obj = build_hgnc_gene(
112
+ gene_data,
113
+ build=build,
114
+ )
115
+
116
+ if gene_obj:
117
+ gene_objects.append(gene_obj)
118
+ else:
119
+ without_coords += 1
89
120
 
90
- gene_obj = build_hgnc_gene(gene_data, build=build)
91
- gene_objects.append(gene_obj)
121
+ LOG.info(
122
+ "Nr of genes without coordinates in build %s and therefore skipped: %s",
123
+ build,
124
+ without_coords,
125
+ )
126
+ LOG.info(f"Loading {len(gene_objects)} genes into the database")
92
127
 
93
- LOG.info("Nr of genes without coordinates in build %s: %s", build, non_existing)
94
- LOG.info(f"Loading {len(gene_objects)} genes to database")
95
128
  adapter.load_hgnc_bulk(gene_objects)
96
129
 
97
130
  LOG.info("Loading done. %s genes loaded", len(gene_objects))
scout/models/hgnc_map.py CHANGED
@@ -2,6 +2,8 @@ from __future__ import unicode_literals
2
2
 
3
3
  from typing import List, Optional
4
4
 
5
+ from pydantic import BaseModel, Field, field_validator, model_validator
6
+
5
7
 
6
8
  class Exon(dict):
7
9
  """Exon dictionary
@@ -66,90 +68,51 @@ class HgncTranscript(dict):
66
68
  self["mane_plus_clinical"] = mane_plus_clinical
67
69
 
68
70
 
69
- class HgncGene(dict):
70
- """HgncGene dictionary
71
-
72
- 'hgnc_id': int, # This is the hgnc id, required:
73
- 'hgnc_symbol': str, # The primary symbol, required
74
- 'ensembl_id': str, # required
75
- 'build': str, # '37' or '38', defaults to '37', required
76
-
77
- 'chromosome': str, # required
78
- 'start': int, # required
79
- 'end': int, # required
80
-
81
- 'description': str, # Gene description
82
- 'aliases': list(), # Gene symbol aliases, includes hgnc_symbol, str
83
- 'entrez_id': int,
84
- 'omim_id': int,
85
- 'pli_score': float,
86
- 'primary_transcripts': list(), # List of refseq transcripts (str)
87
- 'ucsc_id': str,
88
- 'uniprot_ids': list(), # List of str
89
- 'vega_id': str,
90
-
91
- # Inheritance information
92
- 'inheritance_models': list(), # List of model names
93
- 'incomplete_penetrance': bool, # Acquired from HPO
94
-
95
- # Phenotype information
96
- 'phenotypes': list(), # List of dictionaries with phenotype information
97
- """
98
-
99
- def __init__(
100
- self,
101
- hgnc_id,
102
- hgnc_symbol,
103
- ensembl_id,
104
- chrom,
105
- start,
106
- end,
107
- description=None,
108
- aliases=None,
109
- entrez_id=None,
110
- omim_id=None,
111
- pli_score=None,
112
- primary_transcripts=None,
113
- ucsc_id=None,
114
- uniprot_ids=None,
115
- vega_id=None,
116
- inheritance_models=None,
117
- incomplete_penetrance=False,
118
- phenotypes=None,
119
- build="37",
120
- ):
121
- super(HgncGene, self).__init__()
122
- self["hgnc_id"] = int(hgnc_id)
123
- self["hgnc_symbol"] = hgnc_symbol
124
- self["ensembl_id"] = ensembl_id
125
-
126
- self["chromosome"] = chrom
127
- self["start"] = int(start)
128
- self["end"] = int(end)
129
- self["length"] = self["end"] - self["start"]
130
-
131
- self["description"] = description
132
- self["aliases"] = aliases
133
- self["primary_transcripts"] = primary_transcripts
134
- self["inheritance_models"] = inheritance_models
135
- self["phenotypes"] = phenotypes
136
-
137
- self["entrez_id"] = entrez_id
138
- if entrez_id:
139
- self["entrez_id"] = int(entrez_id)
140
-
141
- self["omim_id"] = omim_id
142
- if omim_id:
143
- self["omim_id"] = int(omim_id)
144
-
145
- self["ucsc_id"] = ucsc_id
146
- self["uniprot_ids"] = uniprot_ids
147
- self["vega_id"] = vega_id
148
-
149
- self["pli_score"] = pli_score
150
- if pli_score:
151
- self["pli_score"] = float(pli_score)
152
-
153
- self["incomplete_penetrance"] = incomplete_penetrance
154
-
155
- self["build"] = build
71
+ class HgncGene(BaseModel):
72
+ hgnc_id: int
73
+ hgnc_symbol: str
74
+ build: str
75
+ chromosome: str
76
+ start: int
77
+ end: int
78
+ length: int
79
+ description: Optional[str] = None
80
+ ensembl_id: Optional[str] = Field(None, alias="ensembl_gene_id")
81
+ aliases: Optional[List[str]] = Field(None, alias="previous_symbols")
82
+ entrez_id: Optional[int] = None
83
+ omim_id: Optional[int] = None
84
+ primary_transcripts: Optional[List[str]] = Field(None, alias="ref_seq")
85
+ ucsc_id: Optional[str] = None
86
+ uniprot_ids: Optional[List[str]] = None
87
+ vega_id: Optional[str] = None
88
+ inheritance_models: Optional[List[str]] = None
89
+ incomplete_penetrance: Optional[bool] = False
90
+ phenotypes: Optional[List[dict]] = None
91
+ pli_score: Optional[float] = None
92
+ constraint_lof_oe: Optional[float] = None
93
+ constraint_lof_oe_ci_lower: Optional[float] = None
94
+ constraint_lof_oe_ci_upper: Optional[float] = None
95
+ constraint_lof_z: Optional[float] = None
96
+ constraint_mis_oe: Optional[float] = None
97
+ constraint_mis_oe_ci_lower: Optional[float] = None
98
+ constraint_mis_oe_ci_upper: Optional[float] = None
99
+ constraint_mis_z: Optional[float] = None
100
+
101
+ @model_validator(mode="before")
102
+ def set_gene_length(cls, values) -> "HgncGene":
103
+ """Set gene length."""
104
+ if None in [values.get("end"), values.get("start")]:
105
+ values.update({"length": None})
106
+ else:
107
+ values.update({"length": values.get("end") - values.get("start")})
108
+ return values
109
+
110
+ @field_validator("phenotypes", mode="before")
111
+ @classmethod
112
+ def set_phenotypes_inheritance(cls, phenotypes) -> Optional[List[dict]]:
113
+ """Convert field 'inheritance' of each phenotype in phenotypes from set to list."""
114
+ for phenotype in phenotypes:
115
+ phenotype["inheritance_models"] = list(phenotype.get("inheritance", {}))
116
+ phenotype.pop("inheritance", None)
117
+
118
+ return phenotypes
@@ -14,9 +14,9 @@ class HpoTerm(BaseModel):
14
14
  """
15
15
 
16
16
  hpo_id: str # id field in the hpo.obo file
17
- hpo_number: Optional[
18
- int
19
- ] = None # id field in the hpo.obo file, stripped of the 'HP:' part and the zeroes
17
+ hpo_number: Optional[int] = (
18
+ None # id field in the hpo.obo file, stripped of the 'HP:' part and the zeroes
19
+ )
20
20
  description: str # name field in the hpo.obo file
21
21
  ancestors: List = []
22
22
  all_ancestors: List = []
scout/parse/hgnc.py CHANGED
@@ -24,6 +24,7 @@ def parse_hgnc_line(line, header):
24
24
  hgnc_gene["hgnc_symbol"] = hgnc_symbol
25
25
  hgnc_gene["hgnc_id"] = int(raw_info["hgnc_id"].split(":")[-1])
26
26
  hgnc_gene["description"] = raw_info["name"]
27
+ hgnc_gene["location"] = raw_info["location"] # cytoband
27
28
 
28
29
  # We want to have the current symbol as an alias
29
30
  aliases = set([hgnc_symbol, hgnc_symbol.upper()])
scout/parse/orpha.py CHANGED
@@ -1,4 +1,5 @@
1
1
  """Code for parsing ORPHA formatted files"""
2
+
2
3
  import logging
3
4
  from typing import Any, Dict, List
4
5
  from xml.etree.ElementTree import Element
@@ -1,4 +1,5 @@
1
1
  """Code for parsing conservation"""
2
+
2
3
  import logging
3
4
  import numbers
4
5
 
@@ -324,7 +324,7 @@ def set_variant_frequencies(transcript, entry):
324
324
  continue
325
325
 
326
326
  value = entry[key]
327
- if not value or value == ".":
327
+ if not value or value == "." or value.isalpha():
328
328
  continue
329
329
 
330
330
  if key in THOUSAND_GENOMES_CSQ_KEYS:
@@ -231,10 +231,7 @@ def parse_variant(
231
231
  parsed_variant["revel_score"] = parsed_transcripts[0].get(
232
232
  "revel_rankscore"
233
233
  ) # This is actually the value of REVEL_rankscore
234
-
235
- parsed_variant["revel"] = parsed_transcripts[0].get(
236
- "revel_raw_score"
237
- ) # This is actually the value of REVEL_score
234
+ parsed_variant["revel"] = get_highest_revel_score(parsed_transcripts)
238
235
 
239
236
  ###################### Add conservation ######################
240
237
  parsed_variant["conservation"] = parse_conservations(variant, parsed_transcripts)
@@ -261,6 +258,15 @@ def parse_variant(
261
258
  return parsed_variant
262
259
 
263
260
 
261
+ def get_highest_revel_score(parsed_transcripts: List[dict]) -> Optional[float]:
262
+ """Retrieve the highest REVEL_score value from parsed variant transcripts."""
263
+ tx_revel_scores: List(float) = [
264
+ tx.get("revel_raw_score") for tx in parsed_transcripts if tx.get("revel_raw_score") != None
265
+ ]
266
+ if tx_revel_scores:
267
+ return max(tx_revel_scores)
268
+
269
+
264
270
  def get_genmod_key(case):
265
271
  """Gen genmod key
266
272
 
@@ -64,6 +64,7 @@ JSON_HEADERS = {
64
64
  COVERAGE_REPORT_TIMEOUT = 20
65
65
 
66
66
  PANEL_PROJECTION = {"version": 1, "display_name": 1, "genes": 1}
67
+ PANEL_HIDDEN_PROJECTION = {"version": 1, "display_name": 1, "hidden": 1}
67
68
 
68
69
 
69
70
  def phenomizer_diseases(hpo_ids, case_obj, p_value_treshold=1):
@@ -339,6 +340,8 @@ def case(
339
340
 
340
341
  case_obj["default_genes"] = _get_default_panel_genes(store, case_obj)
341
342
 
343
+ _set_panel_removed(store, case_obj)
344
+
342
345
  for hpo_term in itertools.chain(
343
346
  case_obj.get("phenotype_groups") or [], case_obj.get("phenotype_terms") or []
344
347
  ):
@@ -479,6 +482,18 @@ def _limit_genes_on_default_panels(default_genes: list, limit_genes: list) -> li
479
482
  return list(default_genes_set.intersection(limit_genes_set))
480
483
 
481
484
 
485
+ def _set_panel_removed(store: MongoAdapter, case_obj: dict) -> list:
486
+ """Flag panel on list removed if the latest panel version is marked hidden."""
487
+
488
+ for panel_info in case_obj.get("panels", []):
489
+ latest_panel = store.gene_panel(
490
+ panel_info["panel_name"], projection=PANEL_HIDDEN_PROJECTION
491
+ )
492
+ panel_info["removed"] = (
493
+ latest_panel.get("hidden", False) if latest_panel is not None else False
494
+ )
495
+
496
+
482
497
  def _get_default_panel_genes(store: MongoAdapter, case_obj: dict) -> list:
483
498
  """Get unique genes on case default panels.
484
499
 
@@ -510,7 +525,6 @@ def _get_default_panel_genes(store: MongoAdapter, case_obj: dict) -> list:
510
525
  projection=PANEL_PROJECTION,
511
526
  )
512
527
  latest_panel = store.gene_panel(panel_name, projection=PANEL_PROJECTION)
513
- panel_info["removed"] = False if latest_panel is None else latest_panel.get("hidden", False)
514
528
  if not panel_obj:
515
529
  panel_obj = latest_panel
516
530
  if not panel_obj:
@@ -65,7 +65,7 @@
65
65
  <span class="badge rounded-pill bg-info text-body" data-bs-toggle="tooltip" data-bs-placement="left" title="Matching causatives and managed variants displayed on case page are NOT filtered by gene panel. Use caution to avoid incidental findings.">off</span>
66
66
  {% endif %}
67
67
  </div>
68
- </div> <!--end of div class="row" -->
68
+ </div> <!--end of row -->
69
69
  <div class="row">
70
70
  <form id="case_status_form" method="POST" action="{{ url_for('cases.status', institute_id=institute._id, case_name=case.display_name) }}">
71
71
  <div class="btn-toolbar ms-2" role="toolbar">
@@ -105,7 +105,7 @@
105
105
  {{ 'Inactive' if case.status == 'ignored' else 'Ignored' }}
106
106
  </button>
107
107
  </div>
108
- <div class="btn-group ms-2 role="group">
108
+ <div class="btn-group ms-2" role="group">
109
109
  <select name="tags" id="status_tags_case" multiple class="selectpicker" data-style="btn btn-secondary">
110
110
  {% for tag, data in case_tag_options.items() %}
111
111
  <option {% if 'tags' in case and tag~"" in case.tags %} selected {% endif %} value="{{ tag }}" title="{{ data.label }}">
@@ -119,103 +119,65 @@
119
119
  </div>
120
120
  </div>
121
121
 
122
+ {{ matching_variants() }}
123
+
122
124
  <div class="card panel-default" >
123
- <div class="row mt-0">
124
- <div class="col-sm-12 col-md-12 ms-3">
125
- <div data-bs-toggle='tooltip' class="panel-heading" title="Check if there are any variants in this case
126
- marked as causative in another case for this institute, or are on the managed variants list.">
127
- <strong>{% if hide_matching == false %}
128
- <a href="{{ url_for('cases.case', institute_id=institute._id, case_name=case.display_name, hide_matching='True') }}" class="text-body"><span class="me-1 fa fa-caret-right"></span>Search for matching causatives and managed variants</a>
129
- {% else %}
130
- <a href="{{ url_for('cases.case', institute_id=institute._id, case_name=case.display_name, hide_matching='False') }}" class="text-body"><span class="me-1 fa fa-caret-down"></span>Search for matching causatives and managed variants</a>
131
- {% endif %}
132
- </strong>
133
- </div>
134
- </div>
125
+ <div class="row">
126
+ <div class="col">{{ causatives_list(causatives, partial_causatives, evaluated_variants, institute, case, manual_rank_options, cancer_tier_options) }}</div>
127
+ <div class="col">{{ suspects_list(suspects, institute, case, manual_rank_options, cancer_tier_options) }}</div>
128
+ <!-- end of data sharing panels -->
135
129
  </div>
136
- {% if hide_matching == false %}
137
- {% if other_causatives|length > 0 %}
138
- <div class="row mt-0 ms-3">
139
- <div class="col-xs-12 col-md-12 ms-3">{{ matching_causatives(other_causatives, institute, case) }}</div>
140
- </div>
141
- {% endif %}
142
- {% if default_other_causatives|length > 0%}
143
- <div class="row mt-0 ms-3">
144
- <div class="col-xs-12 col-md-12 ms-3">{{ matching_causatives(default_other_causatives, institute, case, default=True) }}</div>
145
- </div>
146
- {% endif %}
147
- {% if managed_variants|length > 0%}
148
- <div class="row mt-0 ms-3">
149
- <div class="col-sm-12 col-md-12 ms-3">{{ matching_managed_variants(managed_variants, institute, case) }}</div>
150
- </div>
151
- {% endif %}
152
- {% if default_managed_variants|length > 0%}
153
- <div class="row mt-0 ms-3">
154
- <div class="col-sm-12 col-md-12 ms-3">{{ matching_managed_variants(default_managed_variants, institute, case, default=True) }}</div>
155
- </div>
156
- {% endif %}
157
- {% if other_causatives|length == 0 and default_other_causatives|length == 0 and managed_variants|length == 0 and default_managed_variants|length == 0%}
158
- <div class="row mt-0 ms-3">
159
- <div class="col-sm-12 col-md-12 ms-3">No matching causatives or managed variants found</div>
160
- </div>
161
- {% endif %}
162
- {% endif %}
163
- <div class="row">
164
- <div class="col">{{ causatives_list(causatives, partial_causatives, evaluated_variants, institute, case, manual_rank_options, cancer_tier_options) }}</div>
165
- <div class="col">{{ suspects_list(suspects, institute, case, manual_rank_options, cancer_tier_options) }}</div>
166
- <!-- end of data sharing panels -->
167
- </div>
168
130
 
169
- <div class="row">
170
- {% if case.track == 'cancer' %}
171
- <div class="col-sm-12 col-md-12 mt-3">{{ cancer_individuals_table(case, institute, tissue_types, gens_info) }}</div>
172
- {% else %}
173
- <div class="mt-3 col-sm-8 col-md-{{"8" if case.madeline_info and case.individuals|length > 1 else "12"}}">{{ individuals_table(case, institute, tissue_types, display_rerunner, gens_info) }}</div>
174
- {% if case.madeline_info and case.individuals|length > 1 %}
175
- <div class="col-sm-4">
176
- {{ pedigree_panel(case) }}
177
- </div>
131
+ <div class="row">
132
+ {% if case.track == 'cancer' %}
133
+ <div class="col-sm-12 col-md-12 mt-3">{{ cancer_individuals_table(case, institute, tissue_types, gens_info) }}</div>
134
+ {% else %}
135
+ <div class="mt-3 col-sm-8 col-md-{{"8" if case.madeline_info and case.individuals|length > 1 else "12"}}">{{ individuals_table(case, institute, tissue_types, display_rerunner, gens_info) }}</div>
136
+ {% if case.madeline_info and case.individuals|length > 1 %}
137
+ <div class="col-sm-4">
138
+ {{ pedigree_panel(case) }}
139
+ </div>
140
+ {% endif %}
178
141
  {% endif %}
179
- {% endif %}
180
- </div>
181
-
182
- <div class="row mt-3">
183
- <div class="col-6">{{ synopsis_panel() }}</div>
184
- <div class="col-6">{{ comments_panel(institute, case, current_user, comments) }}</div>
185
- </div>
142
+ </div>
186
143
 
187
- <div class="row">
188
- <div class="col-sm-12">
189
- {{ insert_multi_image_panel() }}
144
+ <div class="row mt-3">
145
+ <div class="col-6">{{ synopsis_panel() }}</div>
146
+ <div class="col-6">{{ comments_panel(institute, case, current_user, comments) }}</div>
190
147
  </div>
191
- </div>
192
148
 
193
- <div class="row">
194
- <div class="col-sm-12">
195
- {{ custom_image_panels() }}
149
+ <div class="row">
150
+ <div class="col-sm-12">
151
+ {{ insert_multi_image_panel() }}
152
+ </div>
196
153
  </div>
197
- </div>
198
154
 
199
- <!-- CASE DIAGNOSES AND PHENOTYPES -->
200
- <div class="panel-default">
201
- <div class="panel-heading"><span class="fa fa-user-md"></span>&nbsp;Phenotypes & diagnoses</div>
202
155
  <div class="row">
203
- <div class="col-sm-6 ">
204
- <div class="card h-100">
205
- <div class="card-body">
206
- {{ hpo_panel(case, institute, config) }}
156
+ <div class="col-sm-12">
157
+ {{ custom_image_panels() }}
158
+ </div>
159
+ </div>
160
+
161
+ <!-- CASE DIAGNOSES AND PHENOTYPES -->
162
+ <div class="panel-default">
163
+ <div class="panel-heading"><span class="fa fa-user-md"></span>&nbsp;Phenotypes & diagnoses</div>
164
+ <div class="row">
165
+ <div class="col-sm-6 ">
166
+ <div class="card h-100">
167
+ <div class="card-body">
168
+ {{ hpo_panel(case, institute, config) }}
169
+ </div>
207
170
  </div>
208
171
  </div>
209
- </div>
210
- <div class="col-sm-6">
211
- <div class="card h-100">
212
- <div class="card-body">
213
- {{ hpo_genelist_panel(case, institute, config) }}
172
+ <div class="col-sm-6">
173
+ <div class="card h-100">
174
+ <div class="card-body">
175
+ {{ hpo_genelist_panel(case, institute, config) }}
176
+ </div>
214
177
  </div>
215
178
  </div>
216
- </div>
217
- </div> <!--end of <div class="row">-->
218
- </div>
179
+ </div> <!--end of row>-->
180
+ </div> <!--end of card panel-default -->
219
181
 
220
182
  <!-- diagnoses-related code-->
221
183
  {% if not case.track == 'cancer' %}
@@ -259,8 +221,8 @@
259
221
  {{ reanalysis_modal(institute, case) }}
260
222
  {{ beacon_modal(institute, case) }}
261
223
  {{ matchmaker_modal(institute, case, suspects, mme_nodes) }}
262
- </div><!-- end of <div containter> -->
263
- </div><!-- end of <div col> -->
224
+ </div><!-- end of containter -->
225
+ </div><!-- end of col -->
264
226
  {% endmacro %}
265
227
 
266
228
  {% macro variants_buttons() %}
@@ -390,7 +352,7 @@
390
352
  <a href="{{ url_for('cases.case', institute_id=grouped_case.owner, case_name=grouped_case.display_name) }}">{{ grouped_case.display_name }}</a>
391
353
 
392
354
  <a href="{{ url_for('cases.remove_case_group', institute_id=institute._id, case_name=grouped_case.display_name, case_group=group_id) }}" class="btn btn-link btn-sm">
393
- <span class="fa fa-remove text-dark"></span></a>
355
+ <span class="fa fa-times text-dark"></span></a>
394
356
 
395
357
  </div>
396
358
  {% endfor %}
@@ -398,7 +360,7 @@
398
360
  </div>
399
361
  {% endfor %}
400
362
  {% endif %}
401
- </div> <!-- end of <div class="list-group" style="max-height:200px; overflow-y: scroll;" -->
363
+ </div> <!-- end of list-group -->
402
364
  </div>
403
365
  </div>
404
366
  {% endmacro %}
@@ -574,6 +536,51 @@
574
536
  </div>
575
537
  {% endmacro %}
576
538
 
539
+ {% macro matching_variants() %}
540
+ <div class="card mt-3">
541
+ <div class="row mt-0 ms-3">
542
+ <div class="col-sm-12 col-md-12">
543
+ <div data-bs-toggle='tooltip' class="panel-heading" title="Check if there are any variants in this case
544
+ marked as causative in another case for this institute, or are on the managed variants list.">
545
+ <strong>
546
+ {% if hide_matching == false %}
547
+ <a href="{{ url_for('cases.case', institute_id=institute._id, case_name=case.display_name, hide_matching='True') }}" class="text-body"><span class="me-1 fa fa-caret-down"></span>Search for matching causatives and managed variants</a>
548
+ {% else %}
549
+ <a href="{{ url_for('cases.case', institute_id=institute._id, case_name=case.display_name, hide_matching='False') }}" class="text-body"><span class="me-1 fa fa-caret-right"></span>Search for matching causatives and managed variants</a>
550
+ {% endif %}
551
+ </strong>
552
+ </div>
553
+ </div>
554
+ {% if hide_matching == false %}
555
+ {% if other_causatives|length > 0 %}
556
+ <div class="row mt-0 ms-3">
557
+ <div class="col-xs-12 col-md-12">{{ matching_causatives(other_causatives, institute, case) }}</div>
558
+ </div>
559
+ {% endif %}
560
+ {% if default_other_causatives|length > 0%}
561
+ <div class="row mt-0 ms-3">
562
+ <div class="col-xs-12 col-md-12">{{ matching_causatives(default_other_causatives, institute, case, default=True) }}</div>
563
+ </div>
564
+ {% endif %}
565
+ {% if managed_variants|length > 0%}
566
+ <div class="row mt-0 ms-3">
567
+ <div class="col-sm-12 col-md-12">{{ matching_managed_variants(managed_variants, institute, case) }}</div>
568
+ </div>
569
+ {% endif %}
570
+ {% if default_managed_variants|length > 0%}
571
+ <div class="row mt-0 ms-3">
572
+ <div class="col-sm-12 col-md-12">{{ matching_managed_variants(default_managed_variants, institute, case, default=True) }}</div>
573
+ </div>
574
+ {% endif %}
575
+ {% if other_causatives|length == 0 and default_other_causatives|length == 0 and managed_variants|length == 0 and default_managed_variants|length == 0%}
576
+ <div class="row mt-0 ms-3">
577
+ <div class="col-sm-12 col-md-12">No matching causatives or managed variants found</div>
578
+ </div>
579
+ {% endif %}
580
+ {% endif %}
581
+ </div>
582
+ </div>
583
+ {% endmacro %}
577
584
 
578
585
  {% block scripts %}
579
586
  {{ super() }}
@@ -247,7 +247,7 @@
247
247
  {% else %}
248
248
  <button type="submit" name="action" value="DELETE" class="btn btn-warning btn-xs form-control">
249
249
  {% endif %}
250
- <span class="fa fa-remove"></span>
250
+ <span class="fa fa-times"></span>
251
251
  {{ user.name }}
252
252
  </button>
253
253
  </form>