scout-browser 4.98.0__py3-none-any.whl → 4.99.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 (54) hide show
  1. scout/adapter/mongo/institute.py +42 -55
  2. scout/adapter/mongo/variant.py +19 -15
  3. scout/adapter/mongo/variant_loader.py +11 -11
  4. scout/build/individual.py +2 -0
  5. scout/build/variant/variant.py +8 -0
  6. scout/commands/download/ensembl.py +18 -2
  7. scout/commands/update/individual.py +2 -0
  8. scout/commands/update/panelapp.py +15 -2
  9. scout/constants/__init__.py +6 -7
  10. scout/constants/clnsig.py +2 -0
  11. scout/constants/file_types.py +12 -0
  12. scout/constants/igv_tracks.py +8 -6
  13. scout/constants/panels.py +3 -0
  14. scout/constants/variant_tags.py +6 -6
  15. scout/demo/643594.config.yaml +1 -0
  16. scout/load/panelapp.py +11 -5
  17. scout/models/case/case_loading_models.py +4 -0
  18. scout/parse/variant/clnsig.py +38 -0
  19. scout/parse/variant/genotype.py +4 -10
  20. scout/parse/variant/models.py +5 -11
  21. scout/parse/variant/rank_score.py +5 -13
  22. scout/parse/variant/variant.py +90 -111
  23. scout/server/app.py +33 -22
  24. scout/server/blueprints/alignviewers/controllers.py +29 -10
  25. scout/server/blueprints/alignviewers/templates/alignviewers/igv_viewer.html +41 -11
  26. scout/server/blueprints/cases/templates/cases/utils.html +6 -6
  27. scout/server/blueprints/clinvar/controllers.py +29 -14
  28. scout/server/blueprints/clinvar/templates/clinvar/multistep_add_variant.html +13 -4
  29. scout/server/blueprints/clinvar/views.py +14 -2
  30. scout/server/blueprints/institutes/controllers.py +10 -2
  31. scout/server/blueprints/login/controllers.py +112 -12
  32. scout/server/blueprints/login/views.py +38 -60
  33. scout/server/blueprints/public/templates/public/index.html +5 -1
  34. scout/server/blueprints/variant/controllers.py +1 -1
  35. scout/server/blueprints/variant/templates/variant/acmg.html +6 -2
  36. scout/server/blueprints/variant/templates/variant/components.html +19 -0
  37. scout/server/blueprints/variant/templates/variant/utils.html +3 -3
  38. scout/server/blueprints/variants/controllers.py +10 -1
  39. scout/server/blueprints/variants/templates/variants/components.html +28 -0
  40. scout/server/blueprints/variants/templates/variants/mei-variants.html +8 -6
  41. scout/server/blueprints/variants/templates/variants/sv-variants.html +9 -7
  42. scout/server/blueprints/variants/templates/variants/utils.html +8 -12
  43. scout/server/blueprints/variants/templates/variants/variants.html +4 -25
  44. scout/server/config.py +8 -0
  45. scout/server/utils.py +22 -5
  46. scout/utils/acmg.py +25 -26
  47. scout/utils/ensembl_biomart_clients.py +1 -1
  48. scout/utils/ensembl_rest_clients.py +25 -32
  49. scout/utils/hgvs.py +1 -1
  50. {scout_browser-4.98.0.dist-info → scout_browser-4.99.0.dist-info}/METADATA +10 -14
  51. {scout_browser-4.98.0.dist-info → scout_browser-4.99.0.dist-info}/RECORD +54 -54
  52. {scout_browser-4.98.0.dist-info → scout_browser-4.99.0.dist-info}/WHEEL +0 -0
  53. {scout_browser-4.98.0.dist-info → scout_browser-4.99.0.dist-info}/entry_points.txt +0 -0
  54. {scout_browser-4.98.0.dist-info → scout_browser-4.99.0.dist-info}/licenses/LICENSE +0 -0
@@ -60,36 +60,51 @@ class InstituteHandler(object):
60
60
  ) -> Union[dict, str]:
61
61
  """Update the information for an institute."""
62
62
 
