scout-browser 4.101.0__py3-none-any.whl → 4.103.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. scout/adapter/mongo/case.py +26 -122
  2. scout/adapter/mongo/clinvar.py +98 -32
  3. scout/adapter/mongo/event.py +0 -47
  4. scout/adapter/mongo/hgnc.py +7 -2
  5. scout/adapter/mongo/omics_variant.py +8 -0
  6. scout/adapter/mongo/variant_loader.py +12 -4
  7. scout/build/variant/variant.py +1 -0
  8. scout/commands/load/variants.py +1 -1
  9. scout/commands/update/user.py +87 -49
  10. scout/constants/__init__.py +4 -0
  11. scout/constants/clinvar.py +10 -0
  12. scout/constants/igv_tracks.py +6 -2
  13. scout/constants/phenotype.py +1 -0
  14. scout/constants/variant_tags.py +18 -0
  15. scout/demo/NIST.trgt.stranger.vcf.gz +0 -0
  16. scout/demo/NIST.trgt.stranger.vcf.gz.tbi +0 -0
  17. scout/demo/__init__.py +1 -0
  18. scout/load/hpo.py +8 -2
  19. scout/models/clinvar.py +86 -0
  20. scout/parse/variant/coordinates.py +5 -1
  21. scout/parse/variant/gene.py +5 -9
  22. scout/parse/variant/genotype.py +66 -42
  23. scout/parse/variant/variant.py +2 -0
  24. scout/server/app.py +71 -2
  25. scout/server/blueprints/alignviewers/controllers.py +8 -6
  26. scout/server/blueprints/alignviewers/templates/alignviewers/igv_viewer.html +4 -0
  27. scout/server/blueprints/alignviewers/templates/alignviewers/utils.html +1 -1
  28. scout/server/blueprints/cases/controllers.py +57 -29
  29. scout/server/blueprints/cases/templates/cases/case_report.html +28 -90
  30. scout/server/blueprints/cases/templates/cases/matchmaker.html +1 -1
  31. scout/server/blueprints/cases/templates/cases/phenotype.html +1 -1
  32. scout/server/blueprints/cases/templates/cases/utils.html +34 -53
  33. scout/server/blueprints/cases/views.py +32 -33
  34. scout/server/blueprints/clinvar/controllers.py +235 -54
  35. scout/server/blueprints/clinvar/form.py +38 -1
  36. scout/server/blueprints/clinvar/static/form_style.css +8 -1
  37. scout/server/blueprints/clinvar/templates/clinvar/clinvar_onc_submissions.html +200 -0
  38. scout/server/blueprints/clinvar/templates/clinvar/clinvar_submissions.html +3 -2
  39. scout/server/blueprints/clinvar/templates/clinvar/components.html +198 -0
  40. scout/server/blueprints/clinvar/templates/clinvar/multistep_add_onc_variant.html +187 -0
  41. scout/server/blueprints/clinvar/templates/clinvar/multistep_add_variant.html +9 -348
  42. scout/server/blueprints/clinvar/templates/clinvar/scripts.html +193 -0
  43. scout/server/blueprints/clinvar/views.py +90 -13
  44. scout/server/blueprints/diagnoses/controllers.py +4 -8
  45. scout/server/blueprints/diagnoses/templates/diagnoses/diagnoses.html +1 -1
  46. scout/server/blueprints/diagnoses/templates/diagnoses/disease_term.html +1 -1
  47. scout/server/blueprints/diagnoses/views.py +2 -2
  48. scout/server/blueprints/institutes/controllers.py +148 -75
  49. scout/server/blueprints/institutes/forms.py +1 -0
  50. scout/server/blueprints/institutes/templates/overview/cases.html +1 -1
  51. scout/server/blueprints/institutes/templates/overview/gene_variants.html +15 -6
  52. scout/server/blueprints/institutes/templates/overview/institute_sidebar.html +28 -2
  53. scout/server/blueprints/institutes/templates/overview/utils.html +1 -1
  54. scout/server/blueprints/institutes/views.py +17 -4
  55. scout/server/blueprints/login/controllers.py +2 -1
  56. scout/server/blueprints/login/views.py +5 -2
  57. scout/server/blueprints/mme/templates/mme/mme_submissions.html +2 -2
  58. scout/server/blueprints/omics_variants/templates/omics_variants/outliers.html +2 -2
  59. scout/server/blueprints/omics_variants/views.py +2 -2
  60. scout/server/blueprints/phenotypes/controllers.py +15 -2
  61. scout/server/blueprints/phenotypes/templates/phenotypes/hpo_terms.html +1 -1
  62. scout/server/blueprints/variant/controllers.py +11 -12
  63. scout/server/blueprints/variant/templates/variant/cancer-variant.html +2 -1
  64. scout/server/blueprints/variant/templates/variant/components.html +0 -1
  65. scout/server/blueprints/variant/templates/variant/sv-variant.html +2 -1
  66. scout/server/blueprints/variant/templates/variant/utils.html +1 -1
  67. scout/server/blueprints/variant/templates/variant/variant.html +2 -2
  68. scout/server/blueprints/variant/templates/variant/variant_details.html +100 -84
  69. scout/server/blueprints/variant/utils.py +25 -0
  70. scout/server/blueprints/variants/controllers.py +11 -42
  71. scout/server/blueprints/variants/templates/variants/cancer-variants.html +5 -3
  72. scout/server/blueprints/variants/templates/variants/str-variants.html +4 -1
  73. scout/server/blueprints/variants/templates/variants/sv-variants.html +3 -3
  74. scout/server/blueprints/variants/templates/variants/utils.html +4 -0
  75. scout/server/blueprints/variants/templates/variants/variants.html +4 -4
  76. scout/server/blueprints/variants/views.py +9 -8
  77. scout/server/config.py +3 -0
  78. scout/server/extensions/beacon_extension.py +7 -2
  79. scout/server/extensions/clinvar_extension.py +2 -2
  80. scout/server/templates/bootstrap_global.html +11 -1
  81. scout/server/templates/layout.html +6 -1
  82. scout/server/utils.py +24 -3
  83. {scout_browser-4.101.0.dist-info → scout_browser-4.103.0.dist-info}/METADATA +1 -1
  84. {scout_browser-4.101.0.dist-info → scout_browser-4.103.0.dist-info}/RECORD +87 -81
  85. {scout_browser-4.101.0.dist-info → scout_browser-4.103.0.dist-info}/WHEEL +0 -0
  86. {scout_browser-4.101.0.dist-info → scout_browser-4.103.0.dist-info}/entry_points.txt +0 -0
  87. {scout_browser-4.101.0.dist-info → scout_browser-4.103.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,72 +1,110 @@
1
1
  import logging
2
+ from typing import List, Optional, Tuple
2
3
 
3
4
  import click
4
5
  from flask.cli import with_appcontext
5
6
 
7
+ from scout.adapter import MongoAdapter
6
8
  from scout.server.extensions import store
7
9
 
8
10
  LOG = logging.getLogger(__name__)
11
+ USER_ROLES = ["admin", "mme_submitter", "beacon_submitter"]
9
12
 
10
13
 
11
14
  @click.command("user", short_help="Update a user")
12
- @click.option("--user-id", "-u", help="A email adress that identifies the user", required=True)
13
- @click.option(
14
- "--update-role",
15
- "-r",
16
- # There will be more roles in the future
17
- type=click.Choice(["admin", "mme_submitter", "beacon_submitter"]),
18
- help="Add a role to the user",
19
- )
20
- @click.option("--remove-admin", is_flag=True, help="Remove admin rights from user")
21
- @click.option("--add-institute", "-i", multiple=True, help="Specify the institutes to add")
22
- @click.option("--remove-institute", multiple=True, help="Specify the institutes to remove")
15
+ @click.option("--user-id", "-u", required=True, help="An email address that identifies the user")
16
+ @click.option("--update-role", "-r", type=click.Choice(USER_ROLES), help="Add a role to the user")
17
+ @click.option("--remove-admin", is_flag=True, help="(Deprecated) Remove admin role from the user")
18
+ @click.option("--remove-role", multiple=True, help="Specify roles to remove from the user")
19
+ @click.option("--add-institute", "-i", multiple=True, help="Specify institutes to add")
20
+ @click.option("--remove-institute", multiple=True, help="Specify institutes to remove")
23
21
  @with_appcontext
24
- def user(user_id, update_role, add_institute, remove_admin, remove_institute):
25
- """
26
- Update a user in the database
27
- """
22
+ def user(
23
+ user_id: str,
24
+ update_role: Optional[str],
25
+ remove_admin: bool,
26
+ remove_role: Tuple[str, ...],
27
+ add_institute: Tuple[str, ...],
28
+ remove_institute: Tuple[str, ...],
29
+ ) -> None:
30
+ """Update roles and institutes for a user in the database."""
28
31
  adapter = store
29
-
30
32
  user_obj = adapter.user(user_id)
31
-
32
33
  if not user_obj:
33
34
  LOG.warning("User %s could not be found", user_id)
34
35
  raise click.Abort()
35
36
 
36
- existing_roles = set(user_obj.get("roles", []))
37
- if update_role:
38
- if not update_role in user_obj["roles"]:
39
- existing_roles = set(user_obj["roles"])
40
- existing_roles.add(update_role)
41
- LOG.info("Adding role %s to user", update_role)
37
+ user_obj["roles"] = process_roles(
38
+ current_roles=user_obj.get("roles", []),
39
+ add_role=update_role,
40
+ remove_roles=remove_role,
41
+ remove_admin=remove_admin,
42
+ user_id=user_id,
43
+ )
44
+ user_obj["institutes"] = process_institutes(
45
+ current_institutes=user_obj.get("institutes", []),
46
+ add_institutes=add_institute,
47
+ remove_institutes=remove_institute,
48
+ adapter=adapter,
49
+ user_id=user_id,
50
+ )
51
+ adapter.update_user(user_obj)
52
+
53
+
54
+ def process_roles(
55
+ current_roles: List[str],
56
+ add_role: Optional[str],
57
+ remove_roles: Tuple[str, ...],
58
+ remove_admin: bool,
59
+ user_id: str,
60
+ ) -> List[str]:
61
+ """Define the list of roles for a user in the database."""
62
+ roles = set(current_roles)
63
+
64
+ if add_role:
65
+ if add_role in roles:
66
+ LOG.warning("User already has role '%s'", add_role)
42
67
  else:
43
- LOG.warning("User already have role %s", update_role)
44
-
45
- if remove_admin:
46
- try:
47
- existing_roles.remove("admin")
48
- LOG.info("Removing admin rights from user %s", user_id)
49
- except KeyError as err:
50
- LOG.info("User %s does not have admin rights", user_id)
51
-
52
- user_obj["roles"] = list(existing_roles)
53
-
54
- existing_institutes = set(user_obj.get("institutes", []))
55
- for institute_id in add_institute:
56
- institute_obj = adapter.institute(institute_id)
57
- if not institute_obj:
58
- LOG.warning("Institute %s could not be found", institute_id)
68
+ roles.add(add_role)
69
+ LOG.info("Adding role '%s' to user '%s'", add_role, user_id)
70
+
71
+ all_remove = set(remove_roles)
72
+ if remove_admin and "admin" not in all_remove:
73
+ click.echo("⚠️ --remove-admin is deprecated. Use --remove-role admin instead.")
74
+ all_remove.add("admin")
75
+
76
+ for role in all_remove:
77
+ if role in roles:
78
+ roles.remove(role)
79
+ LOG.info("Removing role '%s' from user '%s'", role, user_id)
59
80
  else:
60
- existing_institutes.add(institute_id)
61
- LOG.info("Adding institute %s to user", institute_id)
81
+ LOG.info("User does not have role '%s'", role)
62
82
 
63
- for institute_id in remove_institute:
64
- try:
65
- existing_institutes.remove(institute_id)
66
- LOG.info("Removing institute %s from user", institute_id)
67
- except KeyError as err:
68
- LOG.info("User does not have access to institute %s", institute_id)
83
+ return list(roles)
69
84
 
70
- user_obj["institutes"] = list(existing_institutes)
71
85
 
72
- adapter.update_user(user_obj)
86
+ def process_institutes(
87
+ current_institutes: List[str],
88
+ add_institutes: Tuple[str, ...],
89
+ remove_institutes: Tuple[str, ...],
90
+ adapter: MongoAdapter,
91
+ user_id: str,
92
+ ) -> List[str]:
93
+ """Define the list of institutes for a user in the database."""
94
+ institutes = set(current_institutes)
95
+
96
+ for inst in add_institutes:
97
+ if adapter.institute(inst):
98
+ institutes.add(inst)
99
+ LOG.info("Adding institute '%s' to user '%s'", inst, user_id)
100
+ else:
101
+ LOG.warning("Institute '%s' could not be found", inst)
102
+
103
+ for inst in remove_institutes:
104
+ if inst in institutes:
105
+ institutes.remove(inst)
106
+ LOG.info("Removing institute '%s' from user '%s'", inst, user_id)
107
+ else:
108
+ LOG.info("User does not have access to institute '%s'", inst)
109
+
110
+ return list(institutes)
@@ -36,6 +36,7 @@ from .clinvar import (
36
36
  CONDITION_PREFIX,
37
37
  GERMLINE_CLASSIF_TERMS,
38
38
  MULTIPLE_CONDITION_EXPLANATION,
39
+ ONCOGENIC_CLASSIF_TERMS,
39
40
  )
40
41
  from .clnsig import CLINSIG_MAP, ONC_CLNSIG, REV_CLINSIG_MAP, TRUSTED_REVSTAT_LEVEL
41
42
  from .disease_parsing import (
@@ -78,6 +79,7 @@ from .indexes import ID_PROJECTION, INDEXES
78
79
  from .panels import PANELAPP_CONFIDENCE_EXCLUDE
79
80
  from .phenotype import (
80
81
  COHORT_TAGS,
82
+ HPO_LINK_URL,
81
83
  HPO_URL,
82
84
  HPOTERMS_URL,
83
85
  ORPHA_URLS,
@@ -99,8 +101,10 @@ from .variant_tags import (
99
101
  MANUAL_RANK_OPTIONS,
100
102
  MOSAICISM_OPTIONS,
101
103
  OUTLIER_TYPES,
104
+ REVEL_SCORE_LABEL_COLOR_MAP,
102
105
  SPIDEX_HUMAN,
103
106
  SPIDEX_LEVELS,
107
+ SPLICEAI_SCORE_LABEL_COLOR_MAP,
104
108
  SV_TYPES,
105
109
  VARIANT_CALL,
106
110
  VARIANT_FILTERS,
@@ -1,8 +1,12 @@
1
+ from scout.constants.clnsig import ONC_CLNSIG
2
+
1
3
  CLINVAR_API_URL_DEFAULT = "https://submit.ncbi.nlm.nih.gov/api/v1/submissions/"
2
4
  PRECLINVAR_URL = "https://preclinvar.scilifelab.se"
3
5
 
4
6
  ASSERTION_METHOD = "ACMG Guidelines, 2015"
5
7
  ASSERTION_METHOD_CIT = "PMID:25741868"
8
+ ASSERTION_ONC_ONC_DB = "PubMed"
9
+ ASSERTION_CRITERIA_ONC_ID = "36063163"
6
10
  NOT_PROVIDED = "not provided"
7
11
 
8
12
  # Header used to create the Variant .CSV file for the manual ClinVar submission
@@ -95,6 +99,8 @@ GERMLINE_CLASSIF_TERMS = [
95
99
  NOT_PROVIDED,
96
100
  ]
97
101
 
102
+ ONCOGENIC_CLASSIF_TERMS = ONC_CLNSIG
103
+
98
104
  REVSTAT_TERMS = {
99
105
  "conflicting_interpretations",
100
106
  "multiple_submitters",
@@ -187,6 +193,10 @@ CONDITION_PREFIX = {
187
193
  "Orphanet": "ORPHA",
188
194
  }
189
195
 
196
+ CONDITION_DBS_API = ["HP", "MedGen", "MeSH", "MONDO", "OMIM", "Orphanet"]
197
+
190
198
  CLINVAR_ASSERTION_METHOD_CIT_DB_OPTIONS = {"DOI", "pmc", "PMID"}
199
+ CITATION_DBS_API = ["PubMed", "BookShelf", "DOI", "pmc"]
191
200
 
192
201
  MULTIPLE_CONDITION_EXPLANATION = ["Novel disease", "Uncertain", "Co-occurring"]
202
+ PRESENCE_IN_NORMAL_TISSUE = ["present", "absent", "not tested"]
@@ -4,11 +4,15 @@ HG19REF_URL = (
4
4
  )
5
5
  HG19REF_INDEX_URL = "https://s3.amazonaws.com/igv.broadinstitute.org/genomes/seq/1kg_v37/human_g1k_v37_decoy.fasta.fai"
6
6
  HG19CYTOBAND_URL = "https://raw.githubusercontent.com/Clinical-Genomics/reference-files/refs/heads/master/rare-disease/region/grch37_cytoband.bed"
7
- HG19ALIAS_URL = "https://igv.org/genomes/data/hg19/hg19_alias.tab"
7
+ HG19ALIAS_URL = (
8
+ "https://raw.githubusercontent.com/igvteam/igv-data/refs/heads/main/data/hg19/hg19_alias.tab"
9
+ )
8
10
 
9
11
  HG38REF_URL = "https://s3.amazonaws.com/igv.broadinstitute.org/genomes/seq/hg38/hg38.fa"
10
12
  HG38REF_INDEX_URL = "https://s3.amazonaws.com/igv.broadinstitute.org/genomes/seq/hg38/hg38.fa.fai"
11
- HG38ALIAS_URL = "https://igv.org/genomes/data/hg38/hg38_alias.tab"
13
+ HG38ALIAS_URL = (
14
+ "https://raw.githubusercontent.com/igvteam/igv-data/refs/heads/main/data/hg38/hg38_alias.tab"
15
+ )
12
16
  HG38CYTOBAND_URL = "https://igv-genepattern-org.s3.amazonaws.com/genomes/hg38/cytoBandIdeo.txt.gz"
13
17
 
14
18
  HG38GENES_URL = "https://hgdownload.soe.ucsc.edu/goldenPath/hg38/database/ncbiRefSeq.txt.gz"
@@ -2,6 +2,7 @@ HPO_URL = "http://purl.obolibrary.org/obo/hp/hpoa/{}"
2
2
  HPOTERMS_URL = (
3
3
  "https://raw.githubusercontent.com/obophenotype/human-phenotype-ontology/master/hp.obo"
4
4
  )
5
+ HPO_LINK_URL = "https://hpo.jax.org/browse/term/"
5
6
 
6
7
  ORPHA_URLS = {
7
8
  "orpha_to_hpo": "https://www.orphadata.com/data/xml/en_product4.xml",
@@ -19,6 +19,22 @@ CONSERVATION = {
19
19
  "phylop": {"conserved_min": 2.5, "conserved_max": 100},
20
20
  }
21
21
 
22
+ REVEL_SCORE_LABEL_COLOR_MAP = {
23
+ (0.932, 1.000): {"label": "Strong pathogenic", "color": "danger"},
24
+ (0.773, 0.932): {"label": "Moderate pathogenic", "color": "red"},
25
+ (0.644, 0.773): {"label": "Supporting pathogenic", "color": "orange"},
26
+ (0.290, 0.644): {"label": "Uncertain significance", "color": "secondary"},
27
+ (0.183, 0.290): {"label": "Supporting benign", "color": "info"},
28
+ (0.016, 0.183): {"label": "Moderate benign", "color": "info"},
29
+ (0.0, 0.016): {"label": "Strong to very strong benign", "color": "success"},
30
+ }
31
+
32
+ SPLICEAI_SCORE_LABEL_COLOR_MAP = {
33
+ (0.20, 1.00): {"label": "Predicted impact on splicing", "color": "warning"},
34
+ (0.10, 0.20): {"label": "Not informative", "color": "secondary"},
35
+ (0.00, 0.10): {"label": "No impact on splicing", "color": "success"},
36
+ }
37
+
22
38
  FEATURE_TYPES = (
23
39
  "exonic",
24
40
  "splicing",
@@ -535,6 +551,7 @@ CALLERS = {
535
551
  {"id": "deepvariant", "name": "DeepVariant"},
536
552
  _FREEBAYES,
537
553
  _GATK,
554
+ {"id": "mutect2", "name": "MuTect2"},
538
555
  {"id": "samtools", "name": "SAMtools"},
539
556
  ],
540
557
  "cancer": [
@@ -562,6 +579,7 @@ CALLERS = {
562
579
  {"id": "cnvpytor", "name": "CNVpytor"},
563
580
  {"id": "delly", "name": "Delly"},
564
581
  _GATK,
582
+ {"id": "gcnvcaller", "name": "GATK GermlineCNV"},
565
583
  {"id": "hificnv", "name": "HiFiCNV"},
566
584
  _MANTA,
567
585
  {"id": "severus", "name": "Severus"},
Binary file
Binary file
scout/demo/__init__.py CHANGED
@@ -25,6 +25,7 @@ gene_fusion_report_path = str(files(BASE_PATH).joinpath("draw-fusions-example.pd
25
25
  clinical_snv_path = str(files(BASE_PATH).joinpath("643594.clinical.vcf.gz"))
26
26
  clinical_sv_path = str(files(BASE_PATH).joinpath("643594.clinical.SV.vcf.gz"))
27
27
  clinical_str_path = str(files(BASE_PATH).joinpath("643594.clinical.str.stranger.vcf.gz"))
28
+ str_trgt_path = str(files(BASE_PATH).joinpath("NIST.trgt.stranger.vcf.gz"))
28
29
  clinical_fusion_path = str(files(BASE_PATH).joinpath("fusion_data.vcf"))
29
30
  customannotation_snv_path = str(files(BASE_PATH).joinpath("customannotations_one.vcf.gz"))
30
31
  vep_97_annotated_path = str(
scout/load/hpo.py CHANGED
@@ -1,6 +1,6 @@
1
1
  import logging
2
2
  from datetime import datetime
3
- from typing import Dict, Iterable, Optional
3
+ from typing import Iterable, Optional
4
4
 
5
5
  from click import progressbar
6
6
 
@@ -45,7 +45,13 @@ def load_hpo_terms(
45
45
  if not gene_info:
46
46
  continue
47
47
 
48
- hgnc_id = gene_info["true"]
48
+ if gene_info["true"] is not None:
49
+ hgnc_id = gene_info["true"]
50
+ elif len(gene_info["ids"]) == 1:
51
+ # unique aliases can be used
52
+ hgnc_id = list(gene_info["ids"])[0]
53
+ else:
54
+ continue
49
55
 
50
56
  if hpo_id not in hpo_terms:
51
57
  continue
scout/models/clinvar.py CHANGED
@@ -1,6 +1,12 @@
1
1
  from datetime import date, datetime
2
+ from enum import Enum
3
+ from typing import List, Literal, Optional
2
4
 
3
5
  from bson.objectid import ObjectId
6
+ from pydantic import BaseModel
7
+
8
+ from scout.constants import CHROMOSOMES
9
+ from scout.constants.clinvar import CITATION_DBS_API, ONCOGENIC_CLASSIF_TERMS
4
10
 
5
11
  """Model of the document that gets saved/updated in the clinvar_submission collection
6
12
  for each institute that has cases with ClinVar submission objects"""
@@ -76,3 +82,83 @@ clinvar_casedata = {
76
82
  "method_purpose": str, # default: "discovery"
77
83
  "reported_at": date,
78
84
  }
85
+
86
+ ### Models used for oncogenocity submissions via API
87
+
88
+
89
+ CitationDB = Enum("CitationDB", {db.upper(): db for db in CITATION_DBS_API})
90
+ OncogenicityClassificationDescription = Enum(
91
+ "OncogenicityClassificationDescription",
92
+ {term.upper().replace(" ", "_"): term for term in ONCOGENIC_CLASSIF_TERMS},
93
+ )
94
+
95
+
96
+ class Citation(BaseModel):
97
+ db: CitationDB
98
+ id: str
99
+
100
+
101
+ class OncogenicityClassification(BaseModel):
102
+ oncogenicityClassificationDescription: OncogenicityClassificationDescription
103
+ dateLastEvaluated: str
104
+ comment: Optional[str] = None
105
+ citation: Optional[List[Citation]] = None
106
+
107
+
108
+ class ObservedIn(BaseModel):
109
+ alleleOrigin: str
110
+ affectedStatus: str
111
+ collectionMethod: str
112
+ numberOfIndividuals: int
113
+ presenceOfSomaticVariantInNormalTissue: str
114
+ somaticVariantAlleleFraction: Optional[float] = None
115
+
116
+
117
+ class Gene(BaseModel):
118
+ symbol: str
119
+
120
+
121
+ Chromosome = Enum(
122
+ "Chromosome", {c: c for c in CHROMOSOMES}
123
+ ) # ClinVar API accepts only 'MT' chromosome
124
+
125
+
126
+ class Variant(BaseModel):
127
+ """It's defined by either coordinates or hgvs."""
128
+
129
+ alternateAllele: Optional[str] = None
130
+ assembly: Optional[Literal["GRCh37", "GRCh38"]] = None
131
+ chromosome: Optional[Chromosome] = None
132
+ gene: Optional[List[Gene]] = None
133
+ hgvs: Optional[str] = None
134
+ start: Optional[int] = None
135
+ stop: Optional[int] = None
136
+
137
+
138
+ class VariantSet(BaseModel):
139
+ variant: List[Variant]
140
+
141
+
142
+ class Condition(BaseModel):
143
+ db: Optional[str] = None
144
+ id: Optional[str] = None
145
+ name: Optional[str] = None
146
+
147
+
148
+ class ConditionSet(BaseModel):
149
+ condition: List[Condition]
150
+
151
+
152
+ class OncogenicitySubmissionItem(BaseModel):
153
+ # Field necessary for the API submissions:
154
+ recordStatus: str
155
+ oncogenicityClassification: OncogenicityClassification
156
+ observedIn: List[ObservedIn]
157
+ variantSet: VariantSet
158
+ conditionSet: ConditionSet
159
+
160
+ # Fields necessary to map the variant to a variant in Scout:
161
+ institute_id: str
162
+ case_id: str
163
+ case_name: str
164
+ variant_id: str
@@ -1,6 +1,6 @@
1
1
  """Code to parse variant coordinates"""
2
2
 
3
- from scout.constants import BND_ALT_PATTERN, CHR_PATTERN, CYTOBANDS_37, CYTOBANDS_38
3
+ from scout.constants import BND_ALT_PATTERN, CHR_PATTERN, CYTOBANDS_37, CYTOBANDS_38, SV_TYPES
4
4
 
5
5
 
6
6
  def get_cytoband_coordinates(chrom, pos, build):
@@ -144,6 +144,10 @@ def parse_coordinates(variant, category, build="37"):
144
144
  svtype = variant.INFO.get("SVTYPE")
145
145
  if svtype:
146
146
  svtype = svtype.lower()
147
+ else:
148
+ alt_type = alt.lstrip("<").rstrip(">").lower()
149
+ if alt_type in SV_TYPES:
150
+ svtype = alt_type
147
151
  sub_category = svtype
148
152
  if sub_category == "bnd":
149
153
  end_chrom = get_end_chrom(alt, chrom)
@@ -48,10 +48,6 @@ def parse_genes(transcripts):
48
48
  # List with all genes and their transcripts
49
49
  genes = []
50
50
 
51
- hgvs_identifier = None
52
- canonical_transcript = None
53
- exon = None
54
-
55
51
  # Loop over all genes
56
52
  for gene_id in genes_to_transcripts:
57
53
  # Get the transcripts for a gene
@@ -78,8 +74,7 @@ def parse_genes(transcripts):
78
74
  hgnc_symbol = transcript["hgnc_symbol"]
79
75
  if not hgvs_identifier:
80
76
  hgvs_identifier = transcript.get("coding_sequence_name")
81
- if not canonical_transcript:
82
- canonical_transcript = transcript["transcript_id"]
77
+
83
78
  if not exon:
84
79
  exon = transcript["exon"]
85
80
 
@@ -102,10 +97,11 @@ def parse_genes(transcripts):
102
97
  most_severe_spliceai_position = transcript["spliceai_delta_position"]
103
98
  spliceai_prediction = transcript["spliceai_prediction"]
104
99
 
105
- if transcript["is_canonical"] and transcript.get("coding_sequence_name"):
106
- hgvs_identifier = transcript.get("coding_sequence_name")
100
+ if transcript["is_canonical"]:
107
101
  canonical_transcript = transcript["transcript_id"]
108
- exon = transcript["exon"]
102
+ if transcript.get("coding_sequence_name"):
103
+ hgvs_identifier = transcript.get("coding_sequence_name")
104
+ exon = transcript["exon"]
109
105
 
110
106
  gene = {
111
107
  "transcripts": gene_transcripts,