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/app.py CHANGED
@@ -8,7 +8,6 @@ from urllib.parse import parse_qsl, unquote, urlsplit
8
8
 
9
9
  import coloredlogs
10
10
  from flask import Flask, current_app, redirect, request, url_for
11
- from flask_babel import Babel
12
11
  from flask_cors import CORS
13
12
  from flask_login import current_user
14
13
  from markdown import markdown as python_markdown
@@ -36,16 +35,6 @@ from .blueprints import (
36
35
 
37
36
  LOG = logging.getLogger(__name__)
38
37
 
39
- try:
40
- from chanjo_report.server.app import configure_template_filters
41
- from chanjo_report.server.blueprints import report_bp
42
- from chanjo_report.server.extensions import api as chanjo_api
43
- except ImportError:
44
- chanjo_api = None
45
- report_bp = None
46
- configure_template_filters = None
47
- LOG.info("chanjo report not installed!")
48
-
49
38
 
50
39
  def create_app(config_file=None, config=None):
51
40
  """Flask app factory function."""
@@ -111,8 +100,8 @@ def configure_extensions(app):
111
100
  extensions.mail.init_app(app)
112
101
 
113
102
  if app.config.get("SQLALCHEMY_DATABASE_URI"):
103
+ extensions.chanjo_report.init_app(app)
114
104
  LOG.info("Chanjo extension enabled")
115
- configure_coverage(app)
116
105
 
117
106
  if app.config.get("LOQUSDB_SETTINGS"):
118
107
  LOG.info("LoqusDB enabled")
@@ -157,9 +146,9 @@ def configure_extensions(app):
157
146
  # setup connection to google oauth2
158
147
  configure_oauth_login(app)
159
148
 
160
- if app.config.get("CLOUD_IGV_TRACKS"):
161
- LOG.info("Collecting IGV tracks from cloud resources")
162
- extensions.cloud_tracks.init_app(app)
149
+ if app.config.get("CUSTOM_IGV_TRACKS") or app.config.get("CLOUD_IGV_TRACKS"):
150
+ LOG.info("Collecting IGV tracks from cloud or local resources")
151
+ extensions.config_igv_tracks.init_app(app)
163
152
 
164
153
  if app.config.get("PHENOPACKET_API_URL"):
165
154
  LOG.info("Enable Phenopacket API")
@@ -319,38 +308,3 @@ def configure_email_logging(app):
319
308
  )
320
309
  )
321
310
  app.logger.addHandler(mail_handler)
322
-
323
-
324
- def configure_coverage(app):
325
- """Setup coverage related extensions."""
326
- # setup chanjo report
327
- app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = True if app.debug else False
328
- if chanjo_api:
329
- chanjo_api.init_app(app)
330
- configure_template_filters(app)
331
- # register chanjo report blueprint
332
- app.register_blueprint(report_bp, url_prefix="/reports")
333
-
334
- babel = Babel()
335
-
336
- def get_locale():
337
- """Determine locale to use for translations."""
338
- accept_languages = current_app.config.get("ACCEPT_LANGUAGES", ["en"])
339
-
340
- # first check request args
341
- session_language = Markup.escape(request.args.get("lang"))
342
- if session_language in accept_languages:
343
- current_app.logger.info("using session language: %s", session_language)
344
- return session_language
345
-
346
- # language can be forced in config
347
- user_language = current_app.config.get("REPORT_LANGUAGE")
348
- if user_language:
349
- return user_language
350
-
351
- # try to guess the language from the user accept header that
352
- # the browser transmits. We support de/fr/en in this example.
353
- # The best match wins.
354
- return request.accept_languages.best_match(accept_languages)
355
-
356
- babel.init_app(app, locale_selector=get_locale)
@@ -7,7 +7,7 @@ from flask import flash, session
7
7
  from flask_login import current_user
8
8
 
9
9
  from scout.constants import CASE_SPECIFIC_TRACKS, HUMAN_REFERENCE, IGV_TRACKS
