scout-browser 4.98.0__py3-none-any.whl → 4.100.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. scout/adapter/mongo/case.py +30 -15
  2. scout/adapter/mongo/clinvar.py +23 -31
  3. scout/adapter/mongo/event.py +14 -4
  4. scout/adapter/mongo/institute.py +42 -55
  5. scout/adapter/mongo/omics_variant.py +14 -1
  6. scout/adapter/mongo/query.py +24 -1
  7. scout/adapter/mongo/variant.py +44 -22
  8. scout/adapter/mongo/variant_loader.py +169 -186
  9. scout/build/individual.py +5 -1
  10. scout/build/variant/variant.py +8 -0
  11. scout/commands/download/ensembl.py +18 -3
  12. scout/commands/load/research.py +2 -3
  13. scout/commands/update/individual.py +3 -0
  14. scout/commands/update/panelapp.py +15 -2
  15. scout/constants/__init__.py +6 -2
  16. scout/constants/clnsig.py +2 -0
  17. scout/constants/file_types.py +12 -0
  18. scout/constants/igv_tracks.py +9 -6
  19. scout/constants/indexes.py +5 -4
  20. scout/constants/panels.py +3 -0
  21. scout/constants/query_terms.py +1 -0
  22. scout/constants/variant_tags.py +6 -6
  23. scout/demo/643594.config.yaml +1 -0
  24. scout/load/panelapp.py +11 -5
  25. scout/models/case/case.py +1 -0
  26. scout/models/case/case_loading_models.py +7 -1
  27. scout/parse/ensembl.py +8 -3
  28. scout/parse/variant/clnsig.py +38 -0
  29. scout/parse/variant/genotype.py +4 -10
  30. scout/parse/variant/models.py +5 -11
  31. scout/parse/variant/rank_score.py +5 -13
  32. scout/parse/variant/variant.py +90 -111
  33. scout/server/app.py +39 -22
  34. scout/server/blueprints/alignviewers/controllers.py +29 -10
  35. scout/server/blueprints/alignviewers/templates/alignviewers/igv_viewer.html +51 -11
  36. scout/server/blueprints/cases/controllers.py +9 -3
  37. scout/server/blueprints/cases/templates/cases/case_report.html +25 -13
  38. scout/server/blueprints/cases/templates/cases/chanjo2_form.html +1 -1
  39. scout/server/blueprints/cases/templates/cases/collapsible_actionbar.html +1 -1
  40. scout/server/blueprints/cases/templates/cases/gene_panel.html +1 -1
  41. scout/server/blueprints/cases/templates/cases/utils.html +25 -6
  42. scout/server/blueprints/clinvar/controllers.py +34 -15
  43. scout/server/blueprints/clinvar/templates/clinvar/clinvar_submissions.html +34 -12
  44. scout/server/blueprints/clinvar/templates/clinvar/multistep_add_variant.html +14 -5
  45. scout/server/blueprints/clinvar/views.py +14 -2
  46. scout/server/blueprints/diagnoses/static/diagnoses.js +8 -1
  47. scout/server/blueprints/institutes/controllers.py +10 -2
  48. scout/server/blueprints/institutes/static/variants_list_scripts.js +9 -1
  49. scout/server/blueprints/institutes/templates/overview/institute_sidebar.html +9 -1
  50. scout/server/blueprints/login/controllers.py +112 -12
  51. scout/server/blueprints/login/views.py +38 -60
  52. scout/server/blueprints/mme/__init__.py +1 -0
  53. scout/server/blueprints/mme/controllers.py +18 -0
  54. scout/server/blueprints/mme/templates/mme/mme_submissions.html +153 -0
  55. scout/server/blueprints/mme/views.py +34 -0
  56. scout/server/blueprints/panels/templates/panels/panel.html +19 -6
  57. scout/server/blueprints/phenotypes/templates/phenotypes/hpo_terms.html +8 -1
  58. scout/server/blueprints/public/templates/public/index.html +5 -1
  59. scout/server/blueprints/variant/controllers.py +19 -10
  60. scout/server/blueprints/variant/templates/variant/acmg.html +15 -2
  61. scout/server/blueprints/variant/templates/variant/cancer-variant.html +1 -1
  62. scout/server/blueprints/variant/templates/variant/components.html +38 -16
  63. scout/server/blueprints/variant/templates/variant/sv-variant.html +2 -2
  64. scout/server/blueprints/variant/templates/variant/utils.html +23 -11
  65. scout/server/blueprints/variant/templates/variant/variant.html +42 -1
  66. scout/server/blueprints/variant/views.py +12 -0
  67. scout/server/blueprints/variants/controllers.py +20 -3
  68. scout/server/blueprints/variants/forms.py +8 -3
  69. scout/server/blueprints/variants/templates/variants/components.html +34 -0
  70. scout/server/blueprints/variants/templates/variants/indicators.html +11 -13
  71. scout/server/blueprints/variants/templates/variants/mei-variants.html +8 -6
  72. scout/server/blueprints/variants/templates/variants/sv-variants.html +9 -7
  73. scout/server/blueprints/variants/templates/variants/utils.html +35 -34
  74. scout/server/blueprints/variants/templates/variants/variants.html +4 -25
  75. scout/server/config.py +8 -0
  76. scout/server/extensions/bionano_extension.py +0 -1
  77. scout/server/extensions/chanjo2_extension.py +54 -13
  78. scout/server/links.py +15 -0
  79. scout/server/static/bs_styles.css +34 -6
  80. scout/server/templates/utils.html +9 -10
  81. scout/server/utils.py +40 -5
  82. scout/utils/acmg.py +25 -26
  83. scout/utils/ensembl_biomart_clients.py +2 -1
  84. scout/utils/ensembl_rest_clients.py +25 -32
  85. scout/utils/hgvs.py +1 -1
  86. scout/utils/scout_requests.py +1 -3
  87. {scout_browser-4.98.0.dist-info → scout_browser-4.100.0.dist-info}/METADATA +10 -14
  88. {scout_browser-4.98.0.dist-info → scout_browser-4.100.0.dist-info}/RECORD +91 -87
  89. {scout_browser-4.98.0.dist-info → scout_browser-4.100.0.dist-info}/WHEEL +0 -0
  90. {scout_browser-4.98.0.dist-info → scout_browser-4.100.0.dist-info}/entry_points.txt +0 -0
  91. {scout_browser-4.98.0.dist-info → scout_browser-4.100.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,5 +1,6 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  import datetime
3
+ import json
3
4
  import logging
4
5
  import operator
5
6
  import re
@@ -831,20 +832,11 @@ class CaseHandler(object):
831
832
  result = self.case_collection.delete_one(query)
832
833
  return result
833
834
 
834
- def check_existing_data(self, case_obj, existing_case, institute_obj, update, keep_actions):
835
- """Make sure data from case to be loaded/reuploaded conforms to case data already saved in database.
836
- Return eventual evaluated variants to be propagated to the updated case if keep_actions is True
837
-
838
- Args:
839
- case_obj(dict): case dictionary to be loaded/reuploaded
840
- existing_case(dict): a case with same _id or same display_name and institute_id as case_obj
841
- institute_obj(dict): institute dictionary
842
- update(bool): If existing case should be updated
843
- keep_actions(bool): If old evaluated variants should be kept when case is updated
844
-
845
- Returns:
846
- previous_evaluated_variants(list): list of variants evaluated in previous case
847
- or None if case is not already present in the database.
835
+ def check_existing_data(
836
+ self, case_obj: dict, existing_case: dict, institute_obj: dict, update, keep_actions: bool
837
+ ) -> Optional[List[dict]]:
838
+ """Make sure data from a case to be loaded/re-uploaded conforms to any case data already saved in the database.
839
+ If keep_actions is True, return any evaluated variants to be propagated to the updated case. The flag indicates that old evaluated variants should be kept when the case is updated.
848
840
  """
849
841
 
850
842
  if existing_case is None:
@@ -886,6 +878,29 @@ class CaseHandler(object):
886
878
  # collect all variants with user actions for this case
887
879
  return list(self.evaluated_variants(case_obj["_id"], institute_obj["_id"]))
888
880
 
881
+ def update_case_phenotypes(self, old_case: dict, new_case: dict):
882
+ """If case has been re-run/re-uploaded, remember phenotype-related settings from the old case, including assigned diseases, HPO terms, phenotype groups and HPO panels."""
883
+ for key in [
884
+ "dynamic_panel_phenotypes",
885
+ "dynamic_gene_list",
886
+ "dynamic_gene_list_edited",
887
+ ]: # Remember key/values from old case
888
+ if key in old_case:
889
+ new_case[key] = old_case[key]
890
+
891
+ for key in [
892
+ "phenotype_terms",
893
+ "phenotype_groups",
894
+ "diagnosis_phenotypes",
895
+ ]: # Remember key/values from old case and integrate with info provided on case config file
896
+ if key not in old_case:
897
+ continue
898
+ new_case[key] = list(
899
+ {
900
+ json.dumps(d, sort_keys=True): d for d in new_case.get(key, []) + old_case[key]
901
+ }.values()
902
+ )
903
+
889
904
  def update_case_data_sharing(self, old_case: dict, new_case: dict):
890
905
  """Update data sharing info for a case that is re-runned/re-uploaded."""
891
906
  for key in ["beacon", "mme_submission"]:
@@ -1008,6 +1023,7 @@ class CaseHandler(object):
1008
1023
  finally:
1009
1024
  if existing_case:
1010
1025
  self.update_case_data_sharing(old_case=existing_case, new_case=case_obj)
1026
+ self.update_case_phenotypes(old_case=existing_case, new_case=case_obj)
1011
1027
  case_obj["rerun_requested"] = False
1012
1028
  if case_obj["status"] in ["active", "archived"]:
1013
1029
  case_obj["status"] = "inactive"
@@ -1017,7 +1033,6 @@ class CaseHandler(object):
1017
1033
  institute_id=institute_obj["_id"],
1018
1034
  force_update_case=True,
1019
1035
  )
1020
-
1021
1036
  self.update_case_cli(case_obj, institute_obj)
1022
1037
  # update Sanger status for the new inserted variants
1023
1038
  self.update_case_sanger_variants(institute_obj, case_obj, old_sanger_variants)
@@ -1,7 +1,7 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  import logging
3
3
  from datetime import datetime
4
- from typing import Optional
4
+ from typing import List, Optional
5
5
 
6
6
  import pymongo
7
7
  from bson import ObjectId
@@ -218,63 +218,55 @@ class ClinVarHandler(object):
218
218
 
219
219
  return sorted_case_data or case_data_list
220
220
 
221
- def clinvar_submissions(self, institute_id):
222
- """Collect all open and closed clinvar submissions for an institute
223
-
224
- Args:
225
- institute_id(str): an institute ID
221
+ def _basic_submission_info(self, result: dict) -> dict:
222
+ """Extracts the basic submission fields."""
223
+ user = self.user(user_id=result.get("created_by"))
224
+ return {
225
+ "_id": result.get("_id"),
226
+ "status": result.get("status"),
227
+ "institute_id": result.get("institute_id"),
228
+ "created_at": result.get("created_at"),
229
+ "created_by": user["name"] if user else None,
230
+ "updated_at": result.get("updated_at"),
231
+ }
226
232
 
227
- Returns:
228
- submissions(list): a list of clinvar submission objects
229
- """
230
- # get first all submission objects
233
+ def clinvar_submissions(self, institute_id: str) -> List[dict]:
234
+ """Collect all open and closed clinvar submissions for an institute"""
231
235
  query = dict(institute_id=institute_id)
232
236
  results = list(
233
237
  self.clinvar_submission_collection.find(query).sort("updated_at", pymongo.DESCENDING)
234
238
  )
235
239
 
236
240
  submissions = []
237
- # Loop over all ClinVar submissions for an institute
238
241
  for result in results:
239
- submission = {}
242
+ submission = self._basic_submission_info(result)
240
243
  cases = {}
241
- user: dict = self.user(user_id=result.get("created_by"))
242
- submission["_id"] = result.get("_id")
243
- submission["status"] = result.get("status")
244
- submission["institute_id"] = result.get("institute_id")
245
- submission["created_at"] = result.get("created_at")
246
- submission["created_by"] = user["name"] if user else None
247
- submission["updated_at"] = result.get("updated_at")
248
-
249
- if "clinvar_subm_id" in result:
244
+
245
+ if result.get("clinvar_subm_id"):
250
246
  submission["clinvar_subm_id"] = result["clinvar_subm_id"]
251
247
 
252
- # If submission has variants registered
253
248
  if result.get("variant_data"):
254
249
  submission["variant_data"] = list(
255
250
  self.clinvar_collection.find({"_id": {"$in": result["variant_data"]}}).sort(
256
251
  "last_evaluated", pymongo.ASCENDING
257
252
  )
258
253
  )
259
-
260
- # Loop over variants contained in a single ClinVar submission
261
254
  for var_info in submission["variant_data"]:
262
- # get case_id from variant id (caseID_variant_ID)
263
255
  case_id = var_info["_id"].rsplit("_", 1)[0]
264
256
  CASE_CLINVAR_SUBMISSION_PROJECTION = {"display_name": 1}
257
+ var_info["added_by"] = self.clinvar_variant_submitter(
258
+ institute_id=institute_id, case_id=case_id, variant_id=var_info["local_id"]
259
+ )
265
260
  case_obj = self.case(
266
261
  case_id=case_id, projection=CASE_CLINVAR_SUBMISSION_PROJECTION
267
262
  )
263
+ if not case_obj:
264
+ cases[case_id] = f"{case_id} (N/A)" # Situation when case has been removed
265
+ continue
268
266
  cases[case_id] = case_obj.get("display_name")
269
267
 
270
- # retrieve user responsible for adding the variant to the submission
271
- var_info["added_by"] = self.clinvar_variant_submitter(
272
- institute_id=institute_id, case_id=case_id, variant_id=var_info["local_id"]
273
- )
274
-
275
268
  submission["cases"] = cases
276
269
 
277
- # If submission has case data registered
278
270
  if result.get("case_data"):
279
271
  unsorted_case_data = list(
280
272
  self.clinvar_collection.find({"_id": {"$in": result["case_data"]}})
@@ -240,16 +240,26 @@ class EventHandler(CaseEventHandler, VariantEventHandler):
240
240
 
241
241
  return self.event_collection.find(query)
242
242
 
243
- def case_events_by_verb(self, category: str, institute: dict, case: dict, verb: str):
243
+ def institute_events_by_verb(
244
+ self, category: str, institute_id: dict, verb: str
245
+ ) -> pymongo.cursor.Cursor:
246
+ """Return events with a specific verb for an institute from the newest."""
247
+ query = {
248
+ "category": category,
249
+ "institute": institute_id,
250
+ "verb": verb,
251
+ }
252
+ return self.event_collection.find(query).sort("updated_at", pymongo.DESCENDING)
253
+
254
+ def case_events_by_verb(
255
+ self, category: str, institute: dict, case: dict, verb: str
256
+ ) -> pymongo.cursor.Cursor:
244
257
  """Return events with a specific verb for a case of an institute
245
258
  Args:
246
259
  category (str): "case" or "variant"
247
260
  institute (dict): an institute id
248
261
  case (dict): a case id
249
262
  verb (str): an event action verb, example: "dismiss_variant"
250
-
251
- Returns:
252
- pymongo.Cursor: Query results
253
263
  """
254
264
  query = {
255
265
  "category": category,
@@ -60,36 +60,51 @@ class InstituteHandler(object):
60
60
  ) -> Union[dict, str]:
61
61
  """Update the information for an institute."""
62
62
 
63
- add_groups = add_groups or False
63
+ def get_phenotype_groups() -> dict:
64
+ """Returns a dictionary with phenotype descriptions and abbreviations."""
65
+ existing_groups = (
66
+ institute_obj.get("phenotype_groups", PHENOTYPE_GROUPS) if add_groups else {}
67
+ )
68
+
69
+ if not phenotype_groups:
70
+ return existing_groups
71
+
72
+ group_abbreviations_list = list(group_abbreviations) if group_abbreviations else []
73
+
74
+ for i, hpo_term in enumerate(phenotype_groups):
75
+ hpo_obj = self.hpo_term(hpo_term)
76
+ if not hpo_obj:
77
+ continue
78
+
79
+ existing_groups[hpo_term] = {
80
+ "name": hpo_obj["description"],
81
+ "abbr": group_abbreviations_list[i] if group_abbreviations_list else None,
82
+ }
83
+
84
+ return existing_groups
85
+
64
86
  institute_obj = self.institute(internal_id)
65
87
  if not institute_obj:
66
88
  raise IntegrityError("Institute {} does not exist in database".format(internal_id))
67
89
 
68
- updates = {"$set": {}}
90
+ updates = {"$set": {}, "$unset": {}}
69
91
  updated_institute = institute_obj
70
92
 
71
93
  if sanger_recipient:
72
- user_obj = self.user(sanger_recipient)
73
- if not user_obj:
74
- raise IntegrityError("user {} does not exist in database".format(sanger_recipient))
75
-
76
- LOG.info(
77
- "Updating sanger recipients for institute: {0} with {1}".format(
78
- internal_id, sanger_recipient
79
- )
80
- )
81
- updates["$push"] = {"sanger_recipients": sanger_recipient}
94
+ old_recipients = institute_obj.get("sanger_recipients", [])
95
+ sanger_recipients = old_recipients + [sanger_recipient]
82
96
 
83
97
  if remove_sanger:
84
- LOG.info(
85
- "Removing sanger recipient {0} from institute: {1}".format(
86
- remove_sanger, internal_id
87
- )
98
+ sanger_recipients = list(
99
+ set(institute_obj.get("sanger_recipients", [])) - set([remove_sanger])
88
100
  )
89
- updates["$pull"] = {"sanger_recipients": remove_sanger}
90
101
 
91
- # Set a number of items
92
- GENERAL_SETTINGS = {
102
+ UPDATE_SETTINGS = {
103
+ "alamut_institution": alamut_institution, # Admin setting
104
+ "alamut_key": alamut_key, # Admin setting
105
+ "check_show_all_vars": check_show_all_vars is not None,
106
+ "clinvar_key": clinvar_key, # Admin setting
107
+ "clinvar_submitters": clinvar_submitters,
93
108
  "cohorts": cohorts,
94
109
  "collaborators": sharing_institutes,
95
110
  "coverage_cutoff": coverage_cutoff,
@@ -98,52 +113,24 @@ class InstituteHandler(object):
98
113
  "gene_panels": gene_panels,
99
114
  "gene_panels_matching": gene_panels_matching,
100
115
  "loqusdb_id": loqusdb_ids,
116
+ "phenotype_groups": get_phenotype_groups(),
101
117
  "sanger_recipients": sanger_recipients,
102
- "clinvar_submitters": clinvar_submitters,
103
- }
104
- for key, value in GENERAL_SETTINGS.items():
105
- if value not in [None, ""]:
106
- updates["$set"][key] = value
107
-
108
- if phenotype_groups is not None:
109
- if group_abbreviations:
110
- group_abbreviations = list(group_abbreviations)
111
- existing_groups = {}
112
- if add_groups:
113
- existing_groups = institute_obj.get("phenotype_groups", PHENOTYPE_GROUPS)
114
- for i, hpo_term in enumerate(phenotype_groups):
115
- hpo_obj = self.hpo_term(hpo_term)
116
- if not hpo_obj:
117
- return "Term {} does not exist in database".format(hpo_term)
118
- hpo_id = hpo_obj["hpo_id"]
119
- description = hpo_obj["description"]
120
- abbreviation = None
121
- if group_abbreviations:
122
- abbreviation = group_abbreviations[i]
123
- existing_groups[hpo_term] = {"name": description, "abbr": abbreviation}
124
- updates["$set"]["phenotype_groups"] = existing_groups
125
-
126
- ADMIN_SETTINGS = {
127
- "alamut_key": alamut_key,
128
- "alamut_institution": alamut_institution,
129
- "clinvar_key": clinvar_key,
130
- "show_all_cases_status": show_all_cases_status,
131
- "soft_filters": soft_filters,
118
+ "show_all_cases_status": show_all_cases_status, # Admin setting
119
+ "soft_filters": soft_filters, # Admin setting
132
120
  }
133
- for key, value in ADMIN_SETTINGS.items():
134
- if value not in [None, "", []]:
121
+ for key, value in UPDATE_SETTINGS.items():
122
+ if bool(value) is True:
135
123
  updates["$set"][key] = value
124
+ else:
125
+ updates["$unset"][key] = "" # Remove the key from the institute document
136
126
 
137
- updates["$set"]["check_show_all_vars"] = check_show_all_vars is not None
138
-
139
- if updates["$set"].keys() or updates.get("$push") or updates.get("$pull"):
127
+ if any(updates.get(op) for op in ["$set", "$unset"]):
140
128
  updates["$set"]["updated_at"] = datetime.now()
141
129
  updated_institute = self.institute_collection.find_one_and_update(
142
130
  {"_id": internal_id},
143
131
  updates,
144
132
  return_document=pymongo.ReturnDocument.AFTER,
145
133
  )
146
-
147
134
  LOG.info("Institute updated")
148
135
 
149
136
  return updated_institute
@@ -1,5 +1,5 @@
1
1
  import logging
2
- from typing import Dict, Optional
2
+ from typing import Dict, Iterable, List, Optional
3
3
 
4
4
  from pymongo import ASCENDING, DESCENDING
5
5
 
@@ -217,3 +217,16 @@ class OmicsVariantHandler:
217
217
  case_id, query=query, variant_ids=variant_ids, category=category, build=build
218
218
  )
219
219
  return self.omics_variant_collection.count_documents(query)
220
+
221
+ def get_omics_variants_hgnc_overlapping(
222
+ self, hgnc_ids: List[int], variant_type: str, variant_obj: dict
223
+ ) -> Iterable[Dict]:
224
+ """Return WTS outliers matching the genes of the DNA variant in question."""
225
+ query = {
226
+ "$and": [
227
+ {"case_id": variant_obj["case_id"]},
228
+ {"variant_type": variant_type},
229
+ {"hgnc_ids": {"$in": hgnc_ids}},
230
+ ]
231
+ }
232
+ return self.omics_variant_collection.find(query)
@@ -13,7 +13,9 @@ from scout.constants import (
13
13
  )
14
14
 
15
15
  CLNSIG_NOT_EXISTS = {"clnsig": {"$exists": False}}
16
+ CLNSIG_ONC_NOT_EXISTS = {"clnsig_onc": {"$exists": False}}
16
17
  CLNSIG_NULL = {"clnsig": {"$eq": None}}
18
+ CLNSIG_ONC_NULL = {"clnsig_onc": {"$eq": None}}
17
19
  CRITERION_EXCLUDE_OPERATOR = {False: "$in", True: "$nin"}
18
20
  EXISTS = {"$exists": True}
19
21
  NOT_EXISTS = {"$exists": False}
@@ -790,7 +792,9 @@ class QueryHandler(object):
790
792
 
791
793
  if criterion == "svtype":
792
794
  svtype = query["svtype"]
793
- mongo_secondary_query.append({"sub_category": {"$in": svtype}})
795
+ mongo_secondary_query.append(
796
+ {"sub_category": {"$in": [re.compile(sub_category) for sub_category in svtype]}}
797
+ )
794
798
 
795
799
  if criterion == "decipher":
796
800
  mongo_query["decipher"] = EXISTS
@@ -848,6 +852,25 @@ class QueryHandler(object):
848
852
  fusion_caller_query.append({caller: "Pass"})
849
853
  mongo_secondary_query.append({"$or": fusion_caller_query})
850
854
 
855
+ if criterion == "clinsig_onc":
856
+
857
+ elem_match = re.compile("|".join(query.get("clinsig_onc")))
858
+
859
+ if query.get("clinsig_onc_exclude"):
860
+ mongo_secondary_query.append(
861
+ {
862
+ "$or": [
863
+ {
864
+ "clnsig_onc.value": {"$not": elem_match}
865
+ }, # Exclude values in `elem_match`
866
+ CLNSIG_ONC_NOT_EXISTS, # Field does not exist
867
+ CLNSIG_ONC_NULL, # Field is null
868
+ ]
869
+ }
870
+ )
871
+ else:
872
+ mongo_secondary_query.append({"clnsig_onc.value": elem_match})
873
+
851
874
  return mongo_secondary_query
852
875
 
853
876
 
@@ -2,7 +2,7 @@
2
2
  # stdlib modules
3
3
  import logging
4
4
  import re
5
- from typing import Any
5
+ from typing import Any, Dict, Iterable, List, Optional, Tuple
6
6
 
7
7
  # Third party modules
8
8
  import pymongo
@@ -689,25 +689,18 @@ class VariantHandler(VariantLoader):
689
689
  result = self.variant_collection.delete_many(query)
690
690
  LOG.info("{0} variants deleted".format(result.deleted_count))
691
691
 
692
- def overlapping(self, variant_obj, limit=30):
693
- """Return overlapping variants.
694
-
695
- Look at the genes that a variant overlaps to.
696
- Then return all variants that overlap these genes.
697
-
698
- If variant_obj is sv it will return the overlapping snvs and oposite
699
- There is a problem when SVs are huge since there are to many overlapping variants.
700
-
701
- Args:
702
- variant_obj(dict)
692
+ def get_variants_hgnc_overlapping(
693
+ self, hgnc_ids: List[int], variant_type: str, limit: Optional[int], variant_obj: dict
694
+ ) -> Iterable[Dict]:
695
+ """Return DNA other categories of DNA variants matching the genes of the DNA variant in question."""
696
+ category = (
697
+ {"$in": ["sv", "mei"]}
698
+ if variant_obj["category"] == "snv"
699
+ else {"$in": ["sv", "snv", "mei", "cancer", "cancer_sv"]}
700
+ )
703
701
 
704
- Returns:
705
- variants(iterable(dict))
706
- """
707
- # This is the category of the variants that we want to collect
708
- category = "snv" if variant_obj["category"] == "sv" else "sv"
709
- variant_type = variant_obj.get("variant_type", "clinical")
710
- hgnc_ids = variant_obj["hgnc_ids"]
702
+ if not limit:
703
+ limit = 30 if variant_obj["category"] == "snv" else 45
711
704
 
712
705
  query = {
713
706
  "$and": [
@@ -718,10 +711,39 @@ class VariantHandler(VariantLoader):
718
711
  ]
719
712
  }
720
713
  sort_key = [("rank_score", pymongo.DESCENDING)]
721
- # We collect the 30 most severe overlapping variants
722
- variants = self.variant_collection.find(query).sort(sort_key).limit(limit)
723
714
 
724
- return variants
715
+ return [
716
+ variant
717
+ for variant in self.variant_collection.find(query).sort(sort_key).limit(limit)
718
+ if variant["variant_id"] != variant_obj["variant_id"]
719
+ ]
720
+
721
+ def hgnc_overlapping(
722
+ self, variant_obj: dict, limit: int = None
723
+ ) -> Tuple[Iterable[Dict], Iterable[Dict]]:
724
+ """Return overlapping variants.
725
+
726
+ Look at the genes that a variant overlaps, then return all variants that overlap these genes.
727
+
728
+ The operation is slightly different depending on the category of the variants that we want to collect.
729
+ If variant_obj is an SV it will return the hgnc_id matching SVs, SNVs, and MEIs but
730
+ for SNVs we will only return the SVs and MEIs since the genmod compounds are way better.
731
+
732
+ Do not return the present variant as matching.
733
+
734
+ limit: A maximum count of returned variants is introduced: mainly this is a problem when SVs are huge since there can be many genes and overlapping variants.
735
+ We sort to offer the LIMIT most severe overlapping variants.
736
+ """
737
+ hgnc_ids = variant_obj.get("hgnc_ids", [])
738
+ variant_type = variant_obj.get("variant_type", "clinical")
739
+ return (
740
+ self.get_variants_hgnc_overlapping(
741
+ hgnc_ids=hgnc_ids, variant_type=variant_type, limit=limit, variant_obj=variant_obj
742
+ ),
743
+ self.get_omics_variants_hgnc_overlapping(
744
+ hgnc_ids=hgnc_ids, variant_type=variant_type, variant_obj=variant_obj
745
+ ),
746
+ )
725
747
 
726
748
  def evaluated_variant_ids_from_events(self, case_id, institute_id):
727
749
  """Returns variant ids for variants that have been evaluated