scout-browser 4.98.0__py3-none-any.whl → 4.100.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 (91) hide show
  1. scout/adapter/mongo/case.py +30 -15
  2. scout/adapter/mongo/clinvar.py +23 -31
  3. scout/adapter/mongo/event.py +14 -4
  4. scout/adapter/mongo/institute.py +42 -55
  5. scout/adapter/mongo/omics_variant.py +14 -1
  6. scout/adapter/mongo/query.py +24 -1
  7. scout/adapter/mongo/variant.py +44 -22
  8. scout/adapter/mongo/variant_loader.py +169 -186
  9. scout/build/individual.py +5 -1
  10. scout/build/variant/variant.py +8 -0
  11. scout/commands/download/ensembl.py +18 -3
  12. scout/commands/load/research.py +2 -3
  13. scout/commands/update/individual.py +3 -0
  14. scout/commands/update/panelapp.py +15 -2
  15. scout/constants/__init__.py +6 -2
  16. scout/constants/clnsig.py +2 -0
  17. scout/constants/file_types.py +12 -0
  18. scout/constants/igv_tracks.py +9 -6
  19. scout/constants/indexes.py +5 -4
  20. scout/constants/panels.py +3 -0
  21. scout/constants/query_terms.py +1 -0
  22. scout/constants/variant_tags.py +6 -6
  23. scout/demo/643594.config.yaml +1 -0
  24. scout/load/panelapp.py +11 -5
  25. scout/models/case/case.py +1 -0
  26. scout/models/case/case_loading_models.py +7 -1
  27. scout/parse/ensembl.py +8 -3
  28. scout/parse/variant/clnsig.py +38 -0
  29. scout/parse/variant/genotype.py +4 -10
  30. scout/parse/variant/models.py +5 -11
  31. scout/parse/variant/rank_score.py +5 -13
  32. scout/parse/variant/variant.py +90 -111
  33. scout/server/app.py +39 -22
  34. scout/server/blueprints/alignviewers/controllers.py +29 -10
  35. scout/server/blueprints/alignviewers/templates/alignviewers/igv_viewer.html +51 -11
  36. scout/server/blueprints/cases/controllers.py +9 -3
  37. scout/server/blueprints/cases/templates/cases/case_report.html +25 -13
  38. scout/server/blueprints/cases/templates/cases/chanjo2_form.html +1 -1
  39. scout/server/blueprints/cases/templates/cases/collapsible_actionbar.html +1 -1
  40. scout/server/blueprints/cases/templates/cases/gene_panel.html +1 -1
  41. scout/server/blueprints/cases/templates/cases/utils.html +25 -6
  42. scout/server/blueprints/clinvar/controllers.py +34 -15
  43. scout/server/blueprints/clinvar/templates/clinvar/clinvar_submissions.html +34 -12
  44. scout/server/blueprints/clinvar/templates/clinvar/multistep_add_variant.html +14 -5
  45. scout/server/blueprints/clinvar/views.py +14 -2
  46. scout/server/blueprints/diagnoses/static/diagnoses.js +8 -1
  47. scout/server/blueprints/institutes/controllers.py +10 -2
  48. scout/server/blueprints/institutes/static/variants_list_scripts.js +9 -1
  49. scout/server/blueprints/institutes/templates/overview/institute_sidebar.html +9 -1
  50. scout/server/blueprints/login/controllers.py +112 -12
  51. scout/server/blueprints/login/views.py +38 -60
  52. scout/server/blueprints/mme/__init__.py +1 -0
  53. scout/server/blueprints/mme/controllers.py +18 -0
  54. scout/server/blueprints/mme/templates/mme/mme_submissions.html +153 -0
  55. scout/server/blueprints/mme/views.py +34 -0
  56. scout/server/blueprints/panels/templates/panels/panel.html +19 -6
  57. scout/server/blueprints/phenotypes/templates/phenotypes/hpo_terms.html +8 -1
  58. scout/server/blueprints/public/templates/public/index.html +5 -1
  59. scout/server/blueprints/variant/controllers.py +19 -10
  60. scout/server/blueprints/variant/templates/variant/acmg.html +15 -2
  61. scout/server/blueprints/variant/templates/variant/cancer-variant.html +1 -1
  62. scout/server/blueprints/variant/templates/variant/components.html +38 -16
  63. scout/server/blueprints/variant/templates/variant/sv-variant.html +2 -2
  64. scout/server/blueprints/variant/templates/variant/utils.html +23 -11
  65. scout/server/blueprints/variant/templates/variant/variant.html +42 -1
  66. scout/server/blueprints/variant/views.py +12 -0
  67. scout/server/blueprints/variants/controllers.py +20 -3
  68. scout/server/blueprints/variants/forms.py +8 -3
  69. scout/server/blueprints/variants/templates/variants/components.html +34 -0
  70. scout/server/blueprints/variants/templates/variants/indicators.html +11 -13
  71. scout/server/blueprints/variants/templates/variants/mei-variants.html +8 -6
  72. scout/server/blueprints/variants/templates/variants/sv-variants.html +9 -7
  73. scout/server/blueprints/variants/templates/variants/utils.html +35 -34
  74. scout/server/blueprints/variants/templates/variants/variants.html +4 -25
  75. scout/server/config.py +8 -0
  76. scout/server/extensions/bionano_extension.py +0 -1
  77. scout/server/extensions/chanjo2_extension.py +54 -13
  78. scout/server/links.py +15 -0
  79. scout/server/static/bs_styles.css +34 -6
  80. scout/server/templates/utils.html +9 -10
  81. scout/server/utils.py +40 -5
  82. scout/utils/acmg.py +25 -26
  83. scout/utils/ensembl_biomart_clients.py +2 -1
  84. scout/utils/ensembl_rest_clients.py +25 -32
  85. scout/utils/hgvs.py +1 -1
  86. scout/utils/scout_requests.py +1 -3
  87. {scout_browser-4.98.0.dist-info → scout_browser-4.100.0.dist-info}/METADATA +10 -14
  88. {scout_browser-4.98.0.dist-info → scout_browser-4.100.0.dist-info}/RECORD +91 -87
  89. {scout_browser-4.98.0.dist-info → scout_browser-4.100.0.dist-info}/WHEEL +0 -0
  90. {scout_browser-4.98.0.dist-info → scout_browser-4.100.0.dist-info}/entry_points.txt +0 -0
  91. {scout_browser-4.98.0.dist-info → scout_browser-4.100.0.dist-info}/licenses/LICENSE +0 -0
