napistu 0.1.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 (77) hide show
  1. napistu/__init__.py +12 -0
  2. napistu/__main__.py +867 -0
  3. napistu/consensus.py +1557 -0
  4. napistu/constants.py +500 -0
  5. napistu/gcs/__init__.py +10 -0
  6. napistu/gcs/constants.py +69 -0
  7. napistu/gcs/downloads.py +180 -0
  8. napistu/identifiers.py +805 -0
  9. napistu/indices.py +227 -0
  10. napistu/ingestion/__init__.py +10 -0
  11. napistu/ingestion/bigg.py +146 -0
  12. napistu/ingestion/constants.py +296 -0
  13. napistu/ingestion/cpr_edgelist.py +106 -0
  14. napistu/ingestion/identifiers_etl.py +148 -0
  15. napistu/ingestion/obo.py +268 -0
  16. napistu/ingestion/psi_mi.py +276 -0
  17. napistu/ingestion/reactome.py +218 -0
  18. napistu/ingestion/sbml.py +621 -0
  19. napistu/ingestion/string.py +356 -0
  20. napistu/ingestion/trrust.py +285 -0
  21. napistu/ingestion/yeast.py +147 -0
  22. napistu/mechanism_matching.py +597 -0
  23. napistu/modify/__init__.py +10 -0
  24. napistu/modify/constants.py +86 -0
  25. napistu/modify/curation.py +628 -0
  26. napistu/modify/gaps.py +635 -0
  27. napistu/modify/pathwayannot.py +1381 -0
  28. napistu/modify/uncompartmentalize.py +264 -0
  29. napistu/network/__init__.py +10 -0
  30. napistu/network/constants.py +117 -0
  31. napistu/network/neighborhoods.py +1594 -0
  32. napistu/network/net_create.py +1647 -0
  33. napistu/network/net_utils.py +652 -0
  34. napistu/network/paths.py +500 -0
  35. napistu/network/precompute.py +221 -0
  36. napistu/rpy2/__init__.py +127 -0
  37. napistu/rpy2/callr.py +168 -0
  38. napistu/rpy2/constants.py +101 -0
  39. napistu/rpy2/netcontextr.py +464 -0
  40. napistu/rpy2/rids.py +697 -0
  41. napistu/sbml_dfs_core.py +2216 -0
  42. napistu/sbml_dfs_utils.py +304 -0
  43. napistu/source.py +394 -0
  44. napistu/utils.py +943 -0
  45. napistu-0.1.0.dist-info/METADATA +56 -0
  46. napistu-0.1.0.dist-info/RECORD +77 -0
  47. napistu-0.1.0.dist-info/WHEEL +5 -0
  48. napistu-0.1.0.dist-info/entry_points.txt +2 -0
  49. napistu-0.1.0.dist-info/licenses/LICENSE +21 -0
  50. napistu-0.1.0.dist-info/top_level.txt +2 -0
  51. tests/__init__.py +0 -0
  52. tests/conftest.py +83 -0
  53. tests/test_consensus.py +255 -0
  54. tests/test_constants.py +20 -0
  55. tests/test_curation.py +134 -0
  56. tests/test_data/__init__.py +0 -0
  57. tests/test_edgelist.py +20 -0
  58. tests/test_gcs.py +23 -0
  59. tests/test_identifiers.py +151 -0
  60. tests/test_igraph.py +353 -0
  61. tests/test_indices.py +88 -0
  62. tests/test_mechanism_matching.py +126 -0
  63. tests/test_net_utils.py +66 -0
  64. tests/test_netcontextr.py +105 -0
  65. tests/test_obo.py +34 -0
  66. tests/test_pathwayannot.py +95 -0
  67. tests/test_precomputed_distances.py +222 -0
  68. tests/test_rpy2.py +61 -0
  69. tests/test_sbml.py +46 -0
  70. tests/test_sbml_dfs_create.py +307 -0
  71. tests/test_sbml_dfs_utils.py +22 -0
  72. tests/test_sbo.py +11 -0
  73. tests/test_set_coverage.py +50 -0
  74. tests/test_source.py +67 -0
  75. tests/test_uncompartmentalize.py +40 -0
  76. tests/test_utils.py +487 -0
  77. tests/utils.py +30 -0
