cool-seq-tool 0.14.3__tar.gz → 0.14.5__tar.gz

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 (65) hide show
  1. cool_seq_tool-0.14.5/.github/CODEOWNERS +1 -0
  2. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/PKG-INFO +1 -1
  3. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/src/cool_seq_tool/mappers/exon_genomic_coords.py +35 -2
  4. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/src/cool_seq_tool/resources/status.py +10 -3
  5. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/src/cool_seq_tool/sources/uta_database.py +27 -3
  6. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/src/cool_seq_tool.egg-info/PKG-INFO +1 -1
  7. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/src/cool_seq_tool.egg-info/SOURCES.txt +1 -0
  8. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/tests/mappers/test_exon_genomic_coords.py +26 -0
  9. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/tests/sources/test_uta_database.py +98 -0
  10. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/.coveragerc +0 -0
  11. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/.github/ISSUE_TEMPLATE/bug-report.yaml +0 -0
  12. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/.github/ISSUE_TEMPLATE/feature-request.yaml +0 -0
  13. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/.github/workflows/checks.yaml +0 -0
  14. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/.github/workflows/pr-priority-label.yaml +0 -0
  15. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/.github/workflows/release.yml +0 -0
  16. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/.github/workflows/stale.yaml +0 -0
  17. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/.gitignore +0 -0
  18. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/.pre-commit-config.yaml +0 -0
  19. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/.readthedocs.yaml +0 -0
  20. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/CITATION.cff +0 -0
  21. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/LICENSE +0 -0
  22. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/README.md +0 -0
  23. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/docs/Makefile +0 -0
  24. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/docs/make.bat +0 -0
  25. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/docs/source/_static/img/biomart.png +0 -0
  26. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/docs/source/_templates/module_summary.rst +0 -0
  27. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/docs/source/changelog.rst +0 -0
  28. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/docs/source/conf.py +0 -0
  29. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/docs/source/contributing.rst +0 -0
  30. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/docs/source/index.rst +0 -0
  31. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/docs/source/install.rst +0 -0
  32. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/docs/source/license.rst +0 -0
  33. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/docs/source/reference/index.rst +0 -0
  34. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/docs/source/transcript_selection.rst +0 -0
  35. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/docs/source/usage.rst +0 -0
  36. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/pyproject.toml +0 -0
  37. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/setup.cfg +0 -0
  38. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/src/cool_seq_tool/__init__.py +0 -0
  39. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/src/cool_seq_tool/app.py +0 -0
  40. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/src/cool_seq_tool/handlers/__init__.py +0 -0
  41. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/src/cool_seq_tool/handlers/seqrepo_access.py +0 -0
  42. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/src/cool_seq_tool/mappers/__init__.py +0 -0
  43. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/src/cool_seq_tool/mappers/alignment.py +0 -0
  44. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/src/cool_seq_tool/mappers/feature_overlap.py +0 -0
  45. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/src/cool_seq_tool/mappers/liftover.py +0 -0
  46. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/src/cool_seq_tool/mappers/mane_transcript.py +0 -0
  47. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/src/cool_seq_tool/resources/__init__.py +0 -0
  48. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/src/cool_seq_tool/resources/data_files.py +0 -0
  49. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/src/cool_seq_tool/resources/transcript_mapping.tsv +0 -0
  50. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/src/cool_seq_tool/schemas.py +0 -0
  51. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/src/cool_seq_tool/sources/__init__.py +0 -0
  52. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/src/cool_seq_tool/sources/mane_transcript_mappings.py +0 -0
  53. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/src/cool_seq_tool/sources/transcript_mappings.py +0 -0
  54. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/src/cool_seq_tool/utils.py +0 -0
  55. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/src/cool_seq_tool.egg-info/dependency_links.txt +0 -0
  56. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/src/cool_seq_tool.egg-info/requires.txt +0 -0
  57. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/src/cool_seq_tool.egg-info/top_level.txt +0 -0
  58. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/tests/conftest.py +0 -0
  59. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/tests/handlers/test_feature_overlap.py +0 -0
  60. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/tests/handlers/test_seqrepo_access.py +0 -0
  61. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/tests/mappers/test_alignment.py +0 -0
  62. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/tests/mappers/test_liftover.py +0 -0
  63. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/tests/mappers/test_mane_transcript.py +0 -0
  64. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/tests/sources/test_mane_transcript_mappings.py +0 -0
  65. {cool_seq_tool-0.14.3 → cool_seq_tool-0.14.5}/tests/test_utils.py +0 -0