@@ -23,6 +23,18 @@ ORDERED_FILE_TYPE_MAP = OrderedDict(
23
23
  ]
24
24
  )
25
25
 
26
+ DNA_SAMPLE_VARIANT_CATEGORIES = [
27
+ "snv",
28
+ "sv",
29
+ "mei",
30
+ "str",
31
+ "vcf_snv_mt",
32
+ "vcf_snv_research_mt",
33
+ "vcf_snv_research",
34
+ "vcf_sv_research_mt",
35
+ "vcf_sv_research",
36
+ "vcf_mei_research",
37
+ ]
26
38
 
27
39
  ORDERED_OMICS_FILE_TYPE_MAP = OrderedDict(
28
40
  [
@@ -11,7 +11,7 @@ HG38REF_INDEX_URL = "https://s3.amazonaws.com/igv.broadinstitute.org/genomes/seq
11
11
  HG38ALIAS_URL = "https://igv.org/genomes/data/hg38/hg38_alias.tab"
12
12
  HG38CYTOBAND_URL = "https://igv-genepattern-org.s3.amazonaws.com/genomes/hg38/cytoBandIdeo.txt.gz"
13
13
 
14
- HG38GENES_URL = "https://s3.amazonaws.com/igv.org.genomes/hg38/refGene.txt.gz"
14
+ HG38GENES_URL = "https://hgdownload.soe.ucsc.edu/goldenPath/hg38/database/ncbiRefSeq.txt.gz"
15
15
  HG38GENES_FORMAT = "refgene"
16
16
 
17
17
  HG19GENES_URL = "https://s3.amazonaws.com/igv.org.genomes/hg19/ncbiRefSeq.sorted.txt.gz"
@@ -124,11 +124,14 @@ HUMAN_GENES_38 = {
124
124
  }
125
125
 
126
126
  CASE_SPECIFIC_TRACKS = {
127
- "rhocall_bed": "Rhocall Zygosity",
128
- "rhocall_wig": "Rhocall Regions",
129
- "tiddit_coverage_wig": "TIDDIT Coverage",
130
- "upd_regions_bed": "UPD regions",
131
- "upd_sites_bed": "UPD sites",
127
+ "paraphase_alignments": "Paraphase Alignment",
128
+ "assembly_alignments": "de novo Assembly Alignment",
129
+ "minor_allele_frequency_wigs": "SV Caller Minor Allele Frequency",
130
+ "rhocall_beds": "Rhocall Zygosity",
131
+ "rhocall_wigs": "Rhocall Regions",
132
+ "tiddit_coverage_wigs": "SV Caller Coverage",
133
+ "upd_regions_beds": "UPD Regions",
134
+ "upd_sites_beds": "UPD Sites",
132
135
  }
133
136
 
134
137
  HUMAN_REFERENCE = {"37": HUMAN_REFERENCE_37, "38": HUMAN_REFERENCE_38}
@@ -99,20 +99,21 @@ INDEXES = {
99
99
  ("category", ASCENDING),
100
100
  ("case_id", ASCENDING),
101
101
  ("variant_type", ASCENDING),
102
- ("rank_score", ASCENDING),
102
+ ("rank_score", DESCENDING),
103
+ ("hgnc_ids", ASCENDING),
103
104
  ],
104
- name="category_caseid_varianttype_rankscore",
105
+ name="category_caseid_varianttype_rankscore_hgncids",
105
106
  ),
106
107
  IndexModel(
107
108
  [
109
+ ("chromosome", ASCENDING),
108
110
  ("case_id", ASCENDING),
109
111
  ("category", ASCENDING),
110
112
  ("variant_type", ASCENDING),
111
- ("chromosome", ASCENDING),
112
113
  ("start", ASCENDING),
113
114
  ("end", ASCENDING),
114
115
  ],
115
- name="caseid_category_chromosome_start_end",
116
+ name="chromosome_caseid_category_start_end",
116
117
  ),
117
118
  IndexModel(
118
119
  [("variant_id", ASCENDING), ("institute", ASCENDING)],
scout/constants/panels.py CHANGED
@@ -25,3 +25,6 @@ PRESELECTED_PANELAPP_PANEL_TYPE_SLUGS = [
25
25
  "gms-signed-off",
26
26
  "rare-disease-100k",
27
27
  ]
28
+
29
+ PANELAPPGREEN_NAME = "PANELAPP-GREEN"
30
+ PANELAPPGREEN_DISPLAY_NAME = "PanelApp Green Genes"
@@ -62,4 +62,5 @@ SECONDARY_CRITERIA = [
62
62
  "split_reads",
63
63
  "fusion_caller",
64
64
  "rank_score",
65
+ "clinsig_onc",
65
66
  ]
@@ -244,7 +244,7 @@ CANCER_TIER_OPTIONS = {
244
244
  "4": {
245
245
  "label": "Tier IV",
246
246
  "description": "Observed at high frequency in the population. No published evidence.",
247
- "label_class": "default",
247
+ "label_class": "success",
248
248
  },
249
249
  }
250
250
 
@@ -334,7 +334,7 @@ MANUAL_RANK_OPTIONS = OrderedDict(
334
334
  "label": "VUS",
335
335
  "name": "Unknown Significance",
336
336
  "description": "Variant of unknown significance",
337
- "label_class": "default",
337
+ "label_class": "primary",
338
338
  },
339
339
  ),
340
340
  (
@@ -361,7 +361,7 @@ MANUAL_RANK_OPTIONS = OrderedDict(
361
361
  "label": "RF",
362
362
  "name": "Risk Factor",
363
363
  "description": "Established risk allele - strong evidence for a small risk increase",
364
- "label_class": "default",
364
+ "label_class": "dark",
365
365
  },
366
366
  ),
367
367
  (
@@ -370,7 +370,7 @@ MANUAL_RANK_OPTIONS = OrderedDict(
370
370
  "label": "LRF",
371
371
  "name": "Likely Risk Factor",
372
372
  "description": "Likely risk allele - some evidence for a small risk increase",
373
- "label_class": "default",
373
+ "label_class": "dark",
374
374
  },
375
375
  ),
376
376
  (
@@ -379,7 +379,7 @@ MANUAL_RANK_OPTIONS = OrderedDict(
379
379
  "label": "URF",
380
380
  "name": "Uncertain Risk Factor",
381
381
  "description": "Uncertain risk allele - uncertain evidence for a small risk increase",
382
- "label_class": "default",
382
+ "label_class": "dark",
383
383
  },
384
384
  ),
385
385
  (
@@ -388,7 +388,7 @@ MANUAL_RANK_OPTIONS = OrderedDict(
388
388
  "label": "O",
389
389
  "name": "Other",
390
390
  "description": "Other, phenotype not related to disease",
391
- "label_class": "default",
391
+ "label_class": "dark",
392
392
  },
393
393
  ),
394
394
  ]
