scout-browser 4.85__py3-none-any.whl → 4.86__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/__version__.py +1 -1
- scout/adapter/mongo/base.py +17 -14
- scout/adapter/mongo/case.py +20 -1
- scout/adapter/mongo/filter.py +36 -1
- scout/adapter/mongo/omics_variant.py +145 -0
- scout/adapter/mongo/query.py +13 -3
- scout/adapter/mongo/variant.py +10 -4
- scout/build/case.py +5 -0
- scout/build/variant/variant.py +1 -0
- scout/constants/__init__.py +3 -1
- scout/constants/case_tags.py +1 -0
- scout/constants/clinvar.py +1 -1
- scout/constants/file_types.py +31 -0
- scout/constants/filters.py +4 -0
- scout/constants/indexes.py +30 -13
- scout/constants/variant_tags.py +3 -0
- scout/demo/643594.clinical.mei.vcf.gz +0 -0
- scout/demo/643594.clinical.mei.vcf.gz.tbi +0 -0
- scout/demo/643594.config.yaml +4 -0
- scout/demo/drop/fraser_top_hits_clinical.tsv +5 -0
- scout/demo/drop/outrider_top_hits_clinical.tsv +10 -0
- scout/load/setup.py +4 -4
- scout/models/case/case_loading_models.py +25 -2
- scout/models/omics_variant.py +227 -0
- scout/parse/omics_variant/__init__.py +11 -0
- scout/parse/omics_variant/drop.py +19 -0
- scout/parse/variant/callers.py +6 -3
- scout/parse/variant/frequency.py +10 -2
- scout/server/app.py +4 -1
- scout/server/blueprints/alignviewers/controllers.py +35 -24
- scout/server/blueprints/alignviewers/templates/alignviewers/igv_sashimi_viewer.html +19 -15
- scout/server/blueprints/alignviewers/templates/alignviewers/igv_viewer.html +45 -5
- scout/server/blueprints/alignviewers/templates/alignviewers/utils.html +1 -1
- scout/server/blueprints/alignviewers/views.py +10 -2
- scout/server/blueprints/cases/controllers.py +3 -0
- scout/server/blueprints/cases/templates/cases/case.html +27 -9
- scout/server/blueprints/cases/templates/cases/case_report.html +2 -17
- scout/server/blueprints/cases/templates/cases/phenotype.html +8 -5
- scout/server/blueprints/cases/templates/cases/utils.html +26 -3
- scout/server/blueprints/clinvar/controllers.py +9 -3
- scout/server/blueprints/dashboard/controllers.py +44 -13
- scout/server/blueprints/dashboard/static/charts.js +46 -36
- scout/server/blueprints/dashboard/templates/dashboard/dashboard_general.html +2 -2
- scout/server/blueprints/institutes/forms.py +2 -0
- scout/server/blueprints/institutes/templates/overview/cases.html +6 -4
- scout/server/blueprints/institutes/templates/overview/gene_variants.html +40 -27
- scout/server/blueprints/institutes/templates/overview/institute_sidebar.html +1 -1
- scout/server/blueprints/institutes/views.py +5 -12
- scout/server/blueprints/omics_variants/__init__.py +1 -0
- scout/server/blueprints/omics_variants/controllers.py +122 -0
- scout/server/blueprints/omics_variants/templates/omics_variants/outliers.html +262 -0
- scout/server/blueprints/omics_variants/views.py +106 -0
- scout/server/blueprints/panels/controllers.py +1 -7
- scout/server/blueprints/panels/templates/panels/panels.html +12 -4
- scout/server/blueprints/panels/views.py +9 -11
- scout/server/blueprints/variant/templates/variant/buttons.html +7 -2
- scout/server/blueprints/variant/templates/variant/str-variant-reviewer.html +1 -1
- scout/server/blueprints/variant/templates/variant/utils.html +1 -1
- scout/server/blueprints/variant/utils.py +54 -103
- scout/server/blueprints/variant/views.py +1 -0
- scout/server/blueprints/variants/controllers.py +1 -4
- scout/server/blueprints/variants/forms.py +42 -0
- scout/server/blueprints/variants/templates/variants/utils.html +8 -4
- scout/server/blueprints/variants/views.py +28 -7
- scout/server/config.py +4 -0
- scout/server/extensions/clinvar_extension.py +7 -7
- scout/server/links.py +2 -2
- scout/server/templates/bootstrap_global.html +1 -4
- scout/server/templates/utils.html +3 -3
- scout/server/utils.py +4 -1
- {scout_browser-4.85.dist-info → scout_browser-4.86.dist-info}/METADATA +10 -10
- {scout_browser-4.85.dist-info → scout_browser-4.86.dist-info}/RECORD +76 -66
- {scout_browser-4.85.dist-info → scout_browser-4.86.dist-info}/WHEEL +1 -1
- {scout_browser-4.85.dist-info → scout_browser-4.86.dist-info}/LICENSE +0 -0
- {scout_browser-4.85.dist-info → scout_browser-4.86.dist-info}/entry_points.txt +0 -0
- {scout_browser-4.85.dist-info → scout_browser-4.86.dist-info}/top_level.txt +0 -0
scout/__version__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = "4.
|
1
|
+
__version__ = "4.86"
|
scout/adapter/mongo/base.py
CHANGED
@@ -45,6 +45,7 @@ from .index import IndexHandler
|
|
45
45
|
from .institute import InstituteHandler
|
46
46
|
from .managed_variant import ManagedVariantHandler
|
47
47
|
from .matchmaker import MMEHandler
|
48
|
+
from .omics_variant import OmicsVariantHandler
|
48
49
|
from .panel import PanelHandler
|
49
50
|
from .phenomodel import PhenoModelHandler
|
50
51
|
from .query import QueryHandler
|
@@ -57,27 +58,28 @@ LOG = logging.getLogger(__name__)
|
|
57
58
|
|
58
59
|
|
59
60
|
class MongoAdapter(
|
60
|
-
|
61
|
-
CaseHandler,
|
61
|
+
ACMGHandler,
|
62
62
|
CaseGroupHandler,
|
63
|
-
|
63
|
+
CaseHandler,
|
64
|
+
ClinVarHandler,
|
65
|
+
CytobandHandler,
|
66
|
+
DiagnosisHandler,
|
64
67
|
EventHandler,
|
68
|
+
FilterHandler,
|
69
|
+
GeneHandler,
|
65
70
|
HpoHandler,
|
66
|
-
DiagnosisHandler,
|
67
|
-
PanelHandler,
|
68
|
-
QueryHandler,
|
69
|
-
VariantHandler,
|
70
|
-
UserHandler,
|
71
|
-
ACMGHandler,
|
72
71
|
IndexHandler,
|
73
|
-
|
72
|
+
InstituteHandler,
|
74
73
|
MMEHandler,
|
75
|
-
TranscriptHandler,
|
76
|
-
FilterHandler,
|
77
74
|
ManagedVariantHandler,
|
78
|
-
|
75
|
+
OmicsVariantHandler,
|
76
|
+
PanelHandler,
|
79
77
|
PhenoModelHandler,
|
78
|
+
QueryHandler,
|
80
79
|
RankModelHandler,
|
80
|
+
TranscriptHandler,
|
81
|
+
UserHandler,
|
82
|
+
VariantHandler,
|
81
83
|
):
|
82
84
|
"""Adapter for communication with a Mongo database."""
|
83
85
|
|
@@ -108,9 +110,10 @@ class MongoAdapter(
|
|
108
110
|
self.hpo_term_collection = database.hpo_term
|
109
111
|
self.institute_collection = database.institute
|
110
112
|
self.managed_variant_collection = database.managed_variant
|
113
|
+
self.omics_variant_collection = database.omics_variant
|
111
114
|
self.panel_collection = database.gene_panel
|
112
|
-
self.rank_model_collection = database.rank_model
|
113
115
|
self.phenomodel_collection = database.phenomodel
|
116
|
+
self.rank_model_collection = database.rank_model
|
114
117
|
self.transcript_collection = database.transcript
|
115
118
|
self.user_collection = database.user
|
116
119
|
self.variant_collection = database.variant
|
scout/adapter/mongo/case.py
CHANGED
@@ -10,7 +10,7 @@ import pymongo
|
|
10
10
|
from bson import ObjectId
|
11
11
|
|
12
12
|
from scout.build.case import build_case
|
13
|
-
from scout.constants import ACMG_MAP, FILE_TYPE_MAP, ID_PROJECTION
|
13
|
+
from scout.constants import ACMG_MAP, FILE_TYPE_MAP, ID_PROJECTION, OMICS_FILE_TYPE_MAP
|
14
14
|
from scout.exceptions import ConfigError, IntegrityError
|
15
15
|
from scout.parse.variant.ids import parse_document_id
|
16
16
|
from scout.utils.algorithms import ui_score
|
@@ -834,6 +834,21 @@ class CaseHandler(object):
|
|
834
834
|
if key in old_case:
|
835
835
|
new_case[key] = old_case[key]
|
836
836
|
|
837
|
+
def _load_omics_variants(self, case_obj: dict, build: str, update: bool = False):
|
838
|
+
"""Load omics variants. The OMICS FILE type dict contains all we need to
|
839
|
+
determine how to load variants (type, category etc)."""
|
840
|
+
|
841
|
+
for omics_file in OMICS_FILE_TYPE_MAP.keys():
|
842
|
+
if not case_obj["omics_files"].get(omics_file):
|
843
|
+
LOG.debug("didn't find %s for case, skipping", omics_file)
|
844
|
+
continue
|
845
|
+
|
846
|
+
if update:
|
847
|
+
LOG.debug("deleting %s omics variants for case", omics_file)
|
848
|
+
self.delete_omics_variants(case_id=case_obj["_id"], file_type=omics_file)
|
849
|
+
|
850
|
+
self.load_omics_variants(case_obj=case_obj, build=build, file_type=omics_file)
|
851
|
+
|
837
852
|
def load_case(self, config_data: dict, update: bool = False, keep_actions: bool = True) -> dict:
|
838
853
|
"""Load a case into the database
|
839
854
|
|
@@ -930,6 +945,8 @@ class CaseHandler(object):
|
|
930
945
|
except (IntegrityError, ValueError, ConfigError, KeyError) as error:
|
931
946
|
LOG.warning(error)
|
932
947
|
|
948
|
+
self._load_omics_variants(case_obj, build=genome_build, update=update)
|
949
|
+
|
933
950
|
if existing_case:
|
934
951
|
self.update_case_data_sharing(old_case=existing_case, new_case=case_obj)
|
935
952
|
case_obj["rerun_requested"] = False
|
@@ -1049,6 +1066,7 @@ class CaseHandler(object):
|
|
1049
1066
|
"gene_fusion_report_research": case_obj.get("gene_fusion_report_research"),
|
1050
1067
|
"genome_build": case_obj.get("genome_build", "37"),
|
1051
1068
|
"has_meivariants": case_obj.get("has_meivariants"),
|
1069
|
+
"has_outliers": case_obj.get("has_outliers"),
|
1052
1070
|
"has_strvariants": case_obj.get("has_strvariants"),
|
1053
1071
|
"has_svvariants": case_obj.get("has_svvariants"),
|
1054
1072
|
"individuals": case_obj["individuals"],
|
@@ -1057,6 +1075,7 @@ class CaseHandler(object):
|
|
1057
1075
|
"mme_submission": case_obj.get("mme_submission"),
|
1058
1076
|
"multiqc": case_obj.get("multiqc"),
|
1059
1077
|
"multiqc_rna": case_obj.get("multiqc_rna"),
|
1078
|
+
"omics_files": case_obj.get("omics_files"),
|
1060
1079
|
"panels": case_obj.get("panels", []),
|
1061
1080
|
"phenotype_groups": case_obj.get("phenotype_groups"),
|
1062
1081
|
"phenotype_terms": case_obj.get("phenotype_terms"),
|
scout/adapter/mongo/filter.py
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
2
|
import logging
|
3
|
+
from typing import Optional
|
3
4
|
|
4
5
|
from bson.objectid import ObjectId
|
5
|
-
from flask import url_for
|
6
|
+
from flask import flash, url_for
|
6
7
|
from pymongo.cursor import CursorType
|
7
8
|
from werkzeug.datastructures import MultiDict
|
8
9
|
|
@@ -105,6 +106,40 @@ class FilterHandler(object):
|
|
105
106
|
|
106
107
|
return filter_id
|
107
108
|
|
109
|
+
def unaudit_filter(self, audit_id: ObjectId, user_obj: dict):
|
110
|
+
"""Removes an audit filter event with a new un-audit filter event."""
|
111
|
+
|
112
|
+
FILTER_SEARCH = {"_id": ObjectId(audit_id), "verb": "filter_audit"}
|
113
|
+
|
114
|
+
audit_event: Optional[dict] = self.event_collection.find_one(FILTER_SEARCH)
|
115
|
+
if audit_event is None:
|
116
|
+
return
|
117
|
+
if audit_event.get("user_id") != user_obj["email"]:
|
118
|
+
flash("You can't un-audit a filter audited by another user.", "warning")
|
119
|
+
return
|
120
|
+
institute_obj: Optional[dict] = self.institute(institute_id=audit_event.get("institute"))
|
121
|
+
case_obj: Optional[dict] = self.case(case_id=audit_event.get("case"))
|
122
|
+
if None in [institute_obj, case_obj]:
|
123
|
+
LOG.error(
|
124
|
+
f"An error occurred un-auditing filter: institute or case missing for audit event {audit_id}."
|
125
|
+
)
|
126
|
+
return
|
127
|
+
|
128
|
+
# Create un-audit event
|
129
|
+
self.create_event(
|
130
|
+
institute=institute_obj,
|
131
|
+
case=case_obj,
|
132
|
+
user=user_obj,
|
133
|
+
link=audit_event["link"],
|
134
|
+
category="case",
|
135
|
+
verb="filter_unaudit",
|
136
|
+
subject=audit_event["subject"],
|
137
|
+
level="global",
|
138
|
+
)
|
139
|
+
|
140
|
+
# Remove audit event
|
141
|
+
self.event_collection.delete_one(FILTER_SEARCH)
|
142
|
+
|
108
143
|
def audit_filter(self, filter_id, institute_obj, case_obj, user_obj, category="snv", link=None):
|
109
144
|
"""Mark audit of filter for case in events.
|
110
145
|
Audit filter leaves a voluntary log trail to answer questions like "did I really check for recessive variants"
|
@@ -0,0 +1,145 @@
|
|
1
|
+
import logging
|
2
|
+
from typing import Dict, Optional
|
3
|
+
|
4
|
+
from scout.constants import OMICS_FILE_TYPE_MAP
|
5
|
+
from scout.models.omics_variant import OmicsVariantLoader
|
6
|
+
from scout.parse.omics_variant import parse_omics_file
|
7
|
+
|
8
|
+
LOG = logging.getLogger(__name__)
|
9
|
+
|
10
|
+
|
11
|
+
class OmicsVariantHandler:
|
12
|
+
def delete_omics_variants(self, case_id: str, file_type: str):
|
13
|
+
"""Delete OMICS variants for a case"""
|
14
|
+
omics_file_type = OMICS_FILE_TYPE_MAP.get(file_type)
|
15
|
+
category = omics_file_type["category"]
|
16
|
+
sub_category = omics_file_type["sub_category"]
|
17
|
+
variant_type = omics_file_type["variant_type"]
|
18
|
+
|
19
|
+
LOG.info(
|
20
|
+
"Deleting old %s %s %s OMICS variants.",
|
21
|
+
variant_type,
|
22
|
+
sub_category,
|
23
|
+
category,
|
24
|
+
)
|
25
|
+
|
26
|
+
query = {
|
27
|
+
"case_id": case_id,
|
28
|
+
"variant_type": variant_type,
|
29
|
+
"category": category,
|
30
|
+
"sub_category": sub_category,
|
31
|
+
}
|
32
|
+
result = self.omics_variant_collection.delete_many(query)
|
33
|
+
|
34
|
+
LOG.info("%s variants deleted", result.deleted_count)
|
35
|
+
|
36
|
+
def set_samples(self, case_obj: dict, omics_model: dict):
|
37
|
+
"""Internal member function to connect individuals.
|
38
|
+
OMICS variants do not have a genotype as such."""
|
39
|
+
|
40
|
+
samples = []
|
41
|
+
|
42
|
+
for ind in case_obj.get("individuals"):
|
43
|
+
if omics_model["sampleID"] == ind.get("individual_id"):
|
44
|
+
sample = {
|
45
|
+
"sample_id": ind["individual_id"],
|
46
|
+
"display_name": ind["display_name"],
|
47
|
+
"genotype_call": "./1",
|
48
|
+
}
|
49
|
+
samples.append(sample)
|
50
|
+
|
51
|
+
omics_model["samples"] = samples
|
52
|
+
|
53
|
+
def set_genes(self, omics_model: dict):
|
54
|
+
"""Internal member function to connect gene based on the hgnc_id / symbol / geneID given in outlier file.
|
55
|
+
We start with the case of having one hgnc_id.
|
56
|
+
"""
|
57
|
+
hgnc_gene = self.hgnc_gene(omics_model["hgnc_ids"][0], omics_model["build"])
|
58
|
+
if hgnc_gene:
|
59
|
+
omics_model["genes"] = [hgnc_gene]
|
60
|
+
|
61
|
+
def load_omics_variants(self, case_obj: dict, file_type: str, build: Optional[str] = "37"):
|
62
|
+
"""Load OMICS variants for a case"""
|
63
|
+
|
64
|
+
case_panels = case_obj.get("panels", [])
|
65
|
+
gene_to_panels = self.gene_to_panels(case_obj)
|
66
|
+
|
67
|
+
omics_file_type: dict = OMICS_FILE_TYPE_MAP.get(file_type)
|
68
|
+
|
69
|
+
nr_inserted = 0
|
70
|
+
|
71
|
+
file_handle = open(case_obj["omics_files"].get(file_type), "r")
|
72
|
+
|
73
|
+
for omics_info in parse_omics_file(file_handle, omics_file_type=omics_file_type):
|
74
|
+
omics_info["case_id"] = case_obj["_id"]
|
75
|
+
omics_info["build"] = "37" if "37" in build else "38"
|
76
|
+
omics_info["file_type"] = file_type
|
77
|
+
omics_info["institute"] = case_obj["owner"]
|
78
|
+
for key in ["category", "sub_category", "variant_type", "analysis_type"]:
|
79
|
+
omics_info[key] = omics_file_type[key]
|
80
|
+
|
81
|
+
omics_model = OmicsVariantLoader(**omics_info).model_dump(
|
82
|
+
by_alias=True, exclude_none=True
|
83
|
+
)
|
84
|
+
|
85
|
+
self.set_genes(omics_model)
|
86
|
+
self.set_samples(case_obj, omics_model)
|
87
|
+
|
88
|
+
# If case has gene panels, only add clinical variants with a matching gene
|
89
|
+
variant_genes = [gene["hgnc_id"] for gene in omics_model.get("genes", [])]
|
90
|
+
if (
|
91
|
+
omics_model["variant_type"] == "clinical"
|
92
|
+
and case_panels
|
93
|
+
and all(variant_gene not in gene_to_panels for variant_gene in variant_genes)
|
94
|
+
):
|
95
|
+
continue
|
96
|
+
|
97
|
+
self.omics_variant_collection.insert_one(omics_model)
|
98
|
+
nr_inserted += 1
|
99
|
+
|
100
|
+
LOG.info("%s variants inserted", nr_inserted)
|
101
|
+
|
102
|
+
def omics_variant(self, variant_id: str, projection: Optional[Dict] = None):
|
103
|
+
"""Return omics variant"""
|
104
|
+
|
105
|
+
return self.omics_variant_collection.find_one({"omics_variant_id": variant_id}, projection)
|
106
|
+
|
107
|
+
def omics_variants(
|
108
|
+
self,
|
109
|
+
case_id: str,
|
110
|
+
query=None,
|
111
|
+
category: str = "outlier",
|
112
|
+
nr_of_variants=50,
|
113
|
+
skip=0,
|
114
|
+
projection: Optional[Dict] = None,
|
115
|
+
build="37",
|
116
|
+
):
|
117
|
+
"""Return omics variants for a case, of a particular type (clinical, research) and category (outlier, ...)."""
|
118
|
+
|
119
|
+
if nr_of_variants == -1:
|
120
|
+
nr_of_variants = 0 # This will return all variants
|
121
|
+
else:
|
122
|
+
nr_of_variants = skip + nr_of_variants
|
123
|
+
|
124
|
+
query = self.build_query(case_id, query=query, category=category, build=build)
|
125
|
+
return self.omics_variant_collection.find(
|
126
|
+
query, projection, skip=skip, limit=nr_of_variants
|
127
|
+
)
|
128
|
+
|
129
|
+
def count_omics_variants(
|
130
|
+
self, case_id, query, variant_ids=None, category="outlier", build="37"
|
131
|
+
):
|
132
|
+
"""Returns number of variants
|
133
|
+
|
134
|
+
Arguments:
|
135
|
+
case_id(str): A string that represents the case
|
136
|
+
query(dict): A query dictionary
|
137
|
+
|
138
|
+
Returns:
|
139
|
+
integer
|
140
|
+
"""
|
141
|
+
|
142
|
+
query = self.build_query(
|
143
|
+
case_id, query=query, variant_ids=variant_ids, category=category, build=build
|
144
|
+
)
|
145
|
+
return self.omics_variant_collection.count_documents(query)
|
scout/adapter/mongo/query.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
import logging
|
2
2
|
import re
|
3
3
|
from datetime import datetime, timedelta
|
4
|
+
from typing import Optional, Union
|
4
5
|
|
5
6
|
from scout.constants import (
|
6
7
|
CLINSIG_MAP,
|
@@ -76,7 +77,11 @@ class QueryHandler(object):
|
|
76
77
|
return variants_query
|
77
78
|
|
78
79
|
def build_variant_query(
|
79
|
-
self,
|
80
|
+
self,
|
81
|
+
query: Optional[dict] = None,
|
82
|
+
institute_ids: Optional[list] = [],
|
83
|
+
category: Optional[Union[str, list]] = "snv",
|
84
|
+
variant_type: Optional[list] = ["clinical"],
|
80
85
|
):
|
81
86
|
"""Build a mongo query across multiple cases.
|
82
87
|
Translate query options from a form into a complete mongo query dictionary.
|
@@ -93,7 +98,7 @@ class QueryHandler(object):
|
|
93
98
|
Args:
|
94
99
|
query(dict): A query dictionary for the database, from a query form.
|
95
100
|
institute_ids: a list of institute _ids
|
96
|
-
category(str): 'snv', 'sv', 'str' 'cancer_sv' or 'cancer'
|
101
|
+
category(str): 'snv', 'sv', 'str' 'cancer_sv' or 'cancer' OR a LIST(str) of the same
|
97
102
|
variant_type(str): 'clinical' or 'research'
|
98
103
|
|
99
104
|
Possible query dict keys:
|
@@ -112,7 +117,12 @@ class QueryHandler(object):
|
|
112
117
|
|
113
118
|
mongo_variant_query["hgnc_symbols"] = {"$in": query["hgnc_symbols"]}
|
114
119
|
mongo_variant_query["variant_type"] = {"$in": variant_type}
|
115
|
-
|
120
|
+
|
121
|
+
if not category:
|
122
|
+
category = "snv"
|
123
|
+
mongo_variant_query["category"] = (
|
124
|
+
{"$in": category} if isinstance(category, list) else category
|
125
|
+
)
|
116
126
|
|
117
127
|
select_cases = None
|
118
128
|
select_case_obj = None
|
scout/adapter/mongo/variant.py
CHANGED
@@ -206,7 +206,7 @@ class VariantHandler(VariantLoader):
|
|
206
206
|
|
207
207
|
return result
|
208
208
|
|
209
|
-
def count_variants(self, case_id, query, variant_ids, category):
|
209
|
+
def count_variants(self, case_id, query, variant_ids, category, build="37"):
|
210
210
|
"""Returns number of variants
|
211
211
|
|
212
212
|
Arguments:
|
@@ -219,7 +219,9 @@ class VariantHandler(VariantLoader):
|
|
219
219
|
integer
|
220
220
|
"""
|
221
221
|
|
222
|
-
query = self.build_query(
|
222
|
+
query = self.build_query(
|
223
|
+
case_id, query=query, variant_ids=variant_ids, category=category, build=build
|
224
|
+
)
|
223
225
|
return self.variant_collection.count_documents(query)
|
224
226
|
|
225
227
|
def variant_update_field(self, variant_id: str, field_name: str, field_value: Any) -> dict:
|
@@ -237,6 +239,7 @@ class VariantHandler(VariantLoader):
|
|
237
239
|
case_id=None,
|
238
240
|
simple_id=None,
|
239
241
|
variant_type="clinical",
|
242
|
+
projection=None,
|
240
243
|
):
|
241
244
|
"""Returns the specified variant.
|
242
245
|
|
@@ -264,7 +267,7 @@ class VariantHandler(VariantLoader):
|
|
264
267
|
# search with a unique id
|
265
268
|
query["_id"] = document_id
|
266
269
|
|
267
|
-
variant_obj = self.variant_collection.find_one(query)
|
270
|
+
variant_obj = self.variant_collection.find_one(query, projection=projection)
|
268
271
|
if not variant_obj:
|
269
272
|
return variant_obj
|
270
273
|
case_obj = self.case(
|
@@ -890,7 +893,10 @@ class VariantHandler(VariantLoader):
|
|
890
893
|
}
|
891
894
|
}
|
892
895
|
pipeline = [match, group]
|
893
|
-
results = self.variant_collection.aggregate(pipeline)
|
896
|
+
results = list(self.variant_collection.aggregate(pipeline))
|
897
|
+
|
898
|
+
omics_results = list(self.omics_variant_collection.aggregate(pipeline))
|
899
|
+
results.extend(omics_results)
|
894
900
|
|
895
901
|
variants_by_type = {}
|
896
902
|
for item in results:
|
scout/build/case.py
CHANGED
@@ -285,6 +285,7 @@ def build_case(case_data, adapter):
|
|
285
285
|
case_obj[report_key] = case_data.get(report_key)
|
286
286
|
|
287
287
|
case_obj["vcf_files"] = case_data.get("vcf_files", {})
|
288
|
+
case_obj["omics_files"] = case_data.get("omics_files", {})
|
288
289
|
case_obj["delivery_report"] = case_data.get("delivery_report")
|
289
290
|
|
290
291
|
_populate_pipeline_info(case_obj, case_data)
|
@@ -297,6 +298,10 @@ def build_case(case_data, adapter):
|
|
297
298
|
|
298
299
|
case_obj["has_meivariants"] = bool(case_obj["vcf_files"].get("vcf_mei"))
|
299
300
|
|
301
|
+
case_obj["has_outliers"] = bool(
|
302
|
+
case_obj["omics_files"].get("fraser") or case_obj["omics_files"].get("outrider")
|
303
|
+
)
|
304
|
+
|
300
305
|
case_obj["is_migrated"] = False
|
301
306
|
|
302
307
|
# What experiment is used, alternatives are rare (rare disease) or cancer
|
scout/build/variant/variant.py
CHANGED
@@ -460,6 +460,7 @@ def add_frequencies(variant_obj, frequencies):
|
|
460
460
|
variant_obj["thousand_genomes_frequency_right"] = call_safe(
|
461
461
|
float, frequencies.get("thousand_g_right")
|
462
462
|
)
|
463
|
+
variant_obj["colorsdb_af"] = call_safe(float, frequencies.get("colorsdb_af"))
|
463
464
|
|
464
465
|
# Add the sv counts:
|
465
466
|
variant_obj["clingen_cgh_benign"] = frequencies.get("clingen_benign")
|
scout/constants/__init__.py
CHANGED
@@ -45,11 +45,12 @@ from .disease_parsing import (
|
|
45
45
|
MIMNR_PATTERN,
|
46
46
|
OMIM_STATUS_MAP,
|
47
47
|
)
|
48
|
-
from .file_types import FILE_TYPE_MAP
|
48
|
+
from .file_types import FILE_TYPE_MAP, OMICS_FILE_TYPE_MAP
|
49
49
|
from .filters import (
|
50
50
|
CLINICAL_FILTER_BASE,
|
51
51
|
CLINICAL_FILTER_BASE_CANCER,
|
52
52
|
CLINICAL_FILTER_BASE_MEI,
|
53
|
+
CLINICAL_FILTER_BASE_OUTLIER,
|
53
54
|
CLINICAL_FILTER_BASE_SV,
|
54
55
|
)
|
55
56
|
from .gene_tags import (
|
@@ -88,6 +89,7 @@ from .variant_tags import (
|
|
88
89
|
GENETIC_MODELS_PALETTE,
|
89
90
|
MANUAL_RANK_OPTIONS,
|
90
91
|
MOSAICISM_OPTIONS,
|
92
|
+
OUTLIER_TYPES,
|
91
93
|
SPIDEX_HUMAN,
|
92
94
|
SPIDEX_LEVELS,
|
93
95
|
SV_TYPES,
|
scout/constants/case_tags.py
CHANGED
@@ -117,6 +117,7 @@ VERBS_MAP = {
|
|
117
117
|
"dismiss_variant": "dismissed variant for",
|
118
118
|
"filter_audit": "marked case audited with filter",
|
119
119
|
"filter_stash": "stored a filter for",
|
120
|
+
"filter_unaudit": "un-audited a filter for",
|
120
121
|
"manual_rank": "updated manual rank for",
|
121
122
|
"mark_causative": "marked causative for",
|
122
123
|
"mark_partial_causative": "mark partial causative for",
|
scout/constants/clinvar.py
CHANGED
scout/constants/file_types.py
CHANGED
@@ -88,3 +88,34 @@ FILE_TYPE_MAP = {
|
|
88
88
|
"load_priority": 50,
|
89
89
|
},
|
90
90
|
}
|
91
|
+
|
92
|
+
OMICS_FILE_TYPE_MAP = {
|
93
|
+
"fraser": {
|
94
|
+
"format": "tsv",
|
95
|
+
"analysis_type": "wts",
|
96
|
+
"category": "outlier",
|
97
|
+
"sub_category": "splicing",
|
98
|
+
"variant_type": "clinical",
|
99
|
+
},
|
100
|
+
"outrider": {
|
101
|
+
"format": "tsv",
|
102
|
+
"analysis_type": "wts",
|
103
|
+
"category": "outlier",
|
104
|
+
"sub_category": "expression",
|
105
|
+
"variant_type": "clinical",
|
106
|
+
},
|
107
|
+
"fraser_research": {
|
108
|
+
"format": "tsv",
|
109
|
+
"analysis_type": "wts",
|
110
|
+
"category": "outlier",
|
111
|
+
"sub_category": "splicing",
|
112
|
+
"variant_type": "research",
|
113
|
+
},
|
114
|
+
"outrider_research": {
|
115
|
+
"format": "tsv",
|
116
|
+
"analysis_type": "wts",
|
117
|
+
"category": "outlier",
|
118
|
+
"sub_category": "expression",
|
119
|
+
"variant_type": "research",
|
120
|
+
},
|
121
|
+
}
|
scout/constants/filters.py
CHANGED
scout/constants/indexes.py
CHANGED
@@ -12,23 +12,48 @@ INDEXES = {
|
|
12
12
|
IndexModel(
|
13
13
|
[("build", ASCENDING), ("chromosome", ASCENDING)],
|
14
14
|
name="build_chromosome",
|
15
|
-
background=True,
|
16
15
|
),
|
17
16
|
IndexModel(
|
18
17
|
[("build", ASCENDING), ("hgnc_id", ASCENDING)],
|
19
18
|
name="build_hgncid",
|
20
|
-
background=True,
|
21
19
|
),
|
22
20
|
IndexModel(
|
23
21
|
[("build", ASCENDING), ("aliases", ASCENDING)],
|
24
22
|
name="build_aliases",
|
25
|
-
background=True,
|
26
23
|
),
|
27
24
|
IndexModel(
|
28
25
|
[("build", ASCENDING), ("hgnc_symbol", ASCENDING)],
|
29
26
|
name="build_hgnc_symbol",
|
30
27
|
),
|
31
28
|
],
|
29
|
+
"omics_variant": [
|
30
|
+
IndexModel(
|
31
|
+
# Clear text variant id index
|
32
|
+
[
|
33
|
+
("omics_variant_id", ASCENDING),
|
34
|
+
],
|
35
|
+
name="omics_variant_id",
|
36
|
+
),
|
37
|
+
IndexModel(
|
38
|
+
# Index for searching across cases for a change in given genes
|
39
|
+
[
|
40
|
+
("hgnc_ids", ASCENDING),
|
41
|
+
("sub_category", ASCENDING),
|
42
|
+
("variant_type", ASCENDING),
|
43
|
+
],
|
44
|
+
name="hgnc_ids_sub_category_variant_type",
|
45
|
+
),
|
46
|
+
IndexModel(
|
47
|
+
# Filterish index
|
48
|
+
[
|
49
|
+
("case_id", ASCENDING),
|
50
|
+
("variant_type", ASCENDING),
|
51
|
+
("sub_category", ASCENDING),
|
52
|
+
("hgnc_ids", ASCENDING),
|
53
|
+
],
|
54
|
+
name="case_id_variant_type_sub_category_hgnc_ids",
|
55
|
+
),
|
56
|
+
],
|
32
57
|
"variant": [
|
33
58
|
IndexModel(
|
34
59
|
[
|
@@ -39,7 +64,6 @@ INDEXES = {
|
|
39
64
|
("hgnc_ids", ASCENDING),
|
40
65
|
],
|
41
66
|
name="caseid_category_varianttype_variantrank_hgncids",
|
42
|
-
background=True,
|
43
67
|
),
|
44
68
|
IndexModel(
|
45
69
|
[
|
@@ -49,8 +73,7 @@ INDEXES = {
|
|
49
73
|
("variant_type", ASCENDING),
|
50
74
|
],
|
51
75
|
name="hgncsymbol_rankscore_category_varianttype",
|
52
|
-
|
53
|
-
partialFilterExpression={"rank_score": {"$gt": 5}, "category": "snv"},
|
76
|
+
partialFilterExpression={"rank_score": {"$gte": 5}},
|
54
77
|
),
|
55
78
|
IndexModel(
|
56
79
|
[
|
@@ -59,7 +82,6 @@ INDEXES = {
|
|
59
82
|
("category", ASCENDING),
|
60
83
|
],
|
61
84
|
name="variantid_caseid_category",
|
62
|
-
background=True,
|
63
85
|
),
|
64
86
|
IndexModel(
|
65
87
|
[
|
@@ -69,7 +91,6 @@ INDEXES = {
|
|
69
91
|
("rank_score", ASCENDING),
|
70
92
|
],
|
71
93
|
name="category_caseid_varianttype_rankscore",
|
72
|
-
background=True,
|
73
94
|
),
|
74
95
|
IndexModel(
|
75
96
|
[
|
@@ -81,18 +102,16 @@ INDEXES = {
|
|
81
102
|
("end", ASCENDING),
|
82
103
|
],
|
83
104
|
name="caseid_category_chromosome_start_end",
|
84
|
-
background=True,
|
85
105
|
),
|
86
106
|
IndexModel(
|
87
107
|
[("variant_id", ASCENDING), ("institute", ASCENDING)],
|
88
108
|
name="variant_id_institute",
|
89
|
-
background=True,
|
90
109
|
),
|
91
110
|
],
|
92
111
|
"hpo_term": [
|
93
112
|
IndexModel([("description", ASCENDING)], name="description"),
|
94
113
|
IndexModel([("description", TEXT)], default_language="english", name="description_text"),
|
95
|
-
IndexModel([("hpo_number", ASCENDING)], name="number"
|
114
|
+
IndexModel([("hpo_number", ASCENDING)], name="number"),
|
96
115
|
],
|
97
116
|
"event": [
|
98
117
|
IndexModel(
|
@@ -115,14 +134,12 @@ INDEXES = {
|
|
115
134
|
IndexModel(
|
116
135
|
[("build", ASCENDING), ("hgnc_id", ASCENDING), ("length", DESCENDING)],
|
117
136
|
name="hgncid_length",
|
118
|
-
background=True,
|
119
137
|
)
|
120
138
|
],
|
121
139
|
"exon": [
|
122
140
|
IndexModel(
|
123
141
|
[("build", ASCENDING), ("hgnc_id", ASCENDING)],
|
124
142
|
name="build_hgncid",
|
125
|
-
background=True,
|
126
143
|
)
|
127
144
|
],
|
128
145
|
"case": [
|
scout/constants/variant_tags.py
CHANGED
@@ -37,6 +37,8 @@ FEATURE_TYPES = (
|
|
37
37
|
|
38
38
|
SV_TYPES = ("ins", "del", "dup", "cnv", "inv", "bnd")
|
39
39
|
|
40
|
+
OUTLIER_TYPES = ("splicing", "expression")
|
41
|
+
|
40
42
|
GENETIC_MODELS = (
|
41
43
|
("AR_hom", "Autosomal Recessive Homozygote"),
|
42
44
|
("AR_hom_dn", "Autosomal Recessive Homozygote De Novo"),
|
@@ -519,4 +521,5 @@ VARIANTS_TARGET_FROM_CATEGORY = {
|
|
519
521
|
"snv": "variants.variants",
|
520
522
|
"str": "variants.str_variants",
|
521
523
|
"fusion": "variants.fusion_variants",
|
524
|
+
"outlier": "omics_variants.outliers",
|
522
525
|
}
|
Binary file
|
Binary file
|
scout/demo/643594.config.yaml
CHANGED
@@ -125,6 +125,10 @@ vcf_snv_research: scout/demo/643594.research.vcf.gz
|
|
125
125
|
vcf_sv_research: scout/demo/643594.research.SV.vcf.gz
|
126
126
|
vcf_mei_research: scout/demo/643594.research.mei.vcf.gz
|
127
127
|
|
128
|
+
omics_files:
|
129
|
+
fraser: scout/demo/drop/fraser_top_hits_clinical.tsv
|
130
|
+
outrider: scout/demo/drop/outrider_top_hits_clinical.tsv
|
131
|
+
|
128
132
|
smn_tsv: scout/demo/643594.solo.smn.tsv
|
129
133
|
|
130
134
|
madeline: scout/demo/madeline.xml
|