@@ -0,0 +1 @@
1
+ * @GenomicMedLab/cool-seq-tool-maintainers
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cool_seq_tool
3
- Version: 0.14.3
3
+ Version: 0.14.5
4
4
  Summary: Common Operation on Lots of Sequences Tool
5
5
  Author: Kori Kuzma, James Stevenson, Katie Stahl, Alex Wagner
6
6
  License: MIT License
@@ -2,6 +2,7 @@
2
2
 
3
3
  import logging
4
4
 
5
+ from ga4gh.core.models import Extension
5
6
  from ga4gh.vrs.models import SequenceLocation, SequenceReference
6
7
  from pydantic import ConfigDict, Field, StrictInt, StrictStr, model_validator
7
8
 
@@ -66,6 +67,24 @@ class TxSegment(BaseModelForbidExtra):
66
67
  ..., description="The genomic position of a transcript segment."
67
68
  )
68
69
 
70
+ @model_validator(mode="before")
71
+ def check_seg_pos(cls, values: dict) -> dict: # noqa: N805
72
+ """Ensure that only one of `start` or `end` is set in the
73
+ genomic_location field
74
+
75
+ :param values: The values in the TxSegment class
76
+ :raises ValueError: If `start` and `end` are both set in
77
+ `genomic_location`
78
+ :return: Values in model
79
+ """
80
+ loc = values.get("genomic_location")
81
+ start = getattr(loc, "start", None)
82
+ end = getattr(loc, "end", None)
83
+ if start and end:
84
+ err_msg = "Only one of `start` or `end` may be set as this describes the start or end of a transcript segment"
85
+ raise ValueError(err_msg)
86
+ return values
87
+
69
88
  model_config = ConfigDict(
70
89
  json_schema_extra={
71
90
  "example": {
@@ -78,6 +97,7 @@ class TxSegment(BaseModelForbidExtra):
78
97
  "refgetAccession": "SQ.Ya6Rs7DHhDeg7YaOSg1EoNi3U_nQ9SvO",
79
98
  },
80
99
  "end": 154192135,
100
+ "extensions": [{"name": "is_exonic", "value": True}],
81
101
  },
82
102
  }
83
103
  }
@@ -135,6 +155,7 @@ class GenomicTxSeg(BaseModelForbidExtra):
135
155
  "refgetAccession": "SQ.Ya6Rs7DHhDeg7YaOSg1EoNi3U_nQ9SvO",
136
156
  },
137
157
  "end": 154192135,
158
+ "extensions": [{"name": "is_exonic", "value": True}],
138
159
  },
139
160
  },
140
161
  "errors": [],
@@ -201,6 +222,7 @@ class GenomicTxSegService(BaseModelForbidExtra):
201
222
  "refgetAccession": "SQ.Ya6Rs7DHhDeg7YaOSg1EoNi3U_nQ9SvO",
202
223
  },
203
224
  "end": 154192135,
225
+ "extensions": [{"name": "is_exonic", "value": True}],
204
226
  },
205
227
  },
206
228
  "seg_end": {
@@ -213,6 +235,7 @@ class GenomicTxSegService(BaseModelForbidExtra):
213
235
  "refgetAccession": "SQ.Ya6Rs7DHhDeg7YaOSg1EoNi3U_nQ9SvO",
214
236
  },