63
- add_groups = add_groups or False
63
+ def get_phenotype_groups() -> dict:
64
+ """Returns a dictionary with phenotype descriptions and abbreviations."""
65
+ existing_groups = (
66
+ institute_obj.get("phenotype_groups", PHENOTYPE_GROUPS) if add_groups else {}
67
+ )
68
+
69
+ if not phenotype_groups:
70
+ return existing_groups
71
+
72
+ group_abbreviations_list = list(group_abbreviations) if group_abbreviations else []
73
+
74
+ for i, hpo_term in enumerate(phenotype_groups):
75
+ hpo_obj = self.hpo_term(hpo_term)
76
+ if not hpo_obj:
77
+ continue
78
+
79
+ existing_groups[hpo_term] = {
80
+ "name": hpo_obj["description"],
81
+ "abbr": group_abbreviations_list[i] if group_abbreviations_list else None,
82
+ }
83
+
84
+ return existing_groups
85
+
64
86
  institute_obj = self.institute(internal_id)
65
87
  if not institute_obj:
66
88
  raise IntegrityError("Institute {} does not exist in database".format(internal_id))
67
89
 
68
- updates = {"$set": {}}
90
+ updates = {"$set": {}, "$unset": {}}
69
91
  updated_institute = institute_obj
70
92
 
71
93
  if sanger_recipient:
72
- user_obj = self.user(sanger_recipient)
73
- if not user_obj:
74
- raise IntegrityError("user {} does not exist in database".format(sanger_recipient))
75
-
76
- LOG.info(
77
- "Updating sanger recipients for institute: {0} with {1}".format(
78
- internal_id, sanger_recipient
79
- )
80
- )
81
- updates["$push"] = {"sanger_recipients": sanger_recipient}
94
+ old_recipients = institute_obj.get("sanger_recipients", [])
95
+ sanger_recipients = old_recipients + [sanger_recipient]
82
96
 
83
97
  if remove_sanger:
