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.
Files changed (87) hide show
  1. scout/__version__.py +1 -1
  2. scout/adapter/client.py +1 -0
  3. scout/adapter/mongo/base.py +0 -1
  4. scout/adapter/mongo/case.py +19 -37
  5. scout/adapter/mongo/case_events.py +98 -2
  6. scout/adapter/mongo/hgnc.py +39 -22
  7. scout/adapter/mongo/institute.py +3 -9
  8. scout/adapter/mongo/panel.py +2 -1
  9. scout/adapter/mongo/variant.py +12 -2
  10. scout/adapter/mongo/variant_loader.py +156 -141
  11. scout/build/genes/hgnc_gene.py +5 -134
  12. scout/commands/base.py +1 -0
  13. scout/commands/download/ensembl.py +1 -0
  14. scout/commands/download/everything.py +1 -0
  15. scout/commands/download/exac.py +1 -0
  16. scout/commands/download/hgnc.py +1 -0
  17. scout/commands/download/hpo.py +1 -0
  18. scout/commands/download/omim.py +1 -0
  19. scout/commands/export/database.py +1 -0
  20. scout/commands/load/panel.py +1 -0
  21. scout/commands/load/report.py +1 -0
  22. scout/commands/update/case.py +10 -10
  23. scout/commands/update/individual.py +6 -1
  24. scout/commands/update/omim.py +1 -0
  25. scout/commands/update/panelapp.py +1 -0
  26. scout/constants/file_types.py +86 -13
  27. scout/export/exon.py +1 -0
  28. scout/load/__init__.py +0 -1
  29. scout/load/all.py +8 -5
  30. scout/load/hgnc_gene.py +1 -1
  31. scout/load/panel.py +8 -4
  32. scout/load/setup.py +1 -0
  33. scout/models/case/case_loading_models.py +6 -16
  34. scout/models/hgnc_map.py +50 -87
  35. scout/models/phenotype_term.py +3 -3
  36. scout/parse/case.py +0 -1
  37. scout/parse/disease_terms.py +1 -0
  38. scout/parse/omim.py +1 -0
  39. scout/parse/orpha.py +1 -0
  40. scout/parse/panel.py +40 -15
  41. scout/parse/variant/conservation.py +1 -0
  42. scout/resources/__init__.py +3 -0
  43. scout/server/app.py +4 -50
  44. scout/server/blueprints/alignviewers/controllers.py +15 -17
  45. scout/server/blueprints/alignviewers/templates/alignviewers/igv_viewer.html +13 -3
  46. scout/server/blueprints/alignviewers/views.py +10 -15
  47. scout/server/blueprints/cases/controllers.py +70 -73
  48. scout/server/blueprints/cases/templates/cases/case.html +94 -71
  49. scout/server/blueprints/cases/templates/cases/collapsible_actionbar.html +1 -1
  50. scout/server/blueprints/cases/templates/cases/phenotype.html +8 -6
  51. scout/server/blueprints/cases/templates/cases/utils.html +3 -3
  52. scout/server/blueprints/cases/views.py +8 -6
  53. scout/server/blueprints/panels/forms.py +1 -0
  54. scout/server/blueprints/variant/controllers.py +14 -19
  55. scout/server/blueprints/variant/templates/variant/acmg.html +25 -16
  56. scout/server/blueprints/variant/templates/variant/components.html +11 -6
  57. scout/server/blueprints/variant/views.py +5 -2
  58. scout/server/blueprints/variants/controllers.py +12 -28
  59. scout/server/blueprints/variants/views.py +1 -1
  60. scout/server/config.py +16 -4
  61. scout/server/extensions/__init__.py +4 -2
  62. scout/server/extensions/beacon_extension.py +1 -0
  63. scout/server/extensions/bionano_extension.py +1 -0
  64. scout/server/extensions/chanjo_extension.py +59 -0
  65. scout/server/extensions/gens_extension.py +1 -0
  66. scout/server/extensions/ldap_extension.py +5 -3
  67. scout/server/extensions/loqus_extension.py +16 -14
  68. scout/server/extensions/matchmaker_extension.py +1 -0
  69. scout/server/extensions/mongo_extension.py +1 -0
  70. scout/server/extensions/phenopacket_extension.py +1 -0
  71. scout/server/extensions/rerunner_extension.py +1 -0
  72. scout/server/links.py +4 -4
  73. scout/server/static/bs_styles.css +20 -2
  74. scout/server/utils.py +16 -2
  75. scout/utils/acmg.py +33 -20
  76. scout/utils/ensembl_rest_clients.py +1 -0
  77. scout/utils/scout_requests.py +1 -0
  78. scout/utils/sort.py +21 -0
  79. scout/utils/track_resources.py +70 -0
  80. {scout_browser-4.82.2.dist-info → scout_browser-4.84.dist-info}/METADATA +2 -5
  81. {scout_browser-4.82.2.dist-info → scout_browser-4.84.dist-info}/RECORD +85 -84
  82. {scout_browser-4.82.2.dist-info → scout_browser-4.84.dist-info}/WHEEL +1 -1
  83. {scout_browser-4.82.2.dist-info → scout_browser-4.84.dist-info}/entry_points.txt +0 -1
  84. scout/load/case.py +0 -36
  85. scout/utils/cloud_resources.py +0 -61
  86. {scout_browser-4.82.2.dist-info → scout_browser-4.84.dist-info}/LICENSE +0 -0
  87. {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
- # Cloud IGV tracks can be configured here to allow users to enable them on their IGV views.
80
- # A number of publicly-available tracks can be found here: https://trackhubregistry.org/
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.cloud_resources import AlignTrackHandler
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
- cloud_tracks = AlignTrackHandler()
37
+ config_igv_tracks = AlignTrackHandler()
37
38
  bionano_access = BioNanoAccessAPI()
39
+ chanjo_report = ChanjoReport()
@@ -1,6 +1,7 @@
1
1
  """Scout supports integration with the Clinical Genomics SciLifeLab Beacon
2
2
  cgbeacon2: https://github.com/Clinical-Genomics/cgbeacon2
3
3
  """
4
+
4
5
  import datetime
5
6
  import logging
6
7
 
@@ -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
@@ -2,6 +2,7 @@
2
2
 
3
3
  * Requires gens version 1.1.1 or greater
4
4
  """
5
+
5
6
  import logging
6
7
 
7
8
  LOG = logging.getLogger(__name__)
@@ -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=app.config["LDAP_REQUIRE_CERT"]
47
- if app.config.get("LDAP_CLIENT_CERT")
48
- else ssl.CERT_NONE,
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
- if search_resp.get("status_code") != 200:
234
- LOG.info(search_resp.get("detail"))
235
- return {}
236
- return search_resp.get("content")
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
@@ -1,6 +1,7 @@
1
1
  """Code for MatchMaker Exchange integration
2
2
  Tested with PatientMatcher: https://github.com/Clinical-Genomics/patientMatcher
3
3
  """
4
+
4
5
  import datetime
5
6
  import json
6
7
  import logging
@@ -1,4 +1,5 @@
1
1
  """Code for flask mongodb extension in scout"""
2
+
2
3
  import os
3
4
 
4
5
  from scout.adapter.client import get_connection
@@ -3,6 +3,7 @@ Extension to Scout for Phenopacket integration (http://phenopackets.org).
3
3
  Write and read JSON Phenopackets, and interact with phenopacket-api backend
4
4
  (https://github.com/squeezeday/phenopacket-api).
5
5
  """
6
+
6
7
  import json as json_lib
7
8
  import logging
8
9
 
@@ -1,4 +1,5 @@
1
1
  """Code for Rerunner integration."""
2
+
2
3
  import logging
3
4
 
4
5
  LOG = logging.getLogger(__name__)
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/cgi-bin/gene_symbol_report?hgnc_id=HGNC:{}"
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
- ] = f"https://www.ncbi.nlm.nih.gov/clinvar/variation/{snp}" # ClinVar variation
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: " \f0d7";
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: " \f0da";
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("SQLALCHEMY_DATABASE_URI"))
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["chanjo_coverage"] = True
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
- if term.endswith("_Strong"):
195
- ps_terms.append(term)
196
- elif term.endswith("_Moderate"):
197
- pm_terms.append(term)
198
- elif term.endswith("_Supporting"):
199
- pp_terms.append(term)
200
- elif term.startswith("PVS"):
201
- pvs = True
202
- elif term.startswith("PS"):
203
- ps_terms.append(term)
204
- elif term.startswith("PM"):
205
- pm_terms.append(term)
206
- elif term.startswith("PP"):
207
- pp_terms.append(term)
208
- elif term.startswith("BA"):
209
- ba = True
210
- elif term.startswith("BS"):
211
- bs_terms.append(term)
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)
@@ -1,4 +1,5 @@
1
1
  """Code for talking to ensembl rest API"""
2
+
2
3
  import logging
3
4
  from urllib.parse import urlencode
4
5
 
@@ -1,4 +1,5 @@
1
1
  """Code for performing requests"""
2
+
2
3
  import logging
3
4
  import urllib.request
4
5
  import zlib
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.82.2
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
-