215
237
  "start": 154170399,
238
+ "extensions": [{"name": "is_exonic", "value": True}],
216
239
  },
217
240
  },
218
241
  }
@@ -705,7 +728,12 @@ class ExonGenomicCoordsMapper:
705
728
  ), None
706
729
 
707
730
  def _get_vrs_seq_loc(
708
- self, genomic_ac: str, genomic_pos: int, is_seg_start: bool, strand: Strand
731
+ self,
732
+ genomic_ac: str,
733
+ genomic_pos: int,
734
+ is_seg_start: bool,
735
+ strand: Strand,
736
+ is_exonic: bool = True,
709
737
  ) -> tuple[SequenceLocation | None, str | None]:
710
738
  """Create VRS Sequence Location for genomic position where transcript segment
711
739
  occurs
@@ -715,6 +743,8 @@ class ExonGenomicCoordsMapper:
715
743
  :param is_seg_start: ``True`` if ``genomic_pos`` is where the transcript segment
716
744
  starts. ``False`` if ``genomic_pos`` is where the transcript segment ends.
717
745
  :param strand: Strand
746
+ :param is_exonic: A boolean indicating if the genomic breakpoint occurs
747
+ on an exon. By default, this is set to ``True``.
718
748
  :return: Tuple containing VRS location (if successful) and error message (if
719
749
  unable to get GA4GH identifier for ``genomic_ac``).
720
750
  """
@@ -734,6 +764,7 @@ class ExonGenomicCoordsMapper:
734
764
  ),
735
765
  start=genomic_pos if use_start else None,
736
766
  end=genomic_pos if not use_start else None,
767
+ extensions=[Extension(name="is_exonic", value=is_exonic)],
737
768
  ), None
738
769
 