@@ -0,0 +1,56 @@
1
+ Metadata-Version: 2.4
2
+ Name: napistu
3
+ Version: 0.1.0
4
+ Summary: Connecting high-dimensional data to curated pathways
5
+ Home-page: https://github.com/napistu/napistu-py
6
+ Author: Sean Hackett
7
+ Author-email: seanmchackett@gmail.com
8
+ Project-URL: Bug Tracker, https://github.com/napistu/napistu-py/issues
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3 :: Only
13
+ Requires-Python: >=3.11
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Requires-Dist: Jinja2
17
+ Requires-Dist: PyYAML==6.*
18
+ Requires-Dist: click==8.*
19
+ Requires-Dist: click-logging
20
+ Requires-Dist: fs==2.4.*
21
+ Requires-Dist: fs-gcsfs==1.5.*
22
+ Requires-Dist: igraph
23
+ Requires-Dist: matplotlib==3.*
24
+ Requires-Dist: numpy==1.26.*
25
+ Requires-Dist: pandas==1.5.*
26
+ Requires-Dist: pydantic==2.*
27
+ Requires-Dist: python-libsbml
28
+ Requires-Dist: requests>=2
29
+ Requires-Dist: scipy==1.14.*
30
+ Requires-Dist: tqdm
31
+ Requires-Dist: zeep==3.*
32
+ Provides-Extra: dev
33
+ Requires-Dist: black==25.*; extra == "dev"
34
+ Requires-Dist: ipykernel; extra == "dev"
35
+ Requires-Dist: pre-commit==3.3.*; extra == "dev"
36
+ Requires-Dist: pytest==7.*; extra == "dev"
37
+ Requires-Dist: pytest-cov; extra == "dev"
38
+ Requires-Dist: ruff; extra == "dev"
39
+ Requires-Dist: testcontainers; extra == "dev"
40
+ Provides-Extra: rpy2
41
+ Requires-Dist: pyarrow==18.0.0; extra == "rpy2"
42
+ Requires-Dist: rpy2==3.5.*; extra == "rpy2"
43
+ Requires-Dist: rpy2-arrow==0.1.1; extra == "rpy2"
44
+ Dynamic: license-file
45
+
46
+ # Napistu Python Library
47
+
48
+ This Python package hosts the majority of the algorithmic code for the [Napistu project](https://github.com/napistu/napistu).
49
+
50
+ ## Setup
51
+
52
+ Currently the only way to use this repository is to clone the repo and perform a local install. e.g., from this directory:
53
+
54
+ ```bash
55
+ pip install .
56
+ ```
@@ -0,0 +1,77 @@
1
+ napistu/__init__.py,sha256=rz6NdV9Fm6a6bBR17VQPHeJQD4DUZWC7zR9a7nNMOhw,269
2
+ napistu/__main__.py,sha256=i1OyReHD58GjyGYShXmMuBfA0VoGBF9dirg2nA4JCa8,28334
3
+ napistu/consensus.py,sha256=p8GjWFzq1cvBB-H-LVSatWL_9fxbdYq2wsF4-JZnc_M,54641
4
+ napistu/constants.py,sha256=hQ1OLH07xFTxMukJLCptzqqHk22vgrByej8lvMb2qbc,14702
5
+ napistu/identifiers.py,sha256=wque0qsMZK2AMsAhkF1ERSMrEF7h6b5SMp3iqVu8e1o,28796
6
+ napistu/indices.py,sha256=UeJjjsYs0sGvZIKz1y4ZQ6aUkABn-6TCUDZ2VCVT9JI,7534
7
+ napistu/mechanism_matching.py,sha256=CPhtM6GERmGlBK8zH1cEvSpsasa0mG7ojLKDOze3dyE,21704
8
+ napistu/sbml_dfs_core.py,sha256=iSng-3cpJVpVMb340YGM6s0pFBTA9SyYCdYvHdZRSMA,79387
9
+ napistu/sbml_dfs_utils.py,sha256=j6Bu3acqOFSEbyVzASXhlnV8hQvi4k-vdMYzVMPzz5A,10318
10
+ napistu/source.py,sha256=oBgw2OZLVBETQG8Mwoc5ZUe-6cg_Yt6Mxsto3fCdw1k,13386
11
+ napistu/utils.py,sha256=G4IfG_WBSxkn5RBdPPn9sAkzrz2BdKgXlFFmMsB9wsA,28038
12
+ napistu/gcs/__init__.py,sha256=1kqmRHlEyI7VpILzurZb1URwC_UIc1PTMEBHQnjXW6s,246
13
+ napistu/gcs/constants.py,sha256=rc-oQBh6pdu7cjqTCerHG_fDub-FQcEjzWh2ic715cs,2844
14
+ napistu/gcs/downloads.py,sha256=EiOxLW1MMexdPTSiakWknTB-BGY1y__s2n1z9Sd8VYM,5033
15
+ napistu/ingestion/__init__.py,sha256=1kqmRHlEyI7VpILzurZb1URwC_UIc1PTMEBHQnjXW6s,246
16
+ napistu/ingestion/bigg.py,sha256=XPJZv64mrIMCuKe1mjQfS5QPR9tmengGvndSjc3QFLA,5559
17
+ napistu/ingestion/constants.py,sha256=TYATiVNrLyuQ1AvLVt35F1xQ8pQ3U19o_N6ZSkdW3PA,9941
18
+ napistu/ingestion/cpr_edgelist.py,sha256=eVT9M7gmdBuGHcAYlvkD_zzvTtyzXufKWjwDiT8OxF4,3572
19
+ napistu/ingestion/identifiers_etl.py,sha256=6ppDUA6lEZurdmVbiFLOUzphYbr-hndMhtqsQnq_yAc,5009
20
+ napistu/ingestion/obo.py,sha256=pszLLfImZxDYjL3WQUCow2hQFURROGHqIq3qbgVtzAM,8836
21
+ napistu/ingestion/psi_mi.py,sha256=Icj73EK75ytFPBw-TH2B6yW1ZWAmckmn5mtPl9pIxuA,9389
22
+ napistu/ingestion/reactome.py,sha256=-Q3GsAsfVkTD7cDD1fLEEnWQbI6vs7nxsdYInk7ZvVE,7907
23
+ napistu/ingestion/sbml.py,sha256=gK6_jHgo6oaiG16WlrbBSvxq_0VzFR4a5fG9IQrp5bU,24153
24
+ napistu/ingestion/string.py,sha256=tsaHrjppgFbl9NnRcB2DytpoontqrpfQL65zD9HPgEM,11668
25
+ napistu/ingestion/trrust.py,sha256=ccjZc_eF3PdxxurnukiEo_e0-aKc_3z22NYbaJBtHdY,9774
26
+ napistu/ingestion/yeast.py,sha256=bwFBNxRq-dLDaddgBL1hpfZj0eQ56nBXyR_9n0NZT9Y,5233
27
+ napistu/modify/__init__.py,sha256=1kqmRHlEyI7VpILzurZb1URwC_UIc1PTMEBHQnjXW6s,246
28
+ napistu/modify/constants.py,sha256=KHigix_8A8kCLWYVGR9_6_n34UNDcq2guDLC1KLeNZ4,2648
29
+ napistu/modify/curation.py,sha256=UNeAfJ26XDFvSwkPL8WHCAP0FQYiVUrSvJn3UIt5jy8,21607
30
+ napistu/modify/gaps.py,sha256=XqwfvzgJywA7ws5hzDlj22xs5tRGc4xOdbQ2v51UJqc,23983
31
+ napistu/modify/pathwayannot.py,sha256=onbQy9YNYPbeOih8fSxymxUQJc1jXjRIQOABv3xkvng,47183
32
+ napistu/modify/uncompartmentalize.py,sha256=U5X4Q7Z-YIkC8_711x3sU21vTVdv9rKfauwz4JNzl6c,9690
33
+ napistu/network/__init__.py,sha256=1kqmRHlEyI7VpILzurZb1URwC_UIc1PTMEBHQnjXW6s,246
34
+ napistu/network/constants.py,sha256=jz8vRjgns74piUcvmoIP_f-8s9w15SxWAEw2lf6XmDY,3661
35
+ napistu/network/neighborhoods.py,sha256=TopPpcUD09bAfJuT_L4dkHwJhV1VJJlzXpyzldYi85A,55512
36
+ napistu/network/net_create.py,sha256=9Rb5I6uLlL50SNADucsT_90F9k7rzmp2EQfEgNlr37E,60343
37
+ napistu/network/net_utils.py,sha256=cMWLOHlz4XvPA8PlPiSFYNDjAEv4t1qlZxwabBaZrK8,21188
38
+ napistu/network/paths.py,sha256=a2J3JWIdMufdNs8Amh6I7s3TOVD2EzLV9khqbWHvGlA,16652
39
+ napistu/network/precompute.py,sha256=83Vr2pxCmEtJJmE_Lq1BI-pEmESDNG0N7vByXjBf_oQ,7517
40
+ napistu/rpy2/__init__.py,sha256=B9tZHiEp6bvysjqvBRQ1aGY493Ks9kouwb0pW7KsKqA,4100
41
+ napistu/rpy2/callr.py,sha256=76ICWj7Jso-qrYLNfiV-DgPyrMTdRXz_EhyGOD9CbFM,4301
42
+ napistu/rpy2/constants.py,sha256=JpJqsxImZis8fFFfePXYdbkhUZhXDZoHSHVf92w1h8U,2619
43
+ napistu/rpy2/netcontextr.py,sha256=gkpBgrASNeH_8IjFyY-Tj-S87HjNOkGdfMta0WRdEnU,16278
44
+ napistu/rpy2/rids.py,sha256=sGMTRuOQRDpHBHZwfTS7uKUW9TBI_yMpht6SFhup8vw,23937
45
+ napistu-0.1.0.dist-info/licenses/LICENSE,sha256=kW8wVT__JWoHjl2BbbJDAZInWa9AxzJeR_uv6-i5x1g,1063
46
+ tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
47
+ tests/conftest.py,sha256=uDuqgZKR37csoUI4t4U-pzEoANMFAIyTA4k8XLRu_Bw,2572
48
+ tests/test_consensus.py,sha256=u_5Hyocz2peNbJMd5ydgoAwQ4il0lFm-PrzrckTCitI,9403
49
+ tests/test_constants.py,sha256=gJLDv7QMeeBiiupyMazj6mumk20KWvGMgm2myHMKKfc,531
50
+ tests/test_curation.py,sha256=-Q2J0D7qs9PGjHZX-rM4NxRLLdwxoapytSo_98q9ItY,3864
51
+ tests/test_edgelist.py,sha256=bdEtQJdd4MeQsNtng9afHYNVDsEy0U07sfVwguAdIBM,560
52
+ tests/test_gcs.py,sha256=sq-zIDfmLIpZ5oFKCmyuaw9tfSzAY5hSnpuN-xqiqpk,561
53
+ tests/test_identifiers.py,sha256=RyuPAMhYI8cDOl2r62idweLxgy7rAs9omeZQ62h56kY,5019
54
+ tests/test_igraph.py,sha256=HoYeFAAFXWtxdonnUTpV59-jCLicNa0_utPaaGKXMAw,10926
55
+ tests/test_indices.py,sha256=-TrKfX4qXsofg_TPQEhHaQc_CuQMEd4_0maJgGCgSfE,2468
56
+ tests/test_mechanism_matching.py,sha256=gD_n2saM7yYa56QU0RMAYMKMAk7oF8ESbM7GHbI6bFY,4156
57
+ tests/test_net_utils.py,sha256=4HqfFF6yycAz7oQYRz6MefzQVQ_ZjWpeUEA4lUDOMJc,1614
58
+ tests/test_netcontextr.py,sha256=PKH0D-8EL0HNrCMtF-fAaYv5Lao4mwVPDZLQ5LHJXqc,3399
59
+ tests/test_obo.py,sha256=47qNCElPzu2nA36Oq83Dqp1RGhITqztjl7UyZ5cMsj4,959
60
+ tests/test_pathwayannot.py,sha256=bceosccNy9tgxQei_7j7ATBSSvBSxOngJvK-mAzR_K0,3312
61
+ tests/test_precomputed_distances.py,sha256=ht7lVz0wGOOQl9UTI1o9ftm0Dk7q8E40UV2jxVmE-Tg,7203
62
+ tests/test_rpy2.py,sha256=beihvGlWsQA9U7V3tfqBIOUL-S8m8Nj84Bg2Wt2sNH8,1491
63
+ tests/test_sbml.py,sha256=w_VU06psAP0Ku3B0flbP4hKhBfx2ZWV3oOdUgWzrMP4,1276
64
+ tests/test_sbml_dfs_create.py,sha256=w29mUcnC6g9Yqp8Q3b-oRQc5GiDvzjS5_GOE_LjwGZo,9982
65
+ tests/test_sbml_dfs_utils.py,sha256=onFWdhrTix30XR1-CMrMXld37BYxEGi6TZrweugLDzI,505
66
+ tests/test_sbo.py,sha256=x_PENFaXYsrZIzOZu9cj_Wrej7i7SNGxgBYYvcigLs0,308
67
+ tests/test_set_coverage.py,sha256=gM6Zl3MhVRHUi0_z0ISqpeXckWT8XdpXb58ipCoWyHU,1606
68
+ tests/test_source.py,sha256=hT0IlpexR5zP0OhWl5BBaho9d1aCYQlFZLwRIRRnw_Y,1969
69
+ tests/test_uncompartmentalize.py,sha256=nAk5kfAVLU9a2VWe2x2HYVcKqj-EnwmwddERIPRax8c,1289
70
+ tests/test_utils.py,sha256=knOWMN9xgaNLDj_4T_ZI3f22p1ZqovRLVDBFaMhOnFs,14845
71
+ tests/utils.py,sha256=SoWQ_5roJteFGcMaOeEiQ5ucwq3Z2Fa3AAs9iXHTsJY,749
72
+ tests/test_data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
73
+ napistu-0.1.0.dist-info/METADATA,sha256=bFJYn_d8Q0WfF5fpnE5tRZAqU3jNHuxXV0xyI-bt0yk,1830
74
+ napistu-0.1.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
75
+ napistu-0.1.0.dist-info/entry_points.txt,sha256=_QnaPOvJNA3IltxmZgWIiBoen-L1bPYX18YQfC7oJgQ,41
76
+ napistu-0.1.0.dist-info/top_level.txt,sha256=Gpvk0a_PjrtqhYcQ9IDr3zR5LqpZ-uIHidQMIpjlvhY,14
77
+ napistu-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (78.1.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ cpr = cpr.__main__:cli
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Calico
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,2 @@
1
+ napistu
2
+ tests
tests/__init__.py ADDED
File without changes
tests/conftest.py ADDED
@@ -0,0 +1,83 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import sys
5
+
6
+ from napistu import consensus
7
+ from napistu import indices
8
+ from napistu import sbml_dfs_core
9
+ from napistu.ingestion import sbml
10
+ from pytest import fixture
11
+ from pytest import skip
12
+
13
+
14
+ @fixture
15
+ def sbml_path():
16
+ test_path = os.path.abspath(os.path.join(__file__, os.pardir))
17
+ sbml_path = os.path.join(test_path, "test_data", "R-HSA-1237044.sbml")
18
+
19
+ if not os.path.isfile(sbml_path):
20
+ raise ValueError(f"{sbml_path} not found")
21
+ return sbml_path
22
+
23
+
24
+ @fixture
25
+ def sbml_model(sbml_path):
26
+ sbml_model = sbml.SBML(sbml_path)
27
+ return sbml_model
28
+
29
+
30
+ @fixture
31
+ def sbml_dfs(sbml_model):
32
+ sbml_dfs = sbml_dfs_core.SBML_dfs(sbml_model)
33
+ return sbml_dfs
34
+
35
+
36
+ @fixture
37
+ def sbml_dfs_metabolism():
38
+ test_path = os.path.abspath(os.path.join(__file__, os.pardir))
39
+ test_data = os.path.join(test_path, "test_data")
40
+
41
+ pw_index = indices.PWIndex(os.path.join(test_data, "pw_index_metabolism.tsv"))
42
+ sbml_dfs_dict = consensus.construct_sbml_dfs_dict(pw_index)
43
+ sbml_dfs = consensus.construct_consensus_model(sbml_dfs_dict, pw_index)
44
+
45
+ return sbml_dfs
46
+
47
+
48
+ @fixture
49
+ def sbml_dfs_glucose_metabolism():
50
+ test_path = os.path.abspath(os.path.join(__file__, os.pardir))
51
+ test_data = os.path.join(test_path, "test_data")
52
+ sbml_path = os.path.join(test_data, "reactome_glucose_metabolism.sbml")
53
+
54
+ sbml_model = sbml.SBML(sbml_path).model
55
+ sbml_dfs = sbml_dfs_core.SBML_dfs(sbml_model)
56
+
57
+ return sbml_dfs
58
+
59
+
60
+ # Define custom markers for platforms
61
+ def pytest_configure(config):
62
+ config.addinivalue_line("markers", "skip_on_windows: mark test to skip on Windows")
63
+ config.addinivalue_line("markers", "skip_on_macos: mark test to skip on macOS")
64
+ config.addinivalue_line("markers", "unix_only: mark test to run only on Unix/Linux systems")
65
+
66
+ # Define platform conditions
67
+ is_windows = sys.platform == "win32"
68
+ is_macos = sys.platform == "darwin"
69
+ is_unix = not (is_windows or is_macos)
70
+
71
+ # Apply skipping based on platform
72
+ def pytest_runtest_setup(item):
73
+ # Skip tests marked to be skipped on Windows
74
+ if is_windows and any(mark.name == "skip_on_windows" for mark in item.iter_markers()):
75
+ skip("Test skipped on Windows")
76
+
77
+ # Skip tests marked to be skipped on macOS
78
+ if is_macos and any(mark.name == "skip_on_macos" for mark in item.iter_markers()):
79
+ skip("Test skipped on macOS")
80
+
81
+ # Skip tests that should run only on Unix
82
+ if not is_unix and any(mark.name == "unix_only" for mark in item.iter_markers()):
83
+ skip("Test runs only on Unix systems")
@@ -0,0 +1,255 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+
5
+ import pandas as pd
6
+ import pytest
7
+ from napistu import consensus
8
+ from napistu import indices
9
+ from napistu import source
10
+ from napistu.ingestion import sbml
11
+ from napistu.modify import pathwayannot
12
+
13
+ test_path = os.path.abspath(os.path.join(__file__, os.pardir))
14
+ test_data = os.path.join(test_path, "test_data")
15
+
16
+
17
+ def test_reduce_to_consensus_ids():
18
+ sbml_path = os.path.join(test_data, "R-HSA-1237044.sbml")
19
+
20
+ # test aggregating by IDs, by moving from compartmentalized_species -> species
21
+
22
+ sbml_model = sbml.SBML(sbml_path).model
23
+ comp_species_df = sbml.setup_cspecies(sbml_model)
24
+ comp_species_df.index.names = ["s_id"]
25
+ consensus_species, species_lookup = consensus.reduce_to_consensus_ids(
26
+ comp_species_df, {"pk": "s_id", "id": "s_Identifiers"}
27
+ )
28
+
29
+ assert isinstance(consensus_species, pd.DataFrame)
30
+ assert consensus_species.shape == (18, 4)
31
+ assert isinstance(species_lookup, pd.Series)
32
+ assert species_lookup.size == 23
33
+
34
+
35
+ def test_consensus():
36
+ pw_index = indices.PWIndex(os.path.join(test_data, "pw_index.tsv"))
37
+ sbml_dfs_dict = consensus.construct_sbml_dfs_dict(pw_index)
38
+
39
+ consensus_model = consensus.construct_consensus_model(sbml_dfs_dict, pw_index)
40
+ assert consensus_model.species.shape == (38, 3)
41
+ assert consensus_model.reactions.shape == (30, 4)
42
+ assert consensus_model.reaction_species.shape == (137, 4)
43
+
44
+ consensus_model = pathwayannot.drop_cofactors(consensus_model)
45
+ assert consensus_model.species.shape == (38, 3)
46
+ assert consensus_model.reaction_species.shape == (52, 4)
47
+ # update reaction_species.shape after more cofactors identified
48
+
49
+ consensus_model.validate()
50
+
51
+
52
+ def test_source_tracking():
53
+ # create input data
54
+ table_schema = {"source": "source_var", "pk": "primary_key"}
55
+
56
+ # define existing sources and the new_id entity they belong to
57
+ # here, we are assuming that each model has a blank source object
58
+ # as if it came from a non-consensus model
59
+ agg_tbl = pd.DataFrame(
60
+ {
61
+ "new_id": [0, 0, 1, 1],
62
+ }
63
+ )
64
+ agg_tbl[table_schema["source"]] = source.Source(init=True)
65
+
66
+ # define new_ids and the models they came from
67
+ # these models will be matched to the pw_index to flush out metadata
68
+ lookup_table = pd.DataFrame(
69
+ {
70
+ "new_id": [0, 0, 1, 1],
71
+ "model": ["R-HSA-1237044", "R-HSA-425381", "R-HSA-1237044", "R-HSA-425381"],
72
+ }
73
+ )
74
+
75
+ # use an existing pw_index since pw_index currently checks for the existence of the source file
76
+ pw_index = indices.PWIndex(os.path.join(test_data, "pw_index.tsv"))
77
+
78
+ # test create source table
79
+ source_table = source.create_source_table(lookup_table, table_schema, pw_index)
80
+ assert source_table["source_var"][0].source.shape == (2, 8)
81
+
82
+ # test create_consensus_sources
83
+ consensus_sources = consensus.create_consensus_sources(
84
+ agg_tbl, lookup_table, table_schema, pw_index
85
+ )
86
+ assert consensus_sources[0].source.shape == (2, 8)
87
+
88
+ # lets add a model which does not have a reference in the pw_index
89
+ invalid_lookup_table = pd.DataFrame(
90
+ {
91
+ "new_id": [0, 0, 1, 1],
92
+ "model": ["R-HSA-1237044", "R-HSA-425381", "R-HSA-1237044", "typo"],
93
+ }
94
+ )
95
+
96
+ # expect a ValueError when the model is not found
97
+ with pytest.raises(ValueError) as _:
98
+ source.create_source_table(invalid_lookup_table, table_schema, pw_index)
99
+
100
+ # now we will aggregate the consensus model above with a new single model (which has some
101
+ # overlapping entries with the consensusd (id 1) and some new ids (id 2)
102
+
103
+ agg_tbl2 = pd.DataFrame(
104
+ {
105
+ "new_id": [0, 1, 1, 2],
106
+ }
107
+ )
108
+
109
+ agg_tbl2[table_schema["source"]] = consensus_sources.tolist() + [
110
+ source.Source(init=True) for i in range(0, 2)
111
+ ]
112
+
113
+ lookup_table2 = pd.DataFrame(
114
+ {
115
+ "new_id": [0, 1, 1, 2],
116
+ # the model for the first two entries should really correspond to the "consensus"
117
+ # but since this is not a file I will stub with one of the pw_index entries
118
+ "model": [
119
+ "R-HSA-1247673",
120
+ "R-HSA-1247673",
121
+ "R-HSA-1475029",
122
+ "R-HSA-1475029",
123
+ ],
124
+ }
125
+ )
126
+
127
+ source_table = source.create_source_table(lookup_table2, table_schema, pw_index)
128
+ assert source_table.shape == (3, 1)
129
+ assert [
130
+ source_table["source_var"][i].source.shape
131
+ for i in range(0, source_table.shape[0])
132
+ ] == [(1, 8), (2, 8), (1, 8)]
133
+
134
+ consensus_sources = consensus.create_consensus_sources(
135
+ agg_tbl2, lookup_table2, table_schema, pw_index
136
+ )
137
+ assert [
138
+ consensus_sources[i].source.shape for i in range(0, consensus_sources.shape[0])
139
+ ] == [(3, 8), (4, 8), (1, 8)]
140
+
141
+
142
+ def test_passing_entity_data():
143
+ pw_index = indices.PWIndex(os.path.join(test_data, "pw_index.tsv"))
144
+ sbml_dfs_dict = consensus.construct_sbml_dfs_dict(pw_index)
145
+
146
+ for model in list(sbml_dfs_dict.keys())[0:3]:
147
+ sbml_dfs_dict[model].add_species_data(
148
+ "my_species_data",
149
+ sbml_dfs_dict[model]
150
+ .species.iloc[0:5]
151
+ .assign(my_species_data_var="testing")["my_species_data_var"]
152
+ .to_frame(),
153
+ )
154
+ sbml_dfs_dict[model].add_reactions_data(
155
+ "my_reactions_data",
156
+ sbml_dfs_dict[model]
157
+ .reactions.iloc[0:5]
158
+ .assign(my_reactions_data_var1="testing")
159
+ .assign(my_reactions_data_var2="testing2")[
160
+ ["my_reactions_data_var1", "my_reactions_data_var2"]
161
+ ],
162
+ )
163
+
164
+ # create a consensus with perfect merges of overlapping id-table-variable values
165
+ # i.e., when combined all merged entries have the same attributes
166
+ consensus_model = consensus.construct_consensus_model(sbml_dfs_dict, pw_index)
167
+
168
+ assert len(consensus_model.species_data) == 1
169
+ assert consensus_model.species_data["my_species_data"].shape == (10, 1)
170
+ assert len(consensus_model.reactions_data) == 1
171
+ assert consensus_model.reactions_data["my_reactions_data"].shape == (14, 2)
172
+
173
+ # add different tables from different models
174
+ for model in list(sbml_dfs_dict.keys())[3:5]:
175
+ sbml_dfs_dict[model].add_species_data(
176
+ "my_other_species_data",
177
+ sbml_dfs_dict[model]
178
+ .species.iloc[0:5]
179
+ .assign(my_species_data="testing")["my_species_data"]
180
+ .to_frame(),
181
+ )
182
+
183
+ consensus_model = consensus.construct_consensus_model(sbml_dfs_dict, pw_index)
184
+ assert len(consensus_model.species_data) == 2
185
+
186
+ # create a case where reactions will be merged and the same reaction
187
+ # in different models has a different value for its reactions_data
188
+ minimal_pw_index = pw_index
189
+ minimal_pw_index.index = minimal_pw_index.index.iloc[0:2]
190
+ minimal_pw_index.index["file"].loc[1] = minimal_pw_index.index["file"][0]
191
+
192
+ duplicated_sbml_dfs_dict = consensus.construct_sbml_dfs_dict(minimal_pw_index)
193
+ # explicitely define the order we'll loop through models so that
194
+ # the position of a model can be used to set mismatching attributes
195
+ # for otherwise identical models
196
+ model_order = list(duplicated_sbml_dfs_dict.keys())
197
+
198
+ for model in duplicated_sbml_dfs_dict.keys():
199
+ model_index = model_order.index(model)
200
+
201
+ duplicated_sbml_dfs_dict[model].add_reactions_data(
202
+ "my_mismatched_data",
203
+ duplicated_sbml_dfs_dict[model]
204
+ .reactions.iloc[0:5]
205
+ .assign(my_reactions_data_var1=model)["my_reactions_data_var1"]
206
+ .to_frame()
207
+ .assign(numeric_var=[x + model_index for x in range(0, 5)])
208
+ .assign(bool_var=[x + model_index % 2 == 0 for x in range(0, 5)]),
209
+ )
210
+
211
+ # assign reversibility is True for one model to
212
+ # confirm that reversibility trumps irreversible
213
+ # when merging reactions with identical stoichiometry but
214
+ # different reversibility attributes
215
+
216
+ duplicated_sbml_dfs_dict["R-HSA-1237044"].reactions = duplicated_sbml_dfs_dict[
217
+ "R-HSA-1237044"
218
+ ].reactions.assign(r_isreversible=True)
219
+
220
+ consensus_model = consensus.construct_consensus_model(
221
+ duplicated_sbml_dfs_dict, pw_index
222
+ )
223
+ assert consensus_model.reactions_data["my_mismatched_data"].shape == (5, 3)
224
+ assert consensus_model.reactions["r_isreversible"].eq(True).all()
225
+
226
+
227
+ def test_consensus_ontology_check():
228
+ pw_index = indices.PWIndex(os.path.join(test_data, "pw_index.tsv"))
229
+
230
+ test_sbml_dfs_dict = consensus.construct_sbml_dfs_dict(pw_index)
231
+ test_consensus_model = consensus.construct_consensus_model(
232
+ test_sbml_dfs_dict, pw_index
233
+ )
234
+
235
+ pre_shared_onto_sp_list, pre_onto_df = consensus.pre_consensus_ontology_check(
236
+ test_sbml_dfs_dict, "species"
237
+ )
238
+ assert set(pre_shared_onto_sp_list) == {"chebi", "reactome", "uniprot"}
239
+
240
+ post_shared_onto_sp_set = consensus.post_consensus_species_ontology_check(
241
+ test_consensus_model
242
+ )
243
+ assert post_shared_onto_sp_set == {"chebi", "reactome", "uniprot"}
244
+
245
+
246
+ ################################################
247
+ # __main__
248
+ ################################################
249
+
250
+ if __name__ == "__main__":
251
+ test_reduce_to_consensus_ids()
252
+ test_consensus()
253
+ test_source_tracking()
254
+ test_passing_entity_data()
255
+ test_consensus_ontology_check()
@@ -0,0 +1,20 @@
1
+ from __future__ import annotations
2
+
3
+ from napistu import constants
4
+
5
+
6
+ def test_sbo_constants():
7
+ # all SBO terms in "MINI_SBO" set have a role
8
+ assert set(constants.SBO_NAME_TO_ROLE.keys()) == set(
9
+ constants.MINI_SBO_FROM_NAME.keys()
10
+ )
11
+ # all roles are valid
12
+ assert [x in constants.VALID_SBO_ROLES for x in constants.SBO_NAME_TO_ROLE.values()]
13
+
14
+
15
+ ################################################
16
+ # __main__
17
+ ################################################
18
+
19
+ if __name__ == "__main__":
20
+ test_sbo_constants()
tests/test_curation.py ADDED
@@ -0,0 +1,134 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+
5
+ import pandas as pd
6
+ from napistu import sbml_dfs_core
7
+ from napistu.ingestion import sbml
8
+ from napistu.modify import curation
9
+
10
+ test_path = os.path.abspath(os.path.join(__file__, os.pardir))
11
+ sbml_path = os.path.join(test_path, "test_data", "R-HSA-1237044.sbml")
12
+
13
+ if not os.path.isfile(sbml_path):
14
+ raise ValueError(f"{sbml_path} not found")
15
+
16
+ # setup mock curations
17
+ curation_dict = dict()
18
+ curation_dict["species"] = pd.DataFrame(
19
+ [
20
+ {
21
+ "species": "hello",
22
+ "uri": "http://www.ebi.ac.uk/chebi/searchId.do?chebiId=CHEBI:35828",
23
+ "curator": "Sean",
24
+ },
25
+ {"species": "good day", "uri": None, "curator": "Sean"},
26
+ ]
27
+ )
28
+
29
+ curation_dict["compartmentalized_species"] = pd.DataFrame(
30
+ [
31
+ {
32
+ "compartmentalized_species": "hello [cytosol]",
33
+ "s_name": "hello",
34
+ "c_name": "cytosol",
35
+ "curator": "Sean",
36
+ }
37
+ ]
38
+ )
39
+
40
+ curation_dict["reactions"] = pd.DataFrame(
41
+ [
42
+ {
43
+ "reactions": "there",
44
+ "stoichiometry": "hello [cytosol] -> CO2 [cytosol]",
45
+ "uri": None,
46
+ "evidence": "how is",
47
+ "curator": "Sean",
48
+ },
49
+ {
50
+ "reactions": "where",
51
+ "stoichiometry": "CO2 [cytosol] -> hello [cytosol]",
52
+ "uri": None,
53
+ "evidence": "your family",
54
+ "curator": "Sean",
55
+ },
56
+ ]
57
+ )
58
+
59
+ curation_dict["reaction_species"] = pd.DataFrame(
60
+ [
61
+ {
62
+ "reaction_species": "NADH [cytosol]",
63
+ "r_name": "CYB5Rs reduce MetHb to HbA",
64
+ "stoichiometry": 0,
65
+ "sbo_term_name": "stimulator",
66
+ "evidence": "weeeee",
67
+ "curator": "Sean",
68
+ }
69
+ ]
70
+ )
71
+
72
+ curation_dict["remove"] = pd.DataFrame(
73
+ [
74
+ {"remove": "reaction_1237042", "table": "reactions", "variable": "r_id"},
75
+ {
76
+ "remove": "CYB5Rs reduce MetHb to HbA",
77
+ "table": "reactions",
78
+ "variable": "r_name",
79
+ },
80
+ {"remove": "CO2", "table": "species", "variable": "s_name"},
81
+ ]
82
+ )
83
+
84
+
85
+ def test_remove_entities():
86
+ sbml_model = sbml.SBML(sbml_path)
87
+ sbml_dfs = sbml_dfs_core.SBML_dfs(sbml_model)
88
+ sbml_dfs.validate()
89
+
90
+ invalid_entities_dict = curation._find_invalid_entities(
91
+ sbml_dfs, curation_dict["remove"]
92
+ )
93
+ invalid_pks = set(invalid_entities_dict.keys())
94
+
95
+ assert invalid_pks == {"sc_id", "rsc_id", "r_id", "s_id"}
96
+
97
+ n_species = sbml_dfs.species.shape[0]
98
+ n_reactions = sbml_dfs.reactions.shape[0]
99
+ n_compartmentalized_species = sbml_dfs.compartmentalized_species.shape[0]
100
+ n_reaction_species = sbml_dfs.reaction_species.shape[0]
101
+ # should be untouched
102
+ n_compartments = sbml_dfs.compartments.shape[0]
103
+
104
+ sbml_dfs = curation._remove_entities(sbml_dfs, invalid_entities_dict)
105
+
106
+ assert n_species - sbml_dfs.species.shape[0] == 1
107
+ assert n_reactions - sbml_dfs.reactions.shape[0] == 2
108
+ assert (
109
+ n_compartmentalized_species - sbml_dfs.compartmentalized_species.shape[0] == 2
110
+ )
111
+ assert n_reaction_species - sbml_dfs.reaction_species.shape[0] == 14
112
+ assert n_compartments - sbml_dfs.compartments.shape[0] == 0
113
+
114
+
115
+ def test_add_entities():
116
+ sbml_model = sbml.SBML(sbml_path)
117
+ sbml_dfs = sbml_dfs_core.SBML_dfs(sbml_model)
118
+ sbml_dfs.validate()
119
+
120
+ new_entities = curation.format_curations(curation_dict, sbml_dfs)
121
+
122
+ assert new_entities["species"].shape == (2, 3)
123
+ assert new_entities["reactions"].shape == (2, 4)
124
+ assert new_entities["compartmentalized_species"].shape == (1, 4)
125
+ assert new_entities["reaction_species"].shape == (5, 4)
126
+
127
+
128
+ ################################################
129
+ # __main__
130
+ ################################################
131
+
132
+ if __name__ == "__main__":
133
+ test_remove_entities()
134
+ test_add_entities()
File without changes