@@ -88,6 +88,7 @@ samples:
88
88
  splice_junctions_bed: scout/demo/ACC5963A1_lanes_1234_star_sorted_sj_filtered_sorted.bed.gz
89
89
  d4_file: scout/demo/ADM1059A3.d4
90
90
 
91
+
91
92
  custom_images:
92
93
  str_variants_images:
93
94
  - title: A png image
scout/load/panelapp.py CHANGED
@@ -11,7 +11,6 @@ from scout.parse.panelapp import parse_panelapp_panel
11
11
  from scout.server.extensions import panelapp
12
12
 
13
13
  LOG = logging.getLogger(__name__)
14
- PANEL_NAME = "PANELAPP-GREEN"
15
14
 
16
15
 
17
16
  def load_panelapp_panel(
@@ -72,7 +71,14 @@ def get_panelapp_genes(
72
71
  return genes
73
72
 
74
73
 
75
- def load_panelapp_green_panel(adapter: MongoAdapter, institute: str, force: bool, signed_off: bool):
74
+ def load_panelapp_green_panel(
75
+ adapter: MongoAdapter,
76
+ institute: str,
77
+ force: bool,
78
+ signed_off: bool,
79
+ panel_id: str,
80
+ panel_display_name: str,
81
+ ):
76
82
  """Load/Update the panel containing all Panelapp Green genes."""
77
83
 
78
84
  def parse_types_filter(types_filter: str, available_types: List[str]) -> List[str]:
@@ -85,10 +91,10 @@ def load_panelapp_green_panel(adapter: MongoAdapter, institute: str, force: bool
85
91
  return [available_types[i] for i in index_list]
86
92
 
87
93
  # check and set panel version
88
- old_panel = adapter.gene_panel(panel_id=PANEL_NAME)
94
+ old_panel = adapter.gene_panel(panel_id=panel_id)
89
95
  green_panel = {
90
- "panel_name": PANEL_NAME,
91
- "display_name": "PanelApp Green Genes",
96
+ "panel_name": panel_id,
97
+ "display_name": panel_display_name,
92
98
  "institute": institute,
93
99
  "version": float(math.floor(old_panel["version"]) + 1) if old_panel else 1.0,
94
100
  "date": datetime.now(),
scout/models/case/case.py CHANGED
@@ -14,6 +14,7 @@ individual = dict(
14
14
  mother=str, # Individual id of mother
15
15
  capture_kits=list, # List of names of capture kits
16
16
  bam_file=str, # Path to bam file
17
+ minor_allele_frequency_wig=str, # Path to wig file
17
18
  rhocall_bed=str, # Path to bed file
18
19
  rhocall_wig=str, # Path to wig file
19
20
  tiddit_coverage_wig=str, # Path to wig file
@@ -25,9 +25,12 @@ LOG = logging.getLogger(__name__)
25
25
  REPID = "{REPID}"
26
26
 
27
27
  SAMPLES_FILE_PATH_CHECKS = [
28
+ "assembly_alignment_path",
28
29
  "bam_file",
29
30
  "d4_file",
31
+ "minor_allele_frequency_wig",
30
32
  "mitodel_file",
33
+ "paraphase_alignment_path",
31
34
  "rhocall_bed",
32
35
  "rhocall_wig",
33
36
  "rna_alignment_path",
@@ -201,6 +204,7 @@ class REViewer(BaseModel):
201
204
 
202
205
  class SampleLoader(BaseModel):
203
206
  alignment_path: Optional[str] = None
207
+ assembly_alignment_path: Optional[str] = None
204
208
  analysis_type: Literal[ANALYSIS_TYPES] = None
205
209
  bam_file: Optional[str] = ""
206
210
  bam_path: Optional[str] = None
@@ -216,11 +220,13 @@ class SampleLoader(BaseModel):
216
220
  individual_id: str = Field(alias="sample_id")
217
221
  is_sma: Optional[str] = None
218
222
  is_sma_carrier: Optional[str] = None
223
+ minor_allele_frequency_wig: Optional[str] = None
219
224
  mitodel_file: Optional[str] = None
220
225
  mitodel: Optional[Mitodel] = Mitodel()
221
226
  mother: Optional[str] = None
222
227
  msi: Optional[str] = None
223
228
  mt_bam: Optional[str] = None
229
+ paraphase_alignment_path: Optional[str] = None
224
230
  phenotype: Literal["affected", "unaffected", "unknown"]
225
231
  predicted_ancestry: Optional[str] = None
226
232
  reviewer: Optional[REViewer] = REViewer()
@@ -237,7 +243,7 @@ class SampleLoader(BaseModel):
237
243
  smn_27134_cn: Optional[int] = None
238
244
  splice_junctions_bed: Optional[str] = None
239
245
  subject_id: Optional[str] = None
240
- tiddit_coverage_wig: Optional[str] = None
246
+ tiddit_coverage_wig: Optional[str] = Field(None, alias="coverage_wig")
241
247
  tissue_type: Optional[str] = None
242
248
  tmb: Optional[str] = None
243
249
  tumor_purity: Optional[float] = 0.0
scout/parse/ensembl.py CHANGED
@@ -3,6 +3,8 @@
3
3
  import logging
4
4
  from typing import Any, Dict, List
5
5
 
6
+ from scout.utils.ensembl_biomart_clients import CHROM_SEPARATOR
7
+
6
8
  LOG = logging.getLogger(__name__)
7
9
 
8
10
 
@@ -120,8 +122,8 @@ def parse_ensembl_genes(lines):
120
122
  if index == 0:
121
123
  header = line.rstrip().split("\t")
122
124
  continue
123
- # After that each line represents a gene
124
-
125
+ elif line == CHROM_SEPARATOR:
126
+ continue
125
127
  yield parse_ensembl_line(line, header)
126
128
 
127
129
 
@@ -143,7 +145,8 @@ def parse_ensembl_transcripts(lines):
143
145
  # File allways start with a header line
144
146
  if index == 0:
145
147
  header = line.rstrip().split("\t")
146
- # After that each line represents a transcript
148
+ elif line == CHROM_SEPARATOR:
149
+ continue
147
150
  else:
148
151
  yield parse_ensembl_line(line, header)
149
152
 
@@ -165,6 +168,8 @@ def parse_ensembl_exons(lines):
165
168
  if index == 0:
166
169
  header = line.rstrip().split("\t")
167
170
  continue
171
+ elif line == CHROM_SEPARATOR:
172
+ continue
168
173
 
169
174
  exon_info = parse_ensembl_line(line, header)
170
175
 
@@ -3,9 +3,47 @@ from typing import Dict, List, Optional, Union
3
3
 
4
4
  import cyvcf2
5
5
 
6
+ from scout.constants.clnsig import ONC_CLNSIG
7
+
6
8
  LOG = logging.getLogger(__name__)
7
9
 
8
10
 
11
+ def split_groups(value: str) -> List[str]:
12
+ """Removes leading underscore from a string and splits it into a list of items."""
13
+ return [
14
+ item.lstrip("_").replace(" ", "_")
15
+ for group in value.replace("&", ",").split(",")
16
+ for item in group.split("/")
17
+ ]
18
+
19
+
20
+ def parse_clnsig_onc(variant: cyvcf2.Variant) -> List[dict]:
21
+ """Collect somatic oncogenicity ClinVar classifications for a variant, if available."""
22
+ if not variant.INFO.get("ONC"):
23
+ return []
24
+ acc = int(variant.INFO.get("CLNVID", 0))
25
+ onc_sig_groups = split_groups(value=variant.INFO.get("ONC", "").lower())
26
+ onc_revstat = ",".join(split_groups(value=variant.INFO.get("ONCREVSTAT", "").lower()))
27
+ onc_dn_groups = split_groups(variant.INFO.get("ONCDN", ""))
28
+
29
+ onc_clnsig_accessions = []
30
+
31
+ for i, onc_sig in enumerate(onc_sig_groups):
32
+ if (
33
+ onc_sig.capitalize() not in ONC_CLNSIG
34
+ ): # This is excluding entries with ONC=no_classification_for_the_single_variant
35
+ continue
36
+ onc_clnsig_accessions.append(
37
+ {
38
+ "accession": acc,
39
+ "value": onc_sig,
40
+ "revstat": onc_revstat,
41
+ "dn": onc_dn_groups[i].replace("|", ","),
42
+ }
43
+ )
44
+ return onc_clnsig_accessions
45
+
46
+
9
47
  def parse_clnsig_low_penetrance(sig_groups: List[str]) -> List[str]:
10
48
  """If 'low_penetrance' is among the clnsig terms of an array, the term gets appended to the term immediately before in the array."""
11
49
  result = []
@@ -27,16 +27,10 @@ GENOTYPE_MAP = {0: "0", 1: "1", -1: "."}
27
27
  LOG = logging.getLogger(__name__)
28
28
 
29
29
 
30
- def parse_genotypes(variant, individuals, individual_positions):
31
- """Parse the genotype calls for a variant
32
-
33
- Args:
34
- variant(cyvcf2.Variant)
35
- individuals: List[dict]
36
- individual_positions(dict)
37
- Returns:
38
- genotypes(list(dict)): A list of genotypes
39
- """
30
+ def parse_genotypes(
31
+ variant: cyvcf2.Variant, individuals: List[Dict], individual_positions: Dict
32
+ ) -> List[Dict]:
33
+ """Parse the genotype calls for a variant"""
40
34
  genotypes = []
41
35
  for ind in individuals:
42
36
  pos = individual_positions[ind["individual_id"]]
@@ -1,19 +1,13 @@
1
- def parse_genetic_models(models_info, case_id):
1
+ def parse_genetic_models(models_info: str, case_id: str) -> list:
2
2
  """Parse the genetic models entry of a vcf
3
3
 
4
- Args:
5
- models_info(str): The raw vcf information
6
- case_id(str)
7
-
8
- Returns:
9
- genetic_models(list)
10
-
4
+ Return inheritance patterns.
11
5
  """
12
6
  genetic_models = []
13
7
  if models_info:
14
8
  for family_info in models_info.split(","):
15
- splitted_info = family_info.split(":")
16
- if splitted_info[0] == case_id:
17
- genetic_models = splitted_info[1].split("|")
9
+ split_info = family_info.split(":")
10
+ if split_info[0] == case_id:
11
+ genetic_models = split_info[1].split("|")
18
12
 
19
13
  return genetic_models
@@ -1,17 +1,9 @@
1
- def parse_rank_score(rank_score_entry, case_id):
2
- """Parse the rank score
3
-
4
- Args:
5
- rank_score_entry(str): The raw rank score entry
6
- case_id(str)
7
-
8
- Returns:
9
- rank_score(float)
10
- """
1
+ def parse_rank_score(rank_score_entry: str, case_id: str) -> float:
2
+ """Parse the rank score from the raw rank score entry"""
11
3
  rank_score = None
12
4
  if rank_score_entry:
13
5
  for family_info in rank_score_entry.split(","):
14
- splitted_info = family_info.split(":")
15
- if case_id == splitted_info[0]:
16
- rank_score = float(splitted_info[1])
6
+ split_info = family_info.split(":")
7
+ if case_id == split_info[0]:
8
+ rank_score = float(split_info[1])
17
9
  return rank_score