739
770
  async def _genomic_to_tx_segment(
@@ -895,6 +926,7 @@ class ExonGenomicCoordsMapper:
895
926
  # Check if breakpoint occurs on an exon.
896
927
  # If not, determine the adjacent exon given the selected transcript
897
928
  if not self._is_exonic_breakpoint(genomic_pos, tx_exons):
929
+ is_exonic = False
898
930
  exon_num = self._get_adjacent_exon(
899
931
  tx_exons_genomic_coords=tx_exons,
900
932
  strand=strand,
@@ -902,6 +934,7 @@ class ExonGenomicCoordsMapper:
902
934
  end=genomic_pos if not is_seg_start else None,
903
935
  )
904
936
  else:
937
+ is_exonic = True
905
938
  exon_data = await self.uta_db.get_tx_exon_aln_v_data(
906
939
  transcript,
907
940
  genomic_pos,
@@ -920,7 +953,7 @@ class ExonGenomicCoordsMapper:
920
953
  )
921
954
 
922
955
  genomic_location, err_msg = self._get_vrs_seq_loc(
923
- genomic_ac, genomic_pos, is_seg_start, strand
956
+ genomic_ac, genomic_pos, is_seg_start, strand, is_exonic
924
957
  )
925
958
  if err_msg:
926
959
  return GenomicTxSeg(errors=[err_msg])
@@ -3,6 +3,7 @@
3
3
  import logging
4
4
  from collections import namedtuple
5
5
  from pathlib import Path
6
+ from urllib.parse import urlparse
6
7
 
7
8
  from agct._core import ChainfileError
8
9
  from asyncpg import InvalidCatalogNameError, UndefinedTableError
@@ -11,7 +12,7 @@ from biocommons.seqrepo import SeqRepo
11
12
  from cool_seq_tool.handlers.seqrepo_access import SEQREPO_ROOT_DIR, SeqRepoAccess
12
13
  from cool_seq_tool.mappers.liftover import LiftOver
13
14
  from cool_seq_tool.resources.data_files import DataFile, get_data_file
14
- from cool_seq_tool.sources.uta_database import UTA_DB_URL, UtaDatabase
15
+ from cool_seq_tool.sources.uta_database import UTA_DB_URL, ParseResult, UtaDatabase
15
16
 
16
17
  _logger = logging.getLogger(__name__)
17
18
 
@@ -119,14 +120,20 @@ async def check_status(
119
120
  else:
120
121
  status["liftover"] = True
121
122
 
123
+ parsed_result = ParseResult(urlparse(db_url))
124
+ sanitized_url = parsed_result.sanitized_url
122
125
  try:
123
126
  await UtaDatabase.create(db_url)
127
+ except ValueError:
128
+ _logger.exception("Database URL is not valid")
124
129
  except (OSError, InvalidCatalogNameError, UndefinedTableError):
125
- _logger.exception("Encountered error instantiating UTA at URI %s", UTA_DB_URL)
130
+ _logger.exception(
131
+ "Encountered error instantiating UTA at URI %s", sanitized_url
132
+ )
126
133
  except Exception as e:
127
134
  _logger.critical(
128
135
  "Encountered unexpected error instantiating UTA from URI %s: %s",
129
- UTA_DB_URL,
136
+ sanitized_url,
130
137
  e,
131
138
  )
132
139
  else:
@@ -5,7 +5,7 @@ import logging
5
5
  from os import environ
6
6
  from typing import Any, Literal, TypeVar
7
7
  from urllib.parse import ParseResult as UrlLibParseResult
8
- from urllib.parse import quote, unquote, urlparse
8
+ from urllib.parse import unquote, urlparse, urlunparse
9
9
 
10
10
  import asyncpg
11
11
  import boto3
@@ -101,8 +101,7 @@ class UtaDatabase:
101
101
  """
102
102
  self.schema = None
103
103
  self._connection_pool = None
104
- original_pwd = db_url.split("//")[-1].split("@")[0].split(":")[-1]
105
- self.db_url = db_url.replace(original_pwd, quote(original_pwd))
104
+ self.db_url = db_url
106
105
  self.args = self._get_conn_args()
107
106
 
108
107
  def _get_conn_args(self) -> DbConnectionArgs:
@@ -954,3 +953,28 @@ class ParseResult(UrlLibParseResult):
954
953
  """Create schema property."""
955
954
  path_elems = self.path.split("/")
956
955
  return path_elems[2] if len(path_elems) > 2 else None
956
+
957
+ @property
958
+ def sanitized_url(self) -> str:
959
+ """Sanitized DB URL with the password masked"""
960
+ netloc = ""
961
+ if self.username:
962
+ netloc += self.username
963
+ if self.password is not None and self.password != "":
964
+ netloc += ":***"
965
+ netloc += "@"
966
+ if self.hostname:
967
+ netloc += f"{self.hostname}"
968
+ if self.port:
969
+ netloc += f":{self.port}"
970
+
971
+ return urlunparse(
972
+ (
973
+ self.scheme,
974
+ netloc,
975
+ self.path,
976
+ self.params,
977
+ self.query,
978
+ self.fragment,
979
+ )
980
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cool_seq_tool
3
- Version: 0.14.3
3
+ Version: 0.14.5
4
4
  Summary: Common Operation on Lots of Sequences Tool
5
5
  Author: Kori Kuzma, James Stevenson, Katie Stahl, Alex Wagner
6
6
  License: MIT License
@@ -6,6 +6,7 @@ CITATION.cff
6
6
  LICENSE
7
7
  README.md
8
8
  pyproject.toml
9
+ .github/CODEOWNERS
9
10
  .github/ISSUE_TEMPLATE/bug-report.yaml
10
11
  .github/ISSUE_TEMPLATE/feature-request.yaml
11
12
  .github/workflows/checks.yaml
@@ -183,6 +183,7 @@ def tpm3_exon1():
183
183
  "refgetAccession": "SQ.Ya6Rs7DHhDeg7YaOSg1EoNi3U_nQ9SvO",
184
184
  },
185
185
  "end": 154192135,
186
+ "extensions": [{"name": "is_exonic", "value": True}],
186
187
  },
187
188
  },
188
189
  }
@@ -207,6 +208,7 @@ def tpm3_exon8():
207
208
  "refgetAccession": "SQ.Ya6Rs7DHhDeg7YaOSg1EoNi3U_nQ9SvO",
208
209
  },
209
210
  "start": 154170399,
211
+ "extensions": [{"name": "is_exonic", "value": True}],
210
212
  },
211
213
  },
212
214
  }
@@ -292,6 +294,7 @@ def mane_braf():
292
294
  "refgetAccession": "SQ.F-LrLMe1SRpfUZHkQmvkVKFEGaoDeHul",
293
295
  },
294
296
  "end": 140801559,
297
+ "extensions": [{"name": "is_exonic", "value": True}],
295
298
  },
296
299
  },
297
300
  "seg_end": {
@@ -304,6 +307,7 @@ def mane_braf():
304
307
  "refgetAccession": "SQ.F-LrLMe1SRpfUZHkQmvkVKFEGaoDeHul",
305
308
  },
306
309
  "start": 140753336,
310
+ "extensions": [{"name": "is_exonic", "value": True}],
307
311
  },
308
312
  },
309
313
  }
@@ -328,6 +332,7 @@ def wee1_exon2_exon11():
328
332
  "refgetAccession": "SQ.2NkFm8HK88MqeNkCgj78KidCAXgnsfV1",
329
333
  },
330
334
  "start": 9576092,
335
+ "extensions": [{"name": "is_exonic", "value": True}],
331
336
  },
332
337
  },
333
338
  "seg_end": {
@@ -340,6 +345,7 @@ def wee1_exon2_exon11():
340
345
  "refgetAccession": "SQ.2NkFm8HK88MqeNkCgj78KidCAXgnsfV1",
341
346
  },
342
347
  "end": 9588449,
348
+ "extensions": [{"name": "is_exonic", "value": True}],
343
349
  },
344
350
  },
345
351
  }
@@ -364,6 +370,7 @@ def mane_wee1_exon2_exon11():
364
370
  "refgetAccession": "SQ.2NkFm8HK88MqeNkCgj78KidCAXgnsfV1",
365
371
  },
366
372
  "start": 9576092,
373
+ "extensions": [{"name": "is_exonic", "value": True}],
367
374
  },
368
375
  },
369
376
  "seg_end": {
@@ -376,6 +383,7 @@ def mane_wee1_exon2_exon11():
376
383
  "refgetAccession": "SQ.2NkFm8HK88MqeNkCgj78KidCAXgnsfV1",
377
384
  },
378
385
  "end": 9588449,
386
+ "extensions": [{"name": "is_exonic", "value": True}],
379
387
  },
380
388
  },
381
389
  }
@@ -400,6 +408,7 @@ def ntrk1_exon10_exon17():
400
408
  "refgetAccession": "SQ.Ya6Rs7DHhDeg7YaOSg1EoNi3U_nQ9SvO",
401
409
  },
402
410
  "start": 156874570,
411
+ "extensions": [{"name": "is_exonic", "value": True}],
403
412
  },
404
413
  },
405
414
  "seg_end": {
@@ -412,6 +421,7 @@ def ntrk1_exon10_exon17():
412
421
  "refgetAccession": "SQ.Ya6Rs7DHhDeg7YaOSg1EoNi3U_nQ9SvO",
413
422
  },
414
423
  "end": 156881850,
424
+ "extensions": [{"name": "is_exonic", "value": True}],
415
425
  },
416
426
  },
417
427
  }
@@ -437,6 +447,7 @@ def zbtb10_exon3_end():
437
447
  "refgetAccession": "SQ.209Z7zJ-mFypBEWLk4rNC6S_OxY5p7bs",
438
448
  },
439
449
  "end": 80514010,
450
+ "extensions": [{"name": "is_exonic", "value": False}],
440
451
  },
441
452
  },
442
453
  }
@@ -461,6 +472,7 @@ def zbtb10_exon5_start():
461
472
  "refgetAccession": "SQ.209Z7zJ-mFypBEWLk4rNC6S_OxY5p7bs",
462
473
  },
463
474
  "start": 80518580,
475
+ "extensions": [{"name": "is_exonic", "value": False}],
464
476
  },
465
477
  },
466
478
  "seg_end": None,
@@ -487,6 +499,7 @@ def tpm3_exon6_end():
487
499
  "refgetAccession": "SQ.Ya6Rs7DHhDeg7YaOSg1EoNi3U_nQ9SvO",
488
500
  },
489
501
  "start": 154171410,
502
+ "extensions": [{"name": "is_exonic", "value": False}],
490
503
  },
491
504
  },
492
505
  }
@@ -511,6 +524,7 @@ def tpm3_exon5_start():
511
524
  "refgetAccession": "SQ.Ya6Rs7DHhDeg7YaOSg1EoNi3U_nQ9SvO",
512
525
  },
513
526
  "end": 154173080,
527
+ "extensions": [{"name": "is_exonic", "value": False}],
514
528
  },
515
529
  },
516
530
  "seg_end": None,
@@ -537,6 +551,7 @@ def gusbp3_exon2_end():
537
551
  "refgetAccession": "SQ.aUiQCzCPZ2d0csHbMSbh2NzInhonSXwI",
538
552
  },
539
553
  "start": 69680764,
554
+ "extensions": [{"name": "is_exonic", "value": False}],
540
555
  },
541
556
  },
542
557
  }
@@ -561,6 +576,7 @@ def eln_grch38_intronic():
561
576
  "refgetAccession": "SQ.F-LrLMe1SRpfUZHkQmvkVKFEGaoDeHul",
562
577
  },
563
578
  "start": 74028173,
579
+ "extensions": [{"name": "is_exonic", "value": True}],
564
580
  },
565
581
  },
566
582
  "seg_end": {
@@ -573,6 +589,7 @@ def eln_grch38_intronic():
573
589
  "refgetAccession": "SQ.F-LrLMe1SRpfUZHkQmvkVKFEGaoDeHul",
574
590
  },
575
591
  "end": 74043599,
592
+ "extensions": [{"name": "is_exonic", "value": False}],
576
593
  },
577
594
  },
578
595
  }
@@ -597,6 +614,7 @@ def gusbp3_exon5_start():
597
614
  "refgetAccession": "SQ.aUiQCzCPZ2d0csHbMSbh2NzInhonSXwI",
598
615
  },
599
616
  "end": 69645878,
617
+ "extensions": [{"name": "is_exonic", "value": False}],
600
618
  },
601
619
  },
602
620
  "seg_end": None,
@@ -648,6 +666,10 @@ def genomic_tx_seg_service_checks(actual, expected=None, is_valid=True):
648
666
  assert (
649
667
  actual_seg.genomic_location.end == expected_seg.genomic_location.end
650
668
  )
669
+ assert (
670
+ actual_seg.genomic_location.extensions
671
+ == expected_seg.genomic_location.extensions
672
+ )
651
673
 
652
674
  assert actual.errors == expected.errors
653
675
  else:
@@ -715,6 +737,10 @@ def genomic_tx_seg_checks(actual, expected=None, is_valid=True):
715
737
  actual_seg.genomic_location.start == expected_seg.genomic_location.start
716
738
  )
717
739
  assert actual_seg.genomic_location.end == expected_seg.genomic_location.end
740
+ assert (
741
+ actual_seg.genomic_location.extensions
742
+ == expected_seg.genomic_location.extensions
743
+ )
718
744
 
719
745
  assert actual.errors == expected.errors
720
746
  else:
@@ -1,11 +1,14 @@
1
1
  """Test UTA data source."""
2
2
 
3
+ from urllib.parse import urlparse
4
+
3
5
  import pytest
4
6
 
5
7
  from cool_seq_tool.schemas import Strand
6
8
  from cool_seq_tool.sources.uta_database import (
7
9
  GenomicTxData,
8
10
  GenomicTxMetadata,
11
+ ParseResult,
9
12
  TxExonAlnData,
10
13
  )
11
14
 
@@ -360,3 +363,98 @@ async def test_get_mane_transcripts_from_genomic_pos(test_db):
360
363
  # invalid ac
361
364
  resp = await test_db.get_transcripts_from_genomic_pos("NC_000007.14232", 140753336)
362
365
  assert resp == []
366
+
367
+
368
+ @pytest.mark.parametrize(
369
+ ("raw_url", "expected"),
370
+ [
371
+ # Username + password
372
+ (
373
+ "postgresql://user:pass@localhost:5432/dbname",
374
+ {
375
+ "scheme": "postgresql",
376
+ "username": "user",
377
+ "password": "pass",
378
+ "hostname": "localhost",
379
+ "port": 5432,
380
+ "database": "dbname",
381
+ "sanitized_url": "postgresql://user:***@localhost:5432/dbname",
382
+ },
383
+ ),
384
+ # Username with null password
385
+ (
386
+ "postgresql://user@localhost/dbname",
387
+ {
388
+ "scheme": "postgresql",
389
+ "username": "user",
390
+ "password": None,
391
+ "hostname": "localhost",
392
+ "port": None,
393
+ "database": "dbname",
394
+ "sanitized_url": "postgresql://user@localhost/dbname",
395
+ },
396
+ ),
397
+ # Password is "0"
398
+ (
399
+ "postgresql://user:0@localhost/dbname",
400
+ {
401
+ "scheme": "postgresql",
402
+ "username": "user",
403
+ "password": "0",
404
+ "hostname": "localhost",
405
+ "port": None,
406
+ "database": "dbname",
407
+ "sanitized_url": "postgresql://user:***@localhost/dbname",
408
+ },
409
+ ),
410
+ # Empty password
411
+ (
412
+ "postgresql://user:@localhost/dbname",
413
+ {
414
+ "scheme": "postgresql",
415
+ "username": "user",
416
+ "password": "",
417
+ "hostname": "localhost",
418
+ "port": None,
419
+ "database": "dbname",
420
+ "sanitized_url": "postgresql://user@localhost/dbname",
421
+ },
422
+ ),
423
+ # No username
424
+ (
425
+ "postgresql://localhost:5432/dbname",
426
+ {
427
+ "scheme": "postgresql",
428
+ "username": None,
429
+ "password": None,
430
+ "hostname": "localhost",
431
+ "port": 5432,
432
+ "database": "dbname",
433
+ "sanitized_url": "postgresql://localhost:5432/dbname",
434
+ },
435
+ ),
436
+ # With query params
437
+ (
438
+ "postgresql://user:secret@localhost/dbname?query#fragment",
439
+ {
440
+ "scheme": "postgresql",
441
+ "username": "user",
442
+ "password": "secret",
443
+ "hostname": "localhost",
444
+ "port": None,
445
+ "database": "dbname",
446
+ "sanitized_url": "postgresql://user:***@localhost/dbname?query#fragment",
447
+ },
448
+ ),
449
+ ],
450
+ )
451
+ async def test_parsed_url(raw_url, expected):
452
+ parsed_result = ParseResult(urlparse(raw_url))
453
+
454
+ assert parsed_result.scheme == expected["scheme"]
455
+ assert parsed_result.username == expected["username"]
456
+ assert parsed_result.password == expected["password"]
457
+ assert parsed_result.hostname == expected["hostname"]
458
+ assert parsed_result.port == expected["port"]
459
+ assert parsed_result.database == expected["database"]
460
+ assert parsed_result.sanitized_url == expected["sanitized_url"]
File without changes
File without changes
File without changes