10
- from scout.server.extensions import cloud_tracks, store
10
+ from scout.server.extensions import config_igv_tracks, store
11
11
  from scout.server.utils import case_append_alignments, find_index
12
12
  from scout.utils.ensembl_rest_clients import EnsemblRestApiClient
13
13
 
@@ -34,15 +34,16 @@ def check_session_tracks(resource):
34
34
  return True
35
35
 
36
36
 
37
- def set_session_tracks(display_obj):
37
+ def set_session_tracks(display_obj: dict):
38
38
  """Save igv tracks as a session object. This way it's easy to verify that a user is requesting one of these files from remote_static view endpoint
39
39
 
40
40
  Args:
41
41
  display_obj(dict): A display object containing case name, list of genes, locus and tracks
42
42
  """
43
+
43
44
  session_tracks = list(display_obj.get("reference_track", {}).values())
44
45
  for key, track_items in display_obj.items():
45
- if key not in ["tracks", "custom_tracks", "sample_tracks", "cloud_public_tracks"]:
46
+ if key not in ["tracks", "custom_tracks", "sample_tracks", "config_custom_tracks"]:
46
47
  continue
47
48
  for track_item in track_items:
48
49
  session_tracks += list(track_item.values())
@@ -107,7 +108,7 @@ def make_igv_tracks(case_obj, variant_id, chrom=None, start=None, stop=None):
107
108
  set_case_specific_tracks(display_obj, case_obj)
108
109
 
109
110
  # Set up custom cloud public tracks, if available
110
- set_cloud_public_tracks(display_obj, build)
111
+ set_config_custom_tracks(display_obj, build)
111
112
 
112
113
  display_obj["display_center_guide"] = True
113
114
 
@@ -295,23 +296,20 @@ def set_case_specific_tracks(display_obj, case_obj):
295
296
  display_obj[track] = track_info
296
297
 
297
298
 
298
- def set_cloud_public_tracks(display_obj, build):
299
- """Set up custom public tracks stored in a cloud bucket, according to the user preferences
300
-
301
- Args:
302
- display_obj(dict) dictionary containing all tracks info
303
- build(string) "37" or "38"
304
- """
299
+ def set_config_custom_tracks(display_obj: dict, build: str):
300
+ """Set up custom public or private tracks stored in a cloud bucket or locally. These tracks were those specified in the Scout config file.
301
+ Respect user's preferences."""
305
302
  user_obj = store.user(email=current_user.email)
306
303
  custom_tracks_names = user_obj.get("igv_tracks")
307
304
 
308
- cloud_public_tracks = []
309
- if hasattr(cloud_tracks, "public_tracks"):
310
- build_tracks = cloud_tracks.public_tracks.get(build, [])
305
+ config_custom_tracks = []
306
+
307
+ if hasattr(config_igv_tracks, "tracks"):
308
+ build_tracks = config_igv_tracks.tracks.get(build, [])
311
309
  for track in build_tracks:
312
310
  # Do not display track if user doesn't want to see it
313
311
  if custom_tracks_names and track["name"] not in custom_tracks_names:
314
312
  continue
315
- cloud_public_tracks.append(track)
316
- if cloud_public_tracks:
317
- display_obj["cloud_public_tracks"] = cloud_public_tracks
313
+ config_custom_tracks.append(track)
314
+ if config_custom_tracks:
315
+ display_obj["config_custom_tracks"] = config_custom_tracks
@@ -64,13 +64,23 @@
64
64
  {% endif %}
65
65
  },
66
66
  {% endfor %}