84
- LOG.info(
85
- "Removing sanger recipient {0} from institute: {1}".format(
86
- remove_sanger, internal_id
87
- )
98
+ sanger_recipients = list(
99
+ set(institute_obj.get("sanger_recipients", [])) - set([remove_sanger])
88
100
  )
89
- updates["$pull"] = {"sanger_recipients": remove_sanger}
90
101
 
91
- # Set a number of items
92
- GENERAL_SETTINGS = {
102
+ UPDATE_SETTINGS = {
103
+ "alamut_institution": alamut_institution, # Admin setting
104
+ "alamut_key": alamut_key, # Admin setting
105
+ "check_show_all_vars": check_show_all_vars is not None,
106
+ "clinvar_key": clinvar_key, # Admin setting
107
+ "clinvar_submitters": clinvar_submitters,
93
108
  "cohorts": cohorts,
94
109
  "collaborators": sharing_institutes,
95
110
  "coverage_cutoff": coverage_cutoff,
@@ -98,52 +113,24 @@ class InstituteHandler(object):
98
113
  "gene_panels": gene_panels,
99
114
  "gene_panels_matching": gene_panels_matching,
100
115
  "loqusdb_id": loqusdb_ids,
116
+ "phenotype_groups": get_phenotype_groups(),
101
117
  "sanger_recipients": sanger_recipients,
102
- "clinvar_submitters": clinvar_submitters,
103
- }
104
- for key, value in GENERAL_SETTINGS.items():
105
- if value not in [None, ""]:
106
- updates["$set"][key] = value
107
-
108
- if phenotype_groups is not None:
109
- if group_abbreviations:
110
- group_abbreviations = list(group_abbreviations)
111
- existing_groups = {}
112
- if add_groups:
113
- existing_groups = institute_obj.get("phenotype_groups", PHENOTYPE_GROUPS)
114
- for i, hpo_term in enumerate(phenotype_groups):
115
- hpo_obj = self.hpo_term(hpo_term)
116
- if not hpo_obj:
117
- return "Term {} does not exist in database".format(hpo_term)
118
- hpo_id = hpo_obj["hpo_id"]
119
- description = hpo_obj["description"]
120
- abbreviation = None
121
- if group_abbreviations:
122
- abbreviation = group_abbreviations[i]
123
- existing_groups[hpo_term] = {"name": description, "abbr": abbreviation}
124
- updates["$set"]["phenotype_groups"] = existing_groups
125
-
126
- ADMIN_SETTINGS = {
127
- "alamut_key": alamut_key,
128
- "alamut_institution": alamut_institution,
129
- "clinvar_key": clinvar_key,
130
- "show_all_cases_status": show_all_cases_status,
131
- "soft_filters": soft_filters,
118
+ "show_all_cases_status": show_all_cases_status, # Admin setting
119
+ "soft_filters": soft_filters, # Admin setting
132
120
  }
133
- for key, value in ADMIN_SETTINGS.items():
134
- if value not in [None, "", []]:
121
+ for key, value in UPDATE_SETTINGS.items():
122
+ if bool(value) is True:
135
123
  updates["$set"][key] = value
124
+ else:
125
+ updates["$unset"][key] = "" # Remove the key from the institute document
136
126
 
137
- updates["$set"]["check_show_all_vars"] = check_show_all_vars is not None
138
-
139
- if updates["$set"].keys() or updates.get("$push") or updates.get("$pull"):
127
+ if any(updates.get(op) for op in ["$set", "$unset"]):
140
128
  updates["$set"]["updated_at"] = datetime.now()
141
129
  updated_institute = self.institute_collection.find_one_and_update(
142
130
  {"_id": internal_id},
143
131
  updates,
144
132
  return_document=pymongo.ReturnDocument.AFTER,
145
133
  )
146
-
147
134
  LOG.info("Institute updated")
148
135
 
149
136
  return updated_institute
@@ -2,7 +2,7 @@
2
2
  # stdlib modules
3
3
  import logging
4
4
  import re
5
- from typing import Any
5
+ from typing import Any, Dict, Iterable
6
6
 
7
7
  # Third party modules
8
8
  import pymongo
@@ -689,25 +689,30 @@ class VariantHandler(VariantLoader):
689
689
  result = self.variant_collection.delete_many(query)
690
690
  LOG.info("{0} variants deleted".format(result.deleted_count))
691
691
 
692
- def overlapping(self, variant_obj, limit=30):
692
+ def hgnc_overlapping(self, variant_obj: dict, limit: int = None) -> Iterable[Dict]:
693
693
  """Return overlapping variants.
694
694
 
695
- Look at the genes that a variant overlaps to.
696
- Then return all variants that overlap these genes.
695
+ Look at the genes that a variant overlaps, then return all variants that overlap these genes.
697
696
 
698
- If variant_obj is sv it will return the overlapping snvs and oposite
699
- There is a problem when SVs are huge since there are to many overlapping variants.
697
+ The operation is slightly different depending on the category of the variants that we want to collect.
698
+ If variant_obj is an SV it will return the hgnc_id matching SVs, SNVs, and MEIs but
699
+ for SNVs we will only return the SVs and MEIs since the genmod compounds are way better.
700
700
 
701
- Args:
702
- variant_obj(dict)
701
+ Do not return the present variant as matching.
703
702
 
704
- Returns:
705
- variants(iterable(dict))
703
+ limit: A maximum count of returned variants is introduced: mainly this is a problem when SVs are huge since there can be many genes and overlapping variants.
704
+ We sort to offer the LIMIT most severe overlapping variants.
706
705
  """
707
- # This is the category of the variants that we want to collect
708
- category = "snv" if variant_obj["category"] == "sv" else "sv"
706
+ category = (
707
+ {"$in": ["sv", "mei"]}
708
+ if variant_obj["category"] == "snv"
709
+ else {"$in": ["sv", "snv", "mei", "cancer", "cancer_sv"]}
710
+ )
711
+
709
712
  variant_type = variant_obj.get("variant_type", "clinical")
710
713
  hgnc_ids = variant_obj["hgnc_ids"]
714
+ if not limit:
715
+ limit = 30 if variant_obj["category"] == "snv" else 45
711
716
 
712
717
  query = {
713
718
  "$and": [
@@ -715,13 +720,12 @@ class VariantHandler(VariantLoader):
715
720
  {"category": category},
716
721
  {"variant_type": variant_type},
717
722
  {"hgnc_ids": {"$in": hgnc_ids}},
723
+ {"variant_id": {"$ne": variant_obj["variant_id"]}},
718
724
  ]
719
725
  }
720
726
  sort_key = [("rank_score", pymongo.DESCENDING)]
721
- # We collect the 30 most severe overlapping variants
722
- variants = self.variant_collection.find(query).sort(sort_key).limit(limit)
723
727
 
724
- return variants
728
+ return self.variant_collection.find(query).sort(sort_key).limit(limit)
725
729
 
726
730
  def evaluated_variant_ids_from_events(self, case_id, institute_id):
727
731
  """Returns variant ids for variants that have been evaluated
@@ -621,16 +621,16 @@ class VariantLoader(object):
621
621
 
622
622
  def load_variants(
623
623
  self,
624
- case_obj,
625
- variant_type="clinical",
626
- category="snv",
627
- rank_threshold=None,
628
- chrom=None,
629
- start=None,
630
- end=None,
631
- gene_obj=None,
632
- custom_images=None,
633
- build="37",
624
+ case_obj: dict,
625
+ variant_type: str = "clinical",
626
+ category: str = "snv",
627
+ rank_threshold: float = None,
628
+ chrom: str = None,
629
+ start: int = None,
630
+ end: int = None,
631
+ gene_obj: dict = None,
632
+ custom_images: list = None,
633
+ build: str = "37",
634
634
  ):
635
635
  """Load variants for a case into scout.
636
636
 
@@ -675,7 +675,7 @@ class VariantLoader(object):
675
675
  )
676
676
 
677
677
  gene_to_panels = self.gene_to_panels(case_obj)
678
- genes = [gene_obj for gene_obj in self.all_genes(build=build)]
678
+ genes = list(self.all_genes(build=build))
679
679
  hgncid_to_gene = self.hgncid_to_gene(genes=genes, build=build)
680
680
  genomic_intervals = self.get_coding_intervals(genes=genes, build=build)
681
681
 
scout/build/individual.py CHANGED
@@ -6,9 +6,11 @@ from scout.exceptions import PedigreeError
6
6
 
7
7
  log = logging.getLogger(__name__)
8
8
  BUILD_INDIVIDUAL_FILES = [
9
+ "assembly_alignment_path",
9
10
  "bam_file",
10
11
  "d4_file",
11
12
  "mt_bam",
13
+ "paraphase_alignment_path",
12
14
  "rhocall_bed",
13
15
  "rhocall_wig",
14
16
  "rna_alignment_path",
@@ -1,5 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  import logging
3
+ from typing import List
3
4
 
4
5
  from scout.utils.convert import call_safe
5
6
  from scout.utils.dict_utils import remove_nonetype
@@ -115,6 +116,7 @@ def build_variant(
115
116
  revel_score = float, REVEL rankscore
116
117
  revel = float, REVEL score
117
118
  clnsig = list, # list of <clinsig>
119
+
118
120
  spidex = float,
119
121
 
120
122
  missing_data = bool, # default False
@@ -247,6 +249,7 @@ def build_variant(
247
249
  add_hgnc_symbols(variant_obj, variant_obj["hgnc_ids"], hgncid_to_gene)
248
250
  link_gene_panels(variant_obj, gene_to_panels)
249
251
  add_clnsig_objects(variant_obj, variant.get("clnsig", []))
252
+ add_clnsig_onc_objects(variant_obj, variant.get("clnsig_onc"))
250
253
 
251
254
  add_callers(variant_obj, variant.get("callers", {}))
252
255
 
@@ -337,6 +340,11 @@ def add_clnsig_objects(variant_obj, clnsig_list):
337
340
  variant_obj["clnsig"] = clnsig_objects
338
341
 
339
342
 
343
+ def add_clnsig_onc_objects(variant_obj: dict, onc_clnsig: List[dict]):
344
+ if onc_clnsig:
345
+ variant_obj["clnsig_onc"] = onc_clnsig
346
+
347
+
340
348
  def add_callers(variant_obj, call_info):
341
349
  """Add call_info to variant_obj
342
350
  Args: variant_obj (Dict)
@@ -8,9 +8,20 @@ import click
8
8
 
9
9
  from scout.utils.ensembl_biomart_clients import EnsemblBiomartHandler
10
10
 
11
+ CHROM_SEPARATOR = "[success]"
12
+ NR_EXPECTED_CHROMS = 24
13
+
11
14
  LOG = logging.getLogger(__name__)
12
15
 
13
16
 
17
+ def integrity_check(nr_chromosomes_in_file: int):
18
+ if nr_chromosomes_in_file < NR_EXPECTED_CHROMS:
19
+ raise BufferError(
20
+ "Ensembl resource does not seem to be complete. Please retry downloading genes/transcripts."
21
+ )
22
+ LOG.info("Integrity check OK.")
23
+
24
+
14
25
  def print_ensembl(
15
26
  out_dir: pathlib.Path, resource_type: List[str], genome_build: Optional[str] = None
16
27
  ):
@@ -31,14 +42,19 @@ def print_ensembl(
31
42
 
32
43
  file_name: str = f"ensembl_{resource_type}_{build}.txt"
33
44
  file_path = out_dir / file_name
45
+ nr_chroms_in_file = 0
34
46
 
35
47
  LOG.info("Print ensembl info %s to %s", build, file_path)
36
48
 
37
49
  with file_path.open("w", encoding="utf-8") as outfile:
38
50
  for line in ensembl_client.stream_resource(interval_type=resource_type):
39
- outfile.write(line + "\n")
51
+ if line.strip() == CHROM_SEPARATOR:
52
+ nr_chroms_in_file += 1
53
+ else:
54
+ outfile.write(line + "\n")
40
55
 
41
- LOG.info(f"{file_name} file saved to disk")
56
+ LOG.info(f"{file_name} file saved to disk.")
57
+ integrity_check(nr_chroms_in_file)
42
58
 
43
59
 
44
60
  @click.command("ensembl", help="Download files with ensembl info")
@@ -7,6 +7,7 @@ import click
7
7
  from scout.server.extensions import store
8
8
 
9
9
  UPDATE_DICT = {
10
+ "assembly_alignment_path": "path",
10
11
  "bam_file": "path",
11
12
  "bionano_access.sample": "str",
12
13
  "bionano_access.project": "str",
@@ -16,6 +17,7 @@ UPDATE_DICT = {
16
17
  "chromograph_images.upd_regions": "str",
17
18
  "chromograph_images.upd_sites": "str",
18
19
  "mt_bam": "path",
20
+ "paraphase_alignment_path": "path",
19
21
  "reviewer.alignment": "path",
20
22
  "reviewer.alignment_index": "path",
21
23
  "reviewer.vcf": "path",
@@ -5,6 +5,7 @@ import logging
5
5
  import click
6
6
  from flask.cli import current_app, with_appcontext
7
7
 
8
+ from scout.constants.panels import PANELAPPGREEN_DISPLAY_NAME, PANELAPPGREEN_NAME
8
9
  from scout.load.panelapp import load_panelapp_green_panel
9
10
  from scout.server.extensions import store
10
11
 
@@ -31,8 +32,15 @@ LOG = logging.getLogger(__name__)
31
32
  is_flag=True,
32
33
  help="Force update even if updated panel contains less genes",
33
34
  )
35
+ @click.option("--panel-id", help="Panel ID", default=PANELAPPGREEN_NAME, show_default=True)
36
+ @click.option(
37
+ "--panel-display-name",
38
+ help="Panel display name",
39
+ default=PANELAPPGREEN_DISPLAY_NAME,
40
+ show_default=True,
41
+ )
34
42
  @with_appcontext
35
- def panelapp_green(institute, force, signed_off):
43
+ def panelapp_green(institute, force, signed_off, panel_id, panel_display_name):
36
44
  """
37
45
  Update the automatically generated PanelApp Green Genes panel in the database.
38
46
  """
@@ -47,7 +55,12 @@ def panelapp_green(institute, force, signed_off):
47
55
 
48
56
  try:
49
57
  load_panelapp_green_panel(
50
- adapter=store, institute=institute, force=force, signed_off=signed_off
58
+ adapter=store,
59
+ institute=institute,
60
+ force=force,
61
+ signed_off=signed_off,
62
+ panel_id=panel_id,
63
+ panel_display_name=panel_display_name,
51
64
  )
52
65
  except Exception as err:
53
66
  LOG.error(err)
@@ -45,7 +45,11 @@ from .disease_parsing import (
45
45
  MIMNR_PATTERN,
46
46
  OMIM_STATUS_MAP,
47
47
  )
48
- from .file_types import ORDERED_FILE_TYPE_MAP, ORDERED_OMICS_FILE_TYPE_MAP
48
+ from .file_types import (
49
+ DNA_SAMPLE_VARIANT_CATEGORIES,
50
+ ORDERED_FILE_TYPE_MAP,
51
+ ORDERED_OMICS_FILE_TYPE_MAP,
52
+ )
49
53
  from .filters import (
50
54
  CLINICAL_FILTER_BASE,
51
55
  CLINICAL_FILTER_BASE_CANCER,
@@ -64,12 +68,7 @@ from .gene_tags import (
64
68
  PANEL_GENE_INFO_TRANSCRIPTS,
65
69
  UPDATE_GENES_RESOURCES,
66
70
  )
67
- from .igv_tracks import (
68
- CASE_SPECIFIC_TRACKS,
69
- HUMAN_REFERENCE,
70
- IGV_TRACKS,
71
- USER_DEFAULT_TRACKS,
72
- )
71
+ from .igv_tracks import CASE_SPECIFIC_TRACKS, HUMAN_REFERENCE, IGV_TRACKS, USER_DEFAULT_TRACKS
73
72
  from .indexes import ID_PROJECTION, INDEXES
74
73
  from .panels import PANELAPP_CONFIDENCE_EXCLUDE
75
74
  from .phenotype import (
scout/constants/clnsig.py CHANGED
@@ -65,3 +65,5 @@ TRUSTED_REVSTAT_LEVEL = [
65
65
  "guideline",
66
66
  "practice_guideline",
67
67
  ]
68
+
69
+ ONC_CLNSIG = ["Oncogenic", "Likely oncogenic", "Uncertain significance", "Likely benign", "Benign"]
@@ -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,13 @@ 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
+ "rhocall_beds": "Rhocall Zygosity",
130
+ "rhocall_wigs": "Rhocall Regions",
131
+ "tiddit_coverage_wigs": "TIDDIT Coverage",
132
+ "upd_regions_beds": "UPD regions",
133
+ "upd_sites_beds": "UPD sites",
132
134
  }
133
135
 
134
136
  HUMAN_REFERENCE = {"37": HUMAN_REFERENCE_37, "38": HUMAN_REFERENCE_38}
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"
@@ -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(),
@@ -25,9 +25,11 @@ 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",
30
31
  "mitodel_file",
32
+ "paraphase_alignment_path",
31
33
  "rhocall_bed",
32
34
  "rhocall_wig",
33
35
  "rna_alignment_path",
@@ -201,6 +203,7 @@ class REViewer(BaseModel):
201
203
 
202
204
  class SampleLoader(BaseModel):
203
205
  alignment_path: Optional[str] = None
206
+ assembly_alignment_path: Optional[str] = None
204
207
  analysis_type: Literal[ANALYSIS_TYPES] = None
205
208
  bam_file: Optional[str] = ""
206
209
  bam_path: Optional[str] = None
@@ -221,6 +224,7 @@ class SampleLoader(BaseModel):
221
224
  mother: Optional[str] = None
222
225
  msi: Optional[str] = None
223
226
  mt_bam: Optional[str] = None
227
+ paraphase_alignment_path: Optional[str] = None
224
228
  phenotype: Literal["affected", "unaffected", "unknown"]
225
229
  predicted_ancestry: Optional[str] = None
226
230
  reviewer: Optional[REViewer] = REViewer()
@@ -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 = []