scout-browser 4.82.2__py3-none-any.whl → 4.84__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/client.py +1 -0
- scout/adapter/mongo/base.py +0 -1
- scout/adapter/mongo/case.py +19 -37
- scout/adapter/mongo/case_events.py +98 -2
- scout/adapter/mongo/hgnc.py +39 -22
- scout/adapter/mongo/institute.py +3 -9
- scout/adapter/mongo/panel.py +2 -1
- scout/adapter/mongo/variant.py +12 -2
- scout/adapter/mongo/variant_loader.py +156 -141
- scout/build/genes/hgnc_gene.py +5 -134
- scout/commands/base.py +1 -0
- scout/commands/download/ensembl.py +1 -0
- scout/commands/download/everything.py +1 -0
- scout/commands/download/exac.py +1 -0
- scout/commands/download/hgnc.py +1 -0
- scout/commands/download/hpo.py +1 -0
- scout/commands/download/omim.py +1 -0
- scout/commands/export/database.py +1 -0
- scout/commands/load/panel.py +1 -0
- scout/commands/load/report.py +1 -0
- scout/commands/update/case.py +10 -10
- scout/commands/update/individual.py +6 -1
- scout/commands/update/omim.py +1 -0
- scout/commands/update/panelapp.py +1 -0
- scout/constants/file_types.py +86 -13
- scout/export/exon.py +1 -0
- scout/load/__init__.py +0 -1
- scout/load/all.py +8 -5
- scout/load/hgnc_gene.py +1 -1
- scout/load/panel.py +8 -4
- scout/load/setup.py +1 -0
- scout/models/case/case_loading_models.py +6 -16
- scout/models/hgnc_map.py +50 -87
- scout/models/phenotype_term.py +3 -3
- scout/parse/case.py +0 -1
- scout/parse/disease_terms.py +1 -0
- scout/parse/omim.py +1 -0
- scout/parse/orpha.py +1 -0
- scout/parse/panel.py +40 -15
- scout/parse/variant/conservation.py +1 -0
- scout/resources/__init__.py +3 -0
- scout/server/app.py +4 -50
- scout/server/blueprints/alignviewers/controllers.py +15 -17
- scout/server/blueprints/alignviewers/templates/alignviewers/igv_viewer.html +13 -3
- scout/server/blueprints/alignviewers/views.py +10 -15
- scout/server/blueprints/cases/controllers.py +70 -73
- scout/server/blueprints/cases/templates/cases/case.html +94 -71
- scout/server/blueprints/cases/templates/cases/collapsible_actionbar.html +1 -1
- scout/server/blueprints/cases/templates/cases/phenotype.html +8 -6
- scout/server/blueprints/cases/templates/cases/utils.html +3 -3
- scout/server/blueprints/cases/views.py +8 -6
- scout/server/blueprints/panels/forms.py +1 -0
- scout/server/blueprints/variant/controllers.py +14 -19
- scout/server/blueprints/variant/templates/variant/acmg.html +25 -16
- scout/server/blueprints/variant/templates/variant/components.html +11 -6
- scout/server/blueprints/variant/views.py +5 -2
- scout/server/blueprints/variants/controllers.py +12 -28
- scout/server/blueprints/variants/views.py +1 -1
- scout/server/config.py +16 -4
- scout/server/extensions/__init__.py +4 -2
- scout/server/extensions/beacon_extension.py +1 -0
- scout/server/extensions/bionano_extension.py +1 -0
- scout/server/extensions/chanjo_extension.py +59 -0
- scout/server/extensions/gens_extension.py +1 -0
- scout/server/extensions/ldap_extension.py +5 -3
- scout/server/extensions/loqus_extension.py +16 -14
- scout/server/extensions/matchmaker_extension.py +1 -0
- scout/server/extensions/mongo_extension.py +1 -0
- scout/server/extensions/phenopacket_extension.py +1 -0
- scout/server/extensions/rerunner_extension.py +1 -0
- scout/server/links.py +4 -4
- scout/server/static/bs_styles.css +20 -2
- scout/server/utils.py +16 -2
- scout/utils/acmg.py +33 -20
- scout/utils/ensembl_rest_clients.py +1 -0
- scout/utils/scout_requests.py +1 -0
- scout/utils/sort.py +21 -0
- scout/utils/track_resources.py +70 -0
- {scout_browser-4.82.2.dist-info → scout_browser-4.84.dist-info}/METADATA +2 -5
- {scout_browser-4.82.2.dist-info → scout_browser-4.84.dist-info}/RECORD +85 -84
- {scout_browser-4.82.2.dist-info → scout_browser-4.84.dist-info}/WHEEL +1 -1
- {scout_browser-4.82.2.dist-info → scout_browser-4.84.dist-info}/entry_points.txt +0 -1
- scout/load/case.py +0 -36
- scout/utils/cloud_resources.py +0 -61
- {scout_browser-4.82.2.dist-info → scout_browser-4.84.dist-info}/LICENSE +0 -0
- {scout_browser-4.82.2.dist-info → scout_browser-4.84.dist-info}/top_level.txt +0 -0
scout/server/config.py
CHANGED
@@ -75,10 +75,9 @@ ACCREDITATION_BADGE = "swedac-1926-iso17025.png"
|
|
75
75
|
# Connection details for Scout REViewer service
|
76
76
|
# SCOUT_REVIEWER_URL = "http://127.0.0.1:8000/reviewer"
|
77
77
|
|
78
|
-
#
|
79
|
-
#
|
80
|
-
#
|
81
|
-
# CLOUD_IGV_TRACKS = [
|
78
|
+
# Custom IGV tracks can be configured here to allow users to enable them on their IGV views.
|
79
|
+
# A number of publicly-available tracks can be found here: https://trackhubregistry.org/ or downloaded from
|
80
|
+
# CUSTOM_IGV_TRACKS = [
|
82
81
|
# {
|
83
82
|
# "name": "public_tracks",
|
84
83
|
# "access": "public",
|
@@ -99,6 +98,19 @@ ACCREDITATION_BADGE = "swedac-1926-iso17025.png"
|
|
99
98
|
# },
|
100
99
|
# ],
|
101
100
|
# },
|
101
|
+
# {
|
102
|
+
# "name": "local_tracks",
|
103
|
+
# "access": "public",
|
104
|
+
# "tracks": [
|
105
|
+
# {
|
106
|
+
# "name": "MANE",
|
107
|
+
# "type": "annotation",
|
108
|
+
# "format": "bigbed",
|
109
|
+
# "build": "38",
|
110
|
+
# "url": "scout/resources/custom_igv_tracks/mane.bb",
|
111
|
+
# },
|
112
|
+
# ],
|
113
|
+
# },
|
102
114
|
# ]
|
103
115
|
|
104
116
|
# Chanjo-Report
|
@@ -6,10 +6,11 @@ from flask_login import LoginManager
|
|
6
6
|
from flask_mail import Mail
|
7
7
|
|
8
8
|
from scout.adapter import MongoAdapter
|
9
|
-
from scout.utils.
|
9
|
+
from scout.utils.track_resources import AlignTrackHandler
|
10
10
|
|
11
11
|
from .beacon_extension import Beacon
|
12
12
|
from .bionano_extension import BioNanoAccessAPI
|
13
|
+
from .chanjo_extension import ChanjoReport
|
13
14
|
from .clinvar_extension import ClinVarApi
|
14
15
|
from .gens_extension import GensViewer
|
15
16
|
from .ldap_extension import LdapManager
|
@@ -33,5 +34,6 @@ phenopacketapi = PhenopacketAPI()
|
|
33
34
|
rerunner = RerunnerService()
|
34
35
|
matchmaker = MatchMaker()
|
35
36
|
beacon = Beacon()
|
36
|
-
|
37
|
+
config_igv_tracks = AlignTrackHandler()
|
37
38
|
bionano_access = BioNanoAccessAPI()
|
39
|
+
chanjo_report = ChanjoReport()
|
@@ -6,6 +6,7 @@
|
|
6
6
|
For further development, the server has a Swagger-like demo interface at https://bionano-access.scilifelab.se/Bnx/
|
7
7
|
which is useful for details, and for sniffing actual message content structure, required cookie variable names etc.
|
8
8
|
"""
|
9
|
+
|
9
10
|
import json
|
10
11
|
import logging
|
11
12
|
from typing import Dict, Iterable, List, Optional, Tuple
|
@@ -0,0 +1,59 @@
|
|
1
|
+
"""
|
2
|
+
Generate coverage reports using chanjo and chanjo-report. Documentation under -> `docs/admin-guide/chanjo_coverage_integration.md`
|
3
|
+
"""
|
4
|
+
|
5
|
+
import logging
|
6
|
+
|
7
|
+
from flask import current_app, request
|
8
|
+
from flask_babel import Babel
|
9
|
+
from markupsafe import Markup
|
10
|
+
|
11
|
+
LOG = logging.getLogger(__name__)
|
12
|
+
|
13
|
+
|
14
|
+
class ChanjoReport:
|
15
|
+
"""Interfaces with chanjo-report. Creates the /reports endpoints in scout domain. Use Babel to set report language."""
|
16
|
+
|
17
|
+
def init_app(self, app):
|
18
|
+
try:
|
19
|
+
from chanjo_report.server.app import configure_template_filters
|
20
|
+
from chanjo_report.server.blueprints import report_bp
|
21
|
+
from chanjo_report.server.extensions import api as chanjo_api
|
22
|
+
except ImportError as error:
|
23
|
+
chanjo_api = None
|
24
|
+
report_bp = None
|
25
|
+
configure_template_filters = None
|
26
|
+
LOG.error(error)
|
27
|
+
|
28
|
+
if not chanjo_api:
|
29
|
+
raise ImportError(
|
30
|
+
"An SQL db path was given, but chanjo-report could not be registered."
|
31
|
+
)
|
32
|
+
|
33
|
+
def get_locale():
|
34
|
+
"""Determine locale to use for translations."""
|
35
|
+
accept_languages = current_app.config.get("ACCEPT_LANGUAGES", ["en"])
|
36
|
+
|
37
|
+
# first check request args
|
38
|
+
session_language = Markup.escape(request.args.get("lang"))
|
39
|
+
if session_language in accept_languages:
|
40
|
+
current_app.logger.info("using session language: %s", session_language)
|
41
|
+
return session_language
|
42
|
+
|
43
|
+
# language can be forced in config
|
44
|
+
user_language = current_app.config.get("REPORT_LANGUAGE")
|
45
|
+
if user_language:
|
46
|
+
return user_language
|
47
|
+
|
48
|
+
# try to guess the language from the user accept header that
|
49
|
+
# the browser transmits. We support de/fr/en in this example.
|
50
|
+
# The best match wins.
|
51
|
+
return request.accept_languages.best_match(accept_languages)
|
52
|
+
|
53
|
+
babel = Babel(app)
|
54
|
+
babel.init_app(app, locale_selector=get_locale)
|
55
|
+
chanjo_api.init_app(app)
|
56
|
+
configure_template_filters(app)
|
57
|
+
app.register_blueprint(report_bp, url_prefix="/reports")
|
58
|
+
app.config["chanjo_report"] = True
|
59
|
+
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = True if app.debug else False
|
@@ -43,9 +43,11 @@ class LdapManager(LDAPConn):
|
|
43
43
|
self.tls = Tls(
|
44
44
|
local_private_key_file=app.config["LDAP_CLIENT_PRIVATE_KEY"],
|
45
45
|
local_certificate_file=app.config["LDAP_CLIENT_CERT"],
|
46
|
-
validate=
|
47
|
-
|
48
|
-
|
46
|
+
validate=(
|
47
|
+
app.config["LDAP_REQUIRE_CERT"]
|
48
|
+
if app.config.get("LDAP_CLIENT_CERT")
|
49
|
+
else ssl.CERT_NONE
|
50
|
+
),
|
49
51
|
version=app.config["LDAP_TLS_VERSION"],
|
50
52
|
ca_certs_file=app.config["LDAP_CA_CERTS_FILE"],
|
51
53
|
valid_names=app.config["LDAP_VALID_NAMES"],
|
@@ -3,11 +3,15 @@
|
|
3
3
|
* Requires loqusdb version 2.5 or greater.
|
4
4
|
* If multiple instances are configured, version will be default's.
|
5
5
|
"""
|
6
|
+
|
6
7
|
import json
|
7
8
|
import logging
|
8
9
|
import subprocess
|
9
10
|
import traceback
|
10
11
|
from subprocess import CalledProcessError
|
12
|
+
from typing import Dict, Optional
|
13
|
+
|
14
|
+
from flask import flash
|
11
15
|
|
12
16
|
from scout.exceptions.config import ConfigError
|
13
17
|
from scout.utils.scout_requests import get_request_json as api_get
|
@@ -180,16 +184,9 @@ class LoqusDB:
|
|
180
184
|
}
|
181
185
|
return loqus_query
|
182
186
|
|
183
|
-
def get_variant(self, variant_info, loqusdb_id="default"):
|
184
|
-
"""Return information for a variant (SNV or SV) from loqusdb
|
185
|
-
|
186
|
-
Args:
|
187
|
-
variant_info(dict)
|
188
|
-
loqusdb_id(string)
|
187
|
+
def get_variant(self, variant_info: dict, loqusdb_id: str = "default") -> Optional[Dict]:
|
188
|
+
"""Return information for a variant (SNV or SV) from loqusdb"""
|
189
189
|
|
190
|
-
Returns:
|
191
|
-
loqus_variant(dict)
|
192
|
-
"""
|
193
190
|
loqus_instance = self.loqusdb_settings.get(loqusdb_id)
|
194
191
|
if loqus_instance is None:
|
195
192
|
LOG.error(f"Could not find a Loqus instance with id:{loqusdb_id}")
|
@@ -202,7 +199,7 @@ class LoqusDB:
|
|
202
199
|
return self.get_api_loqus_variant(loqus_instance.get(API_URL), variant_info)
|
203
200
|
|
204
201
|
@staticmethod
|
205
|
-
def get_api_loqus_variant(api_url, variant_info):
|
202
|
+
def get_api_loqus_variant(api_url, variant_info) -> dict:
|
206
203
|
"""get variant data using a Loqus instance available via REST API
|
207
204
|
|
208
205
|
SNV/INDELS can be queried in loqus by defining a simple id. For SVs we need to call them
|
@@ -230,10 +227,15 @@ class LoqusDB:
|
|
230
227
|
search_url = f"{search_url}/?chrom={chrom}&end_chrom={end_chrom}&pos={pos}&end={end}&sv_type={sv_type}"
|
231
228
|
|
232
229
|
search_resp = api_get(search_url)
|
233
|
-
|
234
|
-
|
235
|
-
return
|
236
|
-
|
230
|
+
|
231
|
+
if search_resp.get("status_code") == 200:
|
232
|
+
return search_resp.get("content")
|
233
|
+
if search_resp.get("message"): # An error occurred during the request
|
234
|
+
flash(
|
235
|
+
f"Connection to Loqusdb instance returned error: '{search_resp['message']}'",
|
236
|
+
"warning",
|
237
|
+
)
|
238
|
+
return {}
|
237
239
|
|
238
240
|
def get_exec_loqus_variant(self, loqus_instance, variant_info):
|
239
241
|
"""Get variant data using a local executable instance of Loqus
|
scout/server/links.py
CHANGED
@@ -159,7 +159,7 @@ def genemania(hgnc_symbol):
|
|
159
159
|
|
160
160
|
|
161
161
|
def genenames(hgnc_id):
|
162
|
-
link = "https://www.genenames.org/
|
162
|
+
link = "https://www.genenames.org/data/gene-symbol-report/#!/hgnc_id/HGNC:{}"
|
163
163
|
if not hgnc_id:
|
164
164
|
return None
|
165
165
|
return link.format(hgnc_id)
|
@@ -679,9 +679,9 @@ def snp_links(variant_obj):
|
|
679
679
|
if "rs" in snp:
|
680
680
|
snp_links[snp] = f"https://www.ncbi.nlm.nih.gov/snp/{snp}" # dbSNP
|
681
681
|
elif snp.isnumeric():
|
682
|
-
snp_links[
|
683
|
-
snp
|
684
|
-
|
682
|
+
snp_links[snp] = (
|
683
|
+
f"https://www.ncbi.nlm.nih.gov/clinvar/variation/{snp}" # ClinVar variation
|
684
|
+
)
|
685
685
|
|
686
686
|
return snp_links
|
687
687
|
|
@@ -333,7 +333,7 @@ body {
|
|
333
333
|
|
334
334
|
/* Closed submenu icon */
|
335
335
|
#sidebar-container .list-group .list-group-item[aria-expanded="false"] .submenu-icon::after {
|
336
|
-
content: " \
|
336
|
+
content: " \f0da";
|
337
337
|
font-family: "Font Awesome 5 Free", ui-monospace;
|
338
338
|
font-weight: 900;
|
339
339
|
display: inline;
|
@@ -342,7 +342,7 @@ body {
|
|
342
342
|
}
|
343
343
|
/* Opened submenu icon */
|
344
344
|
#sidebar-container .list-group .list-group-item[aria-expanded="true"] .submenu-icon::after {
|
345
|
-
content: " \
|
345
|
+
content: " \f0d7";
|
346
346
|
font-family: "Font Awesome 5 Free", ui-monospace;
|
347
347
|
font-weight: 900;
|
348
348
|
display: inline;
|
@@ -351,6 +351,24 @@ body {
|
|
351
351
|
}
|
352
352
|
/* end of sidebar-related stuff */
|
353
353
|
|
354
|
+
/* Closed submenu icon - general case */
|
355
|
+
.text-body[aria-expanded="false"] .collapse-icon::after {
|
356
|
+
content: " \f0da";
|
357
|
+
font-family: "Font Awesome 5 Free", ui-monospace;
|
358
|
+
font-weight: 900;
|
359
|
+
display: inline;
|
360
|
+
text-align: left;
|
361
|
+
}
|
362
|
+
/* Open submenu icon - general case */
|
363
|
+
.text-body[aria-expanded="true"] .collapse-icon::after {
|
364
|
+
content: " \f0d7";
|
365
|
+
font-family: "Font Awesome 5 Free", ui-monospace;
|
366
|
+
font-weight: 900;
|
367
|
+
display: inline;
|
368
|
+
text-align: left;
|
369
|
+
}
|
370
|
+
/* end of sidebar-related stuff */
|
371
|
+
|
354
372
|
option {
|
355
373
|
width: 100%;
|
356
374
|
}
|
scout/server/utils.py
CHANGED
@@ -206,12 +206,26 @@ def user_institutes(store, login_user):
|
|
206
206
|
return institutes
|
207
207
|
|
208
208
|
|
209
|
+
def case_has_mtdna_report(case_obj: dict):
|
210
|
+
"""Display mtDNA report on the case sidebar only for some specific cases."""
|
211
|
+
if case_obj.get("track", "rare") == "cancer":
|
212
|
+
return
|
213
|
+
for ind in case_obj.get("individuals", []):
|
214
|
+
if ind.get("analysis_type") == "wts":
|
215
|
+
continue
|
216
|
+
case_obj["mtdna_report"] = True
|
217
|
+
return
|
218
|
+
|
219
|
+
|
209
220
|
def case_has_chanjo_coverage(case_obj: dict):
|
210
221
|
"""Set case_obj["chanjo_coverage"] to True if there is an instance of chanjo available and case has coverage stats in chanjo."""
|
211
222
|
|
212
|
-
chanjo_instance: bool = bool(current_app.config.get("
|
223
|
+
chanjo_instance: bool = bool(current_app.config.get("chanjo_report"))
|
213
224
|
if case_obj.get("track", "rare") != "cancer" and chanjo_instance:
|
214
|
-
case_obj
|
225
|
+
for ind in case_obj.get("individuals", []):
|
226
|
+
if ind.get("analysis_type") != "wts":
|
227
|
+
case_obj["chanjo_coverage"] = True
|
228
|
+
return
|
215
229
|
|
216
230
|
|
217
231
|
def case_has_chanjo2_coverage(case_obj: dict):
|
scout/utils/acmg.py
CHANGED
@@ -190,27 +190,40 @@ def get_acmg(acmg_terms):
|
|
190
190
|
bs_terms = []
|
191
191
|
# Collection of terms with supporting Benign evidence
|
192
192
|
bp_terms = []
|
193
|
+
|
194
|
+
suffix_map = {
|
195
|
+
"_Strong": {"P": ps_terms, "B": bs_terms},
|
196
|
+
"_Moderate": {"P": pm_terms},
|
197
|
+
"_Supporting": {"P": pp_terms, "B": bp_terms},
|
198
|
+
}
|
199
|
+
|
200
|
+
prefix_map = {
|
201
|
+
"PS": ps_terms,
|
202
|
+
"PM": pm_terms,
|
203
|
+
"PP": pp_terms,
|
204
|
+
"BS": bs_terms,
|
205
|
+
"BP": bp_terms,
|
206
|
+
}
|
207
|
+
|
193
208
|
for term in acmg_terms:
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
elif term.startswith("BP"):
|
213
|
-
bp_terms.append(term)
|
209
|
+
for suffix, prefix_dict in suffix_map.items():
|
210
|
+
if term.endswith(suffix):
|
211
|
+
for prefix, term_list in prefix_dict.items():
|
212
|
+
if term.startswith(prefix):
|
213
|
+
term_list.append(term)
|
214
|
+
break
|
215
|
+
break
|
216
|
+
else:
|
217
|
+
# Do we match any of the two standalone terms
|
218
|
+
if term.startswith("PVS"):
|
219
|
+
pvs = True
|
220
|
+
elif term.startswith("BA"):
|
221
|
+
ba = True
|
222
|
+
else: # Check remaining prefixes if no suffix match or standalone criteria match
|
223
|
+
for prefix, term_list in prefix_map.items():
|
224
|
+
if term.startswith(prefix):
|
225
|
+
term_list.append(term)
|
226
|
+
break
|
214
227
|
|
215
228
|
# We need to start by checking for Pathogenecity
|
216
229
|
pathogenic = is_pathogenic(pvs, ps_terms, pm_terms, pp_terms)
|
scout/utils/scout_requests.py
CHANGED
scout/utils/sort.py
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
from scout.constants import FILE_TYPE_MAP
|
2
|
+
|
3
|
+
|
4
|
+
def get_load_priority(category: str = None, variant_type: str = None, file_type: str = None) -> int:
|
5
|
+
"""
|
6
|
+
Returns most urgent, highest load priority (numerically the lowest prio number) for the given variables
|
7
|
+
from a FILE_TYPE_MAP dict of dicts. Helper useful in a sort function.
|
8
|
+
"""
|
9
|
+
ordered_file_type_map = sorted(FILE_TYPE_MAP.items(), key=lambda ftm: ftm[1]["load_priority"])
|
10
|
+
|
11
|
+
for ftm in ordered_file_type_map:
|
12
|
+
if file_type and file_type != ftm[0]:
|
13
|
+
continue
|
14
|
+
|
15
|
+
if category and category != ftm[1]["category"]:
|
16
|
+
continue
|
17
|
+
|
18
|
+
if variant_type and variant_type != ftm[1]["variant_type"]:
|
19
|
+
continue
|
20
|
+
|
21
|
+
return ftm[1]["load_priority"]
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# coding=UTF-8
|
2
|
+
import logging
|
3
|
+
import re
|
4
|
+
from os.path import abspath, exists, isabs
|
5
|
+
|
6
|
+
LOG = logging.getLogger(__name__)
|
7
|
+
|
8
|
+
REQUIRED_FIELDS = ["name", "type", "url"]
|
9
|
+
TRACK_KEYS = ["name", "type", "format", "url", "indexURL"]
|
10
|
+
URL_PATTERN = re.compile("https?://")
|
11
|
+
|
12
|
+
from typing import List
|
13
|
+
|
14
|
+
|
15
|
+
class AlignTrackHandler:
|
16
|
+
"""Class collecting external IGV tracks stored in the cloud"""
|
17
|
+
|
18
|
+
def init_app(self, app):
|
19
|
+
self.tracks = self.set_custom_tracks(
|
20
|
+
app.config.get("CUSTOM_IGV_TRACKS") or app.config.get("CLOUD_IGV_TRACKS")
|
21
|
+
)
|
22
|
+
|
23
|
+
def track_template(self, track_info: dict) -> dict:
|
24
|
+
"""Provides the template for a VCF track object"""
|
25
|
+
track = {}
|
26
|
+
|
27
|
+
for key in TRACK_KEYS:
|
28
|
+
if key in track_info:
|
29
|
+
track[key] = track_info[key]
|
30
|
+
|
31
|
+
# Save track only of it contains the minimal required keys
|
32
|
+
if all(track.get(key) for key in REQUIRED_FIELDS):
|
33
|
+
return track
|
34
|
+
else:
|
35
|
+
raise FileNotFoundError(
|
36
|
+
f"One or more custom tracks specified in the config file could not be used, Please provide all required keys:{REQUIRED_FIELDS}"
|
37
|
+
)
|
38
|
+
|
39
|
+
def set_local_track_path(self, path: str) -> str:
|
40
|
+
"""Returns the complete path to a local igv track file"""
|
41
|
+
if exists(path) and isabs(path):
|
42
|
+
return path
|
43
|
+
elif exists(path):
|
44
|
+
return abspath(path)
|
45
|
+
else:
|
46
|
+
raise FileNotFoundError(f"Could not verify path to IGV track:{path}")
|
47
|
+
|
48
|
+
def set_custom_tracks(self, track_dictionaries: List[dict]) -> dict:
|
49
|
+
"""Return a list of IGV tracks collected from the config settings
|
50
|
+
|
51
|
+
Args:
|
52
|
+
track_dictionaries: a list of cloud bucket dictionaries containing IGV tracks, can be public or private
|
53
|
+
"""
|
54
|
+
custom_tracks = {"37": [], "38": []}
|
55
|
+
# Loop over the bucket list and collect all public tracks
|
56
|
+
for track_category in track_dictionaries:
|
57
|
+
for track in track_category.get("tracks", []):
|
58
|
+
build = track.get("build")
|
59
|
+
if build not in ["37", "38"]:
|
60
|
+
LOG.warning(
|
61
|
+
"One or more IGV public tracks could not be used, Please provide a genome build ('37', '38') for it."
|
62
|
+
)
|
63
|
+
continue
|
64
|
+
track_obj = self.track_template(track)
|
65
|
+
|
66
|
+
if not URL_PATTERN.search(track_obj["url"]): # It's a local file
|
67
|
+
track_obj["url"] = self.set_local_track_path(track_obj["url"])
|
68
|
+
|
69
|
+
custom_tracks[build].append(track_obj)
|
70
|
+
return custom_tracks
|
@@ -1,12 +1,10 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: scout-browser
|
3
|
-
Version: 4.
|
3
|
+
Version: 4.84
|
4
4
|
Summary: Clinical DNA variant visualizer and browser.
|
5
5
|
Home-page: https://github.com/Clinical-Genomics/scout
|
6
6
|
Author: Måns Magnusson
|
7
7
|
Author-email: mans.magnusson@scilifelab.se
|
8
|
-
License: UNKNOWN
|
9
|
-
Platform: UNKNOWN
|
10
8
|
Classifier: Environment :: Web Environment
|
11
9
|
Classifier: Intended Audience :: Developers
|
12
10
|
Classifier: License :: OSI Approved :: BSD License
|
@@ -16,6 +14,7 @@ Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
|
16
14
|
Classifier: Topic :: Software Development :: Libraries
|
17
15
|
Classifier: Programming Language :: Python :: 3.6
|
18
16
|
Description-Content-Type: text/markdown
|
17
|
+
License-File: LICENSE
|
19
18
|
Requires-Dist: werkzeug <3
|
20
19
|
Requires-Dist: Flask >=2.0
|
21
20
|
Requires-Dist: Flask-Bootstrap
|
@@ -338,5 +337,3 @@ be found in [CONTRIBUTING](CONTRIBUTING.md).
|
|
338
337
|
[black-url]: https://github.com/psf/black
|
339
338
|
[woke-image]: https://github.com/Clinical-Genomics/scout/actions/workflows/woke.yml/badge.svg
|
340
339
|
[woke-url]: https://github.com/Clinical-Genomics/scout/actions/workflows/woke.yml
|
341
|
-
|
342
|
-
|