67
- {% for custTrack in cloud_public_tracks %}
67
+ {% for custTrack in config_custom_tracks %}
68
68
  {
69
69
  name: "{{ custTrack.name }}",
70
70
  type: "{{ custTrack.type }}",
71
71
  format: "{{ custTrack.format }}",
72
- url: "{{ url_for('alignviewers.remote_cors', remote_url=custTrack.url) }}",
73
- indexURL: "{{ url_for('alignviewers.remote_cors', remote_url=custTrack.indexURL) }}"
72
+ height: 70,
73
+ {% if "http" in custTrack.url %}
74
+ url: "{{ url_for('alignviewers.remote_cors', remote_url=custTrack.url) }}",
75
+ {% if custTrack.indexURL %}
76
+ indexURL: "{{ url_for('alignviewers.remote_cors', remote_url=custTrack.indexURL) }}",
77
+ {% endif %}
78
+ {% else %}
79
+ url: "{{ url_for('alignviewers.remote_static', file=custTrack.url) }}",
80
+ {% if custTrack.indexURL %}
81
+ indexURL: "{{ url_for('alignviewers.remote_static', file=custTrack.indexURL) }}",
82
+ {% endif %}
83
+ {% endif %}
74
84
  },
75
85
  {% endfor %}
76
86
  {% for wtrack in rhocall_wig %}
@@ -1,5 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  import logging
3
+ from typing import Optional
3
4
 
4
5
  import requests
5
6
  from flask import (
@@ -11,7 +12,6 @@ from flask import (
11
12
  request,
12
13
  session,
13
14
  )
14
- from flask_login import current_user
15
15
 
16
16
  from scout.server.extensions import store
17
17
  from scout.server.utils import institute_and_case
@@ -119,20 +119,15 @@ def sashimi_igv(institute_id, case_name, variant_id=None):
119
119
  @alignviewers_bp.route(
120
120
  "/<institute_id>/<case_name>/<variant_id>/<chrom>/<start>/<stop>/igv", methods=["GET"]
121
121
  ) # from SV variant page, where you have to pass breakpoints coordinates
122
- def igv(institute_id, case_name, variant_id=None, chrom=None, start=None, stop=None):
123
- """Visualize BAM alignments using igv.js (https://github.com/igvteam/igv.js)
124
-
125
- Args:
126
- institute_id(str): _id of an institute
127
- case_name(str): dislay_name of a case
128
- variant_id(str/None): variant _id or None
129
- chrom(str/None): requested chromosome [1-22], X, Y, [M-MT]
130
- start(int/None): start of the genomic interval to be displayed
131
- stop(int/None): stop of the genomic interval to be displayed
132
-
133
- Returns:
134
- a string, corresponging to the HTML rendering of the IGV alignments page
135
- """
122
+ def igv(
123
+ institute_id: str,
124
+ case_name: str,
125
+ variant_id: Optional[str] = None,
126
+ chrom: Optional[str] = None,
127
+ start: Optional[int] = None,
128
+ stop: Optional[int] = None,
129
+ ) -> Response:
130
+ """Visualize BAM alignments using igv.js (https://github.com/igvteam/igv.js)."""
136
131
  _, case_obj = institute_and_case(
137
132
  store, institute_id, case_name
138
133
  ) # This function takes care of checking if user is authorized to see resource
@@ -48,6 +48,7 @@ from scout.server.utils import (
48
48
  case_has_chanjo2_coverage,
49
49
  case_has_chanjo_coverage,
50
50
  case_has_mt_alignments,
51
+ case_has_mtdna_report,
51
52
  case_has_rna_tracks,
52
53
  institute_and_case,
53
54
  )
@@ -62,6 +63,8 @@ JSON_HEADERS = {
62
63
 
63
64
  COVERAGE_REPORT_TIMEOUT = 20
64
65
 
66
+ PANEL_PROJECTION = {"version": 1, "display_name": 1, "genes": 1}
67
+
65
68
 
66
69
  def phenomizer_diseases(hpo_ids, case_obj, p_value_treshold=1):
67
70
  """Return the list of HGNC symbols that match annotated HPO terms on Phenomizer
@@ -278,22 +281,16 @@ def sma_case(store, institute_obj, case_obj):
278
281
  return data
279
282
 
280
283
 
281
- def case(store, institute_obj, case_obj):
284
+ def case(
285
+ store: MongoAdapter, institute_obj: dict, case_obj: dict, hide_matching: bool = True
286
+ ) -> dict:
282
287
  """Preprocess a single case.
283
288
 
284
289
  Prepare the case to be displayed in the case view.
285
290
 
286
- Args:
287
- store(adapter.MongoAdapter)
288
- institute_obj(models.Institute)
289
- case_obj(models.Case)
290
-
291
- Returns:
292
- data(dict): includes the cases, how many there are and the limit.
293
-
291
+ The return data dict includes the cases, how many there are and the limit.
294
292
  """
295
293
  # Convert individual information to more readable format
296
-
297
294
  _populate_case_individuals(case_obj)
298
295
 
299
296
  case_obj["assignees"] = [
@@ -305,6 +302,7 @@ def case(store, institute_obj, case_obj):
305
302
  case_has_mt_alignments(case_obj)
306
303
  case_has_chanjo_coverage(case_obj)
307
304
  case_has_chanjo2_coverage(case_obj)
305
+ case_has_mtdna_report(case_obj)
308
306
 
309
307
  case_groups = {}
310
308
  case_group_label = {}
@@ -395,31 +393,40 @@ def case(store, institute_obj, case_obj):
395
393
  "case_images", case_obj["custom_images"].get("case", {})
396
394
  )
397
395
 
398
- # Limit secondary findings according to institute settings
399
- limit_genes = store.safe_genes_filter(institute_obj["_id"])
396
+ other_causatives = []
397
+ other_causatives_in_default_panels = []
398
+ default_managed_variants = []
399
+ managed_variants = []
400
400
 
401
- limit_genes_default_panels = _limit_genes_on_default_panels(
402
- case_obj["default_genes"], limit_genes
403
- )
401
+ if hide_matching is False:
402
+ # Limit secondary findings according to institute settings
403
+ limit_genes = store.safe_genes_filter(institute_obj["_id"])
404
404
 
405
- other_causatives, other_causatives_in_default_panels = _matching_causatives(
406
- store, case_obj, limit_genes, limit_genes_default_panels
407
- )
405
+ limit_genes_default_panels = _limit_genes_on_default_panels(
406
+ case_obj["default_genes"], limit_genes
407
+ )
408
408
 
409
- data = {
410
- "institute": institute_obj,
411
- "case": case_obj,
412
- "other_causatives": other_causatives,
413
- "default_other_causatives": other_causatives_in_default_panels,
414
- "managed_variants": [
409
+ other_causatives, other_causatives_in_default_panels = _matching_causatives(
410
+ store, case_obj, limit_genes, limit_genes_default_panels
411
+ )
412
+
413
+ managed_variants = [
415
414
  var for var in store.check_managed(case_obj=case_obj, limit_genes=limit_genes)
416
- ],
417
- "default_managed_variants": [
415
+ ]
416
+ default_managed_variants = [
418
417
  var
419
418
  for var in store.check_managed(
420
419
  case_obj=case_obj, limit_genes=limit_genes_default_panels
421
420
  )
422
- ],
421
+ ]
422
+
423
+ data = {
424
+ "institute": institute_obj,
425
+ "case": case_obj,
426
+ "other_causatives": other_causatives,
427
+ "default_other_causatives": other_causatives_in_default_panels,
428
+ "managed_variants": managed_variants,
429
+ "default_managed_variants": default_managed_variants,
423
430
  "comments": store.events(institute_obj, case=case_obj, comments=True),
424
431
  "hpo_groups": pheno_groups,
425
432
  "case_groups": case_groups,
@@ -441,6 +448,7 @@ def case(store, institute_obj, case_obj):
441
448
  "mme_nodes": matchmaker.connected_nodes,
442
449
  "gens_info": gens.connection_settings(case_obj.get("genome_build")),
443
450
  "display_rerunner": rerunner.connection_settings.get("display", False),
451
+ "hide_matching": hide_matching,
444
452
  }
445
453
 
446
454
  return data
@@ -496,8 +504,12 @@ def _get_default_panel_genes(store: MongoAdapter, case_obj: dict) -> list:
496
504
  continue
497
505
  panel_name = panel_info["panel_name"]
498
506
  panel_version = panel_info.get("version")
499
- panel_obj = store.gene_panel(panel_name, version=panel_version)
500
- latest_panel = store.gene_panel(panel_name)
507
+ panel_obj = store.gene_panel(
508
+ panel_name,
509
+ version=panel_version,
510
+ projection=PANEL_PROJECTION,
511
+ )
512
+ latest_panel = store.gene_panel(panel_name, projection=PANEL_PROJECTION)
501
513
  panel_info["removed"] = False if latest_panel is None else latest_panel.get("hidden", False)
502
514
  if not panel_obj:
503
515
  panel_obj = latest_panel
@@ -555,19 +567,17 @@ def check_outdated_gene_panel(panel_obj, latest_panel):
555
567
  missing_genes, extra_genes
556
568
  """
557
569
  # Create a list of minified gene object for the case panel {hgnc_id, gene_symbol}
558
- case_panel_genes = [
559
- {"hgnc_id": gene["hgnc_id"], "symbol": gene.get("symbol", gene["hgnc_id"])}
560
- for gene in panel_obj["genes"]
561
- ]
570
+ case_panel_genes = set([gene.get("symbol", gene["hgnc_id"]) for gene in panel_obj["genes"]])
562
571
  # And for the latest panel
563
- latest_panel_genes = [
564
- {"hgnc_id": gene["hgnc_id"], "symbol": gene.get("symbol", gene["hgnc_id"])}
565
- for gene in latest_panel["genes"]
566
- ]
572
+ latest_panel_genes = set(
573
+ [gene.get("symbol", gene["hgnc_id"]) for gene in latest_panel["genes"]]
574
+ )
567
575
  # Extract the genes unique to case panel
568
- extra_genes = [gene["symbol"] for gene in case_panel_genes if gene not in latest_panel_genes]
576
+ extra_genes = case_panel_genes.difference(latest_panel_genes)
577
+
569
578
  # Extract the genes unique to latest panel
570
- missing_genes = [gene["symbol"] for gene in latest_panel_genes if gene not in case_panel_genes]
579
+ missing_genes = latest_panel_genes.difference(case_panel_genes)
580
+
571
581
  return extra_genes, missing_genes
572
582
 
573
583
 
@@ -674,7 +684,7 @@ def case_report_content(store: MongoAdapter, institute_obj: dict, case_obj: dict
674
684
  return data
675
685
 
676
686
 
677
- def mt_coverage_stats(individuals, ref_chrom="14"):
687
+ def mt_coverage_stats(individuals, ref_chrom="14") -> dict:
678
688
  """Send a request to chanjo report endpoint to retrieve MT vs autosome coverage stats
679
689
 
680
690
  Args:
@@ -732,10 +742,9 @@ def mt_excel_files(store, case_obj, temp_excel_dir):
732
742
  """
733
743
  today = datetime.datetime.now().strftime(DATE_DAY_FORMATTER)
734
744
  samples = case_obj.get("individuals")
735
- file_header = MT_EXPORT_HEADER
736
745
  coverage_stats = None
737
746
  # if chanjo connection is established, include MT vs AUTOSOME coverage stats
738
- if current_app.config.get("SQLALCHEMY_DATABASE_URI"):
747
+ if current_app.config.get("chanjo_report"):
739
748
  coverage_stats = mt_coverage_stats(samples)
740
749
 
741
750
  query = {"chrom": "MT"}
@@ -765,9 +774,8 @@ def mt_excel_files(store, case_obj, temp_excel_dir):
765
774
  for col, field in enumerate(line): # each field in line becomes a cell
766
775
  Report_Sheet.write(row, col, field)
767
776
 
768
- if bool(
769
- coverage_stats
770
- ): # it's None if app is not connected to Chanjo or {} if samples are not in Chanjo db
777
+ # coverage_stats is None if app is not connected to Chanjo or {} if samples are not in Chanjo db
778
+ if coverage_stats and sample_id in coverage_stats:
771
779
  # Write coverage stats header after introducing 2 empty lines
772
780
  for col, field in enumerate(MT_COV_STATS_HEADER):
773
781
  Report_Sheet.write(row + 3, col, field)
@@ -805,27 +813,6 @@ def update_synopsis(store, institute_obj, case_obj, user_obj, new_synopsis):
805
813
  store.update_synopsis(institute_obj, case_obj, user_obj, link, content=new_synopsis)
806
814
 
807
815
 
808
- def _update_case(store, case_obj, user_obj, institute_obj, verb):
809
- """Update case with new sample data, and create an associated event"""
810
- store.update_case(case_obj, keep_date=True)
811
-
812
- link = url_for(
813
- "cases.case",
814
- institute_id=institute_obj["_id"],
815
- case_name=case_obj["display_name"],
816
- )
817
-
818
- store.create_event(
819
- institute=institute_obj,
820
- case=case_obj,
821
- user=user_obj,
822
- link=link,
823
- category="case",
824
- verb=verb,
825
- subject=case_obj["display_name"],
826
- )
827
-
828
-
829
816
  def update_individuals(store, institute_obj, case_obj, user_obj, ind, age, tissue):
830
817
  """Handle update of individual data (age and/or Tissue type) for a case"""
831
818
 
@@ -841,8 +828,13 @@ def update_individuals(store, institute_obj, case_obj, user_obj, ind, age, tissu
841
828
 
842
829
  case_obj["individuals"] = case_individuals
843
830
 
844
- verb = "update_individual"
845
- _update_case(store, case_obj, user_obj, institute_obj, verb)
831
+ link = url_for(
832
+ "cases.case",
833
+ institute_id=institute_obj["_id"],
834
+ case_name=case_obj["display_name"],
835
+ )
836
+
837
+ store.update_case_individual(case_obj, user_obj, institute_obj, link)
846
838
 
847
839
 
848
840
  def update_cancer_samples(
@@ -866,8 +858,13 @@ def update_cancer_samples(
866
858
 
867
859
  case_obj["individuals"] = case_samples
868
860
 
869
- verb = "update_sample"
870
- _update_case(store, case_obj, user_obj, institute_obj, verb)
861
+ link = url_for(
862
+ "cases.case",
863
+ institute_id=institute_obj["_id"],
864
+ case_name=case_obj["display_name"],
865
+ )
866
+
867
+ store.update_case_sample(case_obj, user_obj, institute_obj, link)
871
868
 
872
869
 
873
870
  def _all_hpo_gene_list_genes(
@@ -1431,13 +1428,13 @@ def _matching_causatives(
1431
1428
  other_causatives_in_default_panels = []
1432
1429
 
1433
1430
  for causative in matching_causatives:
1434
- hgnc_ids = [gene.get("hgnc_id") for gene in causative.get("genes", [])]
1431
+ hgnc_ids = {gene.get("hgnc_id") for gene in causative.get("genes", [])}
1435
1432
  # Fetch all matching causatives if no causatives_filter defined
1436
1433
  # or only causatives matching the filter:
1437
- if not other_causatives_filter or (set(hgnc_ids) & set(other_causatives_filter)):
1434
+ if not other_causatives_filter or (hgnc_ids & set(other_causatives_filter)):
1438
1435
  other_causatives.append(causative)
1439
1436
  # Only matching causatives in default gene panels:
1440
- if set(hgnc_ids) & set(other_causatives_in_default_panels_filter):
1437
+ if hgnc_ids & set(other_causatives_in_default_panels_filter):
1441
1438
  other_causatives_in_default_panels.append(causative)
1442
1439
 
1443
1440
  return other_causatives, other_causatives_in_default_panels