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.
- scout/adapter/mongo/case.py +26 -122
- scout/adapter/mongo/clinvar.py +98 -32
- scout/adapter/mongo/event.py +0 -47
- scout/adapter/mongo/hgnc.py +7 -2
- scout/adapter/mongo/omics_variant.py +8 -0
- scout/adapter/mongo/variant_loader.py +12 -4
- scout/build/variant/variant.py +1 -0
- scout/commands/load/variants.py +1 -1
- scout/commands/update/user.py +87 -49
- scout/constants/__init__.py +4 -0
- scout/constants/clinvar.py +10 -0
- scout/constants/igv_tracks.py +6 -2
- scout/constants/phenotype.py +1 -0
- scout/constants/variant_tags.py +18 -0
- scout/demo/NIST.trgt.stranger.vcf.gz +0 -0
- scout/demo/NIST.trgt.stranger.vcf.gz.tbi +0 -0
- scout/demo/__init__.py +1 -0
- scout/load/hpo.py +8 -2
- scout/models/clinvar.py +86 -0
- scout/parse/variant/coordinates.py +5 -1
- scout/parse/variant/gene.py +5 -9
- scout/parse/variant/genotype.py +66 -42
- scout/parse/variant/variant.py +2 -0
- scout/server/app.py +71 -2
- scout/server/blueprints/alignviewers/controllers.py +8 -6
- scout/server/blueprints/alignviewers/templates/alignviewers/igv_viewer.html +4 -0
- scout/server/blueprints/alignviewers/templates/alignviewers/utils.html +1 -1
- scout/server/blueprints/cases/controllers.py +57 -29
- scout/server/blueprints/cases/templates/cases/case_report.html +28 -90
- scout/server/blueprints/cases/templates/cases/matchmaker.html +1 -1
- scout/server/blueprints/cases/templates/cases/phenotype.html +1 -1
- scout/server/blueprints/cases/templates/cases/utils.html +34 -53
- scout/server/blueprints/cases/views.py +32 -33
- scout/server/blueprints/clinvar/controllers.py +235 -54
- scout/server/blueprints/clinvar/form.py +38 -1
- scout/server/blueprints/clinvar/static/form_style.css +8 -1
- scout/server/blueprints/clinvar/templates/clinvar/clinvar_onc_submissions.html +200 -0
- scout/server/blueprints/clinvar/templates/clinvar/clinvar_submissions.html +3 -2
- scout/server/blueprints/clinvar/templates/clinvar/components.html +198 -0
- scout/server/blueprints/clinvar/templates/clinvar/multistep_add_onc_variant.html +187 -0
- scout/server/blueprints/clinvar/templates/clinvar/multistep_add_variant.html +9 -348
- scout/server/blueprints/clinvar/templates/clinvar/scripts.html +193 -0
- scout/server/blueprints/clinvar/views.py +90 -13
- scout/server/blueprints/diagnoses/controllers.py +4 -8
- scout/server/blueprints/diagnoses/templates/diagnoses/diagnoses.html +1 -1
- scout/server/blueprints/diagnoses/templates/diagnoses/disease_term.html +1 -1
- scout/server/blueprints/diagnoses/views.py +2 -2
- scout/server/blueprints/institutes/controllers.py +148 -75
- scout/server/blueprints/institutes/forms.py +1 -0
- scout/server/blueprints/institutes/templates/overview/cases.html +1 -1
- scout/server/blueprints/institutes/templates/overview/gene_variants.html +15 -6
- scout/server/blueprints/institutes/templates/overview/institute_sidebar.html +28 -2
- scout/server/blueprints/institutes/templates/overview/utils.html +1 -1
- scout/server/blueprints/institutes/views.py +17 -4
- scout/server/blueprints/login/controllers.py +2 -1
- scout/server/blueprints/login/views.py +5 -2
- scout/server/blueprints/mme/templates/mme/mme_submissions.html +2 -2
- scout/server/blueprints/omics_variants/templates/omics_variants/outliers.html +2 -2
- scout/server/blueprints/omics_variants/views.py +2 -2
- scout/server/blueprints/phenotypes/controllers.py +15 -2
- scout/server/blueprints/phenotypes/templates/phenotypes/hpo_terms.html +1 -1
- scout/server/blueprints/variant/controllers.py +11 -12
- scout/server/blueprints/variant/templates/variant/cancer-variant.html +2 -1
- scout/server/blueprints/variant/templates/variant/components.html +0 -1
- scout/server/blueprints/variant/templates/variant/sv-variant.html +2 -1
- scout/server/blueprints/variant/templates/variant/utils.html +1 -1
- scout/server/blueprints/variant/templates/variant/variant.html +2 -2
- scout/server/blueprints/variant/templates/variant/variant_details.html +100 -84
- scout/server/blueprints/variant/utils.py +25 -0
- scout/server/blueprints/variants/controllers.py +11 -42
- scout/server/blueprints/variants/templates/variants/cancer-variants.html +5 -3
- scout/server/blueprints/variants/templates/variants/str-variants.html +4 -1
- scout/server/blueprints/variants/templates/variants/sv-variants.html +3 -3
- scout/server/blueprints/variants/templates/variants/utils.html +4 -0
- scout/server/blueprints/variants/templates/variants/variants.html +4 -4
- scout/server/blueprints/variants/views.py +9 -8
- scout/server/config.py +3 -0
- scout/server/extensions/beacon_extension.py +7 -2
- scout/server/extensions/clinvar_extension.py +2 -2
- scout/server/templates/bootstrap_global.html +11 -1
- scout/server/templates/layout.html +6 -1
- scout/server/utils.py +24 -3
- {scout_browser-4.101.0.dist-info → scout_browser-4.103.0.dist-info}/METADATA +1 -1
- {scout_browser-4.101.0.dist-info → scout_browser-4.103.0.dist-info}/RECORD +87 -81
- {scout_browser-4.101.0.dist-info → scout_browser-4.103.0.dist-info}/WHEEL +0 -0
- {scout_browser-4.101.0.dist-info → scout_browser-4.103.0.dist-info}/entry_points.txt +0 -0
- {scout_browser-4.101.0.dist-info → scout_browser-4.103.0.dist-info}/licenses/LICENSE +0 -0
scout/commands/update/user.py
CHANGED
@@ -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="
|
13
|
-
@click.option(
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
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(
|
25
|
-
|
26
|
-
|
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
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
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
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
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
|
-
|
61
|
-
LOG.info("Adding institute %s to user", institute_id)
|
81
|
+
LOG.info("User does not have role '%s'", role)
|
62
82
|
|
63
|
-
|
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
|
-
|
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)
|
scout/constants/__init__.py
CHANGED
@@ -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,
|
scout/constants/clinvar.py
CHANGED
@@ -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"]
|
scout/constants/igv_tracks.py
CHANGED
@@ -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 =
|
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 =
|
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"
|
scout/constants/phenotype.py
CHANGED
@@ -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",
|
scout/constants/variant_tags.py
CHANGED
@@ -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
|
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
|
-
|
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)
|
scout/parse/variant/gene.py
CHANGED
@@ -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
|
-
|
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"]
|
106
|
-
hgvs_identifier = transcript.get("coding_sequence_name")
|
100
|
+
if transcript["is_canonical"]:
|
107
101
|
canonical_transcript = transcript["transcript_id"]
|
108
|
-
|
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,
|