jsrc 0.2.1__tar.gz → 0.2.2__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 (97) hide show
  1. {jsrc-0.2.1/src/jsrc.egg-info → jsrc-0.2.2}/PKG-INFO +22 -10
  2. {jsrc-0.2.1 → jsrc-0.2.2}/README.md +21 -9
  3. {jsrc-0.2.1 → jsrc-0.2.2}/pyproject.toml +4 -1
  4. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/cli.py +0 -8
  5. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/grn/__init__.py +2 -1
  6. jsrc-0.2.2/src/jsrc/grn/build.py +88 -0
  7. jsrc-0.2.2/src/jsrc/grn/net2json.py +45 -0
  8. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/grn/serve.py +7 -12
  9. jsrc-0.2.2/src/jsrc/grn/sources/index.html +37 -0
  10. jsrc-0.2.2/src/jsrc/grn/sources/script.js +521 -0
  11. jsrc-0.2.1/src/jsrc/grn/viewer.py → jsrc-0.2.2/src/jsrc/grn/sources/style.css +1 -81
  12. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/gs/train.py +1 -1
  13. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/seq/qc.py +0 -1
  14. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/seq/translate.py +11 -0
  15. {jsrc-0.2.1 → jsrc-0.2.2/src/jsrc.egg-info}/PKG-INFO +22 -10
  16. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc.egg-info/SOURCES.txt +4 -1
  17. jsrc-0.2.1/src/jsrc/grn/net2json.py +0 -144
  18. {jsrc-0.2.1 → jsrc-0.2.2}/LICENSE +0 -0
  19. {jsrc-0.2.1 → jsrc-0.2.2}/setup.cfg +0 -0
  20. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/__init__.py +0 -0
  21. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/analyze/__init__.py +0 -0
  22. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/analyze/bootstrap_phylo.py +0 -0
  23. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/analyze/core.py +0 -0
  24. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/analyze/motif.py +0 -0
  25. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/analyze/msa_consensus.py +0 -0
  26. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/analyze/phylo.py +0 -0
  27. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/analyze/qc.py +0 -0
  28. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/analyze/snpindel.py +0 -0
  29. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/core.py +0 -0
  30. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/grn/anno2json.py +0 -0
  31. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/grn/centrality.py +0 -0
  32. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/grn/core.py +0 -0
  33. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/gs/__init__.py +0 -0
  34. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/gs/build.py +0 -0
  35. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/gs/split.py +0 -0
  36. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/job/__init__.py +0 -0
  37. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/job/core.py +0 -0
  38. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/job/gc.py +0 -0
  39. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/job/history.py +0 -0
  40. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/job/kill.py +0 -0
  41. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/job/logs.py +0 -0
  42. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/job/ls.py +0 -0
  43. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/job/show.py +0 -0
  44. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/job/submit.py +0 -0
  45. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/plot/__init__.py +0 -0
  46. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/plot/chromosome.py +0 -0
  47. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/plot/circoslite.py +0 -0
  48. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/plot/cis.py +0 -0
  49. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/plot/core.py +0 -0
  50. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/plot/domain.py +0 -0
  51. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/plot/dotplot.py +0 -0
  52. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/plot/exon.py +0 -0
  53. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/plot/gene.py +0 -0
  54. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/plot/heart.py +0 -0
  55. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/plot/rose.py +0 -0
  56. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/seq/__init__.py +0 -0
  57. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/seq/codon.py +0 -0
  58. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/seq/core.py +0 -0
  59. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/seq/digest.py +0 -0
  60. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/seq/extract.py +0 -0
  61. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/seq/fetch.py +0 -0
  62. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/seq/kmer.py +0 -0
  63. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/seq/promoter.py +0 -0
  64. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/seq/rename.py +0 -0
  65. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/seq/window.py +0 -0
  66. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/vision/__init__.py +0 -0
  67. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/vision/core.py +0 -0
  68. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/vision/efd.py +0 -0
  69. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/vision/extract.py +0 -0
  70. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc/vision/traits.py +0 -0
  71. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc.egg-info/dependency_links.txt +0 -0
  72. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc.egg-info/entry_points.txt +0 -0
  73. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc.egg-info/requires.txt +0 -0
  74. {jsrc-0.2.1 → jsrc-0.2.2}/src/jsrc.egg-info/top_level.txt +0 -0
  75. {jsrc-0.2.1 → jsrc-0.2.2}/tests/test_analyze_extra.py +0 -0
  76. {jsrc-0.2.1 → jsrc-0.2.2}/tests/test_analyze_phylo.py +0 -0
  77. {jsrc-0.2.1 → jsrc-0.2.2}/tests/test_cli_error_behavior.py +0 -0
  78. {jsrc-0.2.1 → jsrc-0.2.2}/tests/test_cli_module_flows.py +0 -0
  79. {jsrc-0.2.1 → jsrc-0.2.2}/tests/test_grn_conversion.py +0 -0
  80. {jsrc-0.2.1 → jsrc-0.2.2}/tests/test_gs_train.py +0 -0
  81. {jsrc-0.2.1 → jsrc-0.2.2}/tests/test_job_core.py +0 -0
  82. {jsrc-0.2.1 → jsrc-0.2.2}/tests/test_job_portability.py +0 -0
  83. {jsrc-0.2.1 → jsrc-0.2.2}/tests/test_plot_commands.py +0 -0
  84. {jsrc-0.2.1 → jsrc-0.2.2}/tests/test_plot_core.py +0 -0
  85. {jsrc-0.2.1 → jsrc-0.2.2}/tests/test_progress.py +0 -0
  86. {jsrc-0.2.1 → jsrc-0.2.2}/tests/test_seq_codon_kmer.py +0 -0
  87. {jsrc-0.2.1 → jsrc-0.2.2}/tests/test_seq_core.py +0 -0
  88. {jsrc-0.2.1 → jsrc-0.2.2}/tests/test_seq_digest.py +0 -0
  89. {jsrc-0.2.1 → jsrc-0.2.2}/tests/test_seq_extract.py +0 -0
  90. {jsrc-0.2.1 → jsrc-0.2.2}/tests/test_seq_fetch.py +0 -0
  91. {jsrc-0.2.1 → jsrc-0.2.2}/tests/test_seq_promoter.py +0 -0
  92. {jsrc-0.2.1 → jsrc-0.2.2}/tests/test_seq_qc.py +0 -0
  93. {jsrc-0.2.1 → jsrc-0.2.2}/tests/test_seq_rename.py +0 -0
  94. {jsrc-0.2.1 → jsrc-0.2.2}/tests/test_seq_translate.py +0 -0
  95. {jsrc-0.2.1 → jsrc-0.2.2}/tests/test_seq_window.py +0 -0
  96. {jsrc-0.2.1 → jsrc-0.2.2}/tests/test_vision_efd.py +0 -0
  97. {jsrc-0.2.1 → jsrc-0.2.2}/tests/test_vision_extract.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jsrc
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: Python library for bioinformatics and scientific computing
5
5
  Author-email: Jiaoyuan <imjiaoyuan@gmail.com>
6
6
  License-Expression: MIT
@@ -38,27 +38,36 @@ Python library for bioinformatics and scientific computing.
38
38
 
39
39
  ## Installation
40
40
 
41
- PyPI:
41
+ Recommended (global CLI via `uv tool`):
42
42
  ```bash
43
- pip install jsrc
43
+ uv tool install jsrc
44
44
  ```
45
45
 
46
- uv:
46
+ Using `uv` virtual environment:
47
47
  ```bash
48
+ uv venv
49
+ source .venv/bin/activate
48
50
  uv pip install jsrc
49
51
  ```
50
52
 
51
- From source:
53
+ Using Conda virtual environment + pip:
54
+ ```bash
55
+ conda create -n jsrc python=3.11 -y
56
+ conda activate jsrc
57
+ pip install jsrc
58
+ ```
59
+
60
+ From source (development):
52
61
  ```bash
53
62
  git clone https://github.com/imjiaoyuan/jsrc.git
54
63
  cd jsrc
55
64
  uv venv
56
- uv sync --extra dev
65
+ uv sync --extra dev --extra all
57
66
  ```
58
67
 
59
68
  Run `jsrc --help` to get started.
60
69
 
61
- For detailed usage, see the [Documentation](docs/en/index.md).中文文档请参阅 [文档](docs/zh/index.md)。
70
+ For detailed usage, see the [Documentation](docs/en/index.md). 中文文档请参阅 [文档](docs/zh/index.md)。
62
71
 
63
72
  ## Quick Start
64
73
 
@@ -84,7 +93,7 @@ jsrc vision extract --help
84
93
  | `plot` | Gene/exon/chromosome/domain and other plots | `jsrc plot gene ...` |
85
94
  | `analyze` | Phylogeny, motif, consensus, SNP/INDEL, QC | `jsrc analyze phylo ...` |
86
95
  | `gs` | Genomic selection dataset build/split/train | `jsrc gs train ...` |
87
- | `grn` | GRN conversion, centrality, local viewer | `jsrc grn net2json ...` |
96
+ | `grn` | GRN conversion, centrality, build packaging, local serve | `jsrc grn build ...` |
88
97
  | `vision` | Object extraction, morphology traits, EFD | `jsrc vision extract ...` |
89
98
  | `job` | Background job submit/list/log/kill/history | `jsrc job submit "cmd"` |
90
99
 
@@ -136,10 +145,13 @@ jsrc vision traits -i test/leaf1.jpg --channel a --invert
136
145
 
137
146
  **grn module**
138
147
 
139
- Generate 1000-gene random network viewer with full-view mode (top 200 nodes):
148
+ Generate and launch a 1000-gene random network viewer:
140
149
 
141
150
  ```bash
142
- jsrc grn net2json -i test/grn/network.tsv -o test/grn/json/grn.json -n test/grn/annotation.tsv -z test/grn/grn-viewer.zip -s
151
+ jsrc grn net2json -i test/grn/network.tsv -o test/grn/grn.json
152
+ jsrc grn anno2json -i test/grn/annotation.tsv -o test/grn/annotation.json
153
+ jsrc grn build -d test/grn/public -g test/grn/grn.json -n test/grn/annotation.json -z test/grn/grn-viewer.zip -a -t 200
154
+ jsrc grn serve -d test/grn/public -g test/grn/public/json/grn.json -n test/grn/public/json/annotation.json -p 8000 -a -t 200
143
155
  ```
144
156
 
145
157
  ![](assets/grn.jpg)
@@ -4,27 +4,36 @@ Python library for bioinformatics and scientific computing.
4
4
 
5
5
  ## Installation
6
6
 
7
- PyPI:
7
+ Recommended (global CLI via `uv tool`):
8
8
  ```bash
9
- pip install jsrc
9
+ uv tool install jsrc
10
10
  ```
11
11
 
12
- uv:
12
+ Using `uv` virtual environment:
13
13
  ```bash
14
+ uv venv
15
+ source .venv/bin/activate
14
16
  uv pip install jsrc
15
17
  ```
16
18
 
17
- From source:
19
+ Using Conda virtual environment + pip:
20
+ ```bash
21
+ conda create -n jsrc python=3.11 -y
22
+ conda activate jsrc
23
+ pip install jsrc
24
+ ```
25
+
26
+ From source (development):
18
27
  ```bash
19
28
  git clone https://github.com/imjiaoyuan/jsrc.git
20
29
  cd jsrc
21
30
  uv venv
22
- uv sync --extra dev
31
+ uv sync --extra dev --extra all
23
32
  ```
24
33
 
25
34
  Run `jsrc --help` to get started.
26
35
 
27
- For detailed usage, see the [Documentation](docs/en/index.md).中文文档请参阅 [文档](docs/zh/index.md)。
36
+ For detailed usage, see the [Documentation](docs/en/index.md). 中文文档请参阅 [文档](docs/zh/index.md)。
28
37
 
29
38
  ## Quick Start
30
39
 
@@ -50,7 +59,7 @@ jsrc vision extract --help
50
59
  | `plot` | Gene/exon/chromosome/domain and other plots | `jsrc plot gene ...` |
51
60
  | `analyze` | Phylogeny, motif, consensus, SNP/INDEL, QC | `jsrc analyze phylo ...` |
52
61
  | `gs` | Genomic selection dataset build/split/train | `jsrc gs train ...` |
53
- | `grn` | GRN conversion, centrality, local viewer | `jsrc grn net2json ...` |
62
+ | `grn` | GRN conversion, centrality, build packaging, local serve | `jsrc grn build ...` |
54
63
  | `vision` | Object extraction, morphology traits, EFD | `jsrc vision extract ...` |
55
64
  | `job` | Background job submit/list/log/kill/history | `jsrc job submit "cmd"` |
56
65
 
@@ -102,10 +111,13 @@ jsrc vision traits -i test/leaf1.jpg --channel a --invert
102
111
 
103
112
  **grn module**
104
113
 
105
- Generate 1000-gene random network viewer with full-view mode (top 200 nodes):
114
+ Generate and launch a 1000-gene random network viewer:
106
115
 
107
116
  ```bash
108
- jsrc grn net2json -i test/grn/network.tsv -o test/grn/json/grn.json -n test/grn/annotation.tsv -z test/grn/grn-viewer.zip -s
117
+ jsrc grn net2json -i test/grn/network.tsv -o test/grn/grn.json
118
+ jsrc grn anno2json -i test/grn/annotation.tsv -o test/grn/annotation.json
119
+ jsrc grn build -d test/grn/public -g test/grn/grn.json -n test/grn/annotation.json -z test/grn/grn-viewer.zip -a -t 200
120
+ jsrc grn serve -d test/grn/public -g test/grn/public/json/grn.json -n test/grn/public/json/annotation.json -p 8000 -a -t 200
109
121
  ```
110
122
 
111
123
  ![](assets/grn.jpg)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "jsrc"
3
- version = "0.2.1"
3
+ version = "0.2.2"
4
4
  description = "Python library for bioinformatics and scientific computing"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -39,5 +39,8 @@ build-backend = "setuptools.build_meta"
39
39
  where = ["src"]
40
40
  include = ["jsrc*"]
41
41
 
42
+ [tool.setuptools.package-data]
43
+ "jsrc.grn" = ["sources/*"]
44
+
42
45
  [tool.black]
43
46
  target-version = ["py310"]
@@ -14,14 +14,6 @@ def setup_logging(verbose: bool = False) -> None:
14
14
  )
15
15
 
16
16
 
17
- def _dispatch(module_name: str, func_name: str = "cmd") -> argparse.Action:
18
- def _runner(args: argparse.Namespace) -> None:
19
- module = importlib.import_module(module_name)
20
- getattr(module, func_name)(args)
21
-
22
- return _runner
23
-
24
-
25
17
  MODULES = {
26
18
  "seq": "jsrc.seq",
27
19
  "plot": "jsrc.plot",
@@ -1,6 +1,6 @@
1
1
  from typing import Any
2
2
 
3
- from jsrc.grn import anno2json, centrality, net2json, serve
3
+ from jsrc.grn import anno2json, build, centrality, net2json, serve
4
4
 
5
5
 
6
6
  def register_subparser(subparsers: Any) -> None:
@@ -8,6 +8,7 @@ def register_subparser(subparsers: Any) -> None:
8
8
  grn_sub = grn_parser.add_subparsers(dest="grn_cmd")
9
9
  grn_parser.set_defaults(_group_parser=grn_parser)
10
10
 
11
+ build.register(grn_sub)
11
12
  net2json.register(grn_sub)
12
13
  anno2json.register(grn_sub)
13
14
  serve.register(grn_sub)
@@ -0,0 +1,88 @@
1
+ import os
2
+ import pathlib
3
+ import shutil
4
+ import zipfile
5
+ from argparse import Namespace
6
+ from pathlib import Path
7
+ from typing import Any
8
+
9
+ from jsrc.grn.core import ensure_dir, write_text
10
+
11
+ _SCRIPT_TEMPLATE = (pathlib.Path(__file__).parent / "sources" / "script.js").read_text(
12
+ encoding="utf-8"
13
+ )
14
+ _INDEX_HTML = (pathlib.Path(__file__).parent / "sources" / "index.html").read_text(
15
+ encoding="utf-8"
16
+ )
17
+ _STYLE_CSS = (pathlib.Path(__file__).parent / "sources" / "style.css").read_text(
18
+ encoding="utf-8"
19
+ )
20
+
21
+
22
+ def _sync_assets(
23
+ base: str,
24
+ view_mode: str,
25
+ threshold: int,
26
+ max_nodes: int,
27
+ ) -> None:
28
+ ensure_dir(base)
29
+ ensure_dir(os.path.join(base, "css"))
30
+ ensure_dir(os.path.join(base, "js"))
31
+ ensure_dir(os.path.join(base, "json"))
32
+ write_text(os.path.join(base, "index.html"), _INDEX_HTML)
33
+ write_text(os.path.join(base, "css/style.css"), _STYLE_CSS)
34
+ script = (
35
+ _SCRIPT_TEMPLATE.replace("__JSRC_VIEW_MODE__", view_mode)
36
+ .replace("__JSRC_FULL_THRESHOLD__", str(threshold))
37
+ .replace("__JSRC_MAX_DISPLAY_NODES__", str(max_nodes))
38
+ )
39
+ write_text(os.path.join(base, "js/script.js"), script)
40
+
41
+
42
+ def _zip_viewer(viewer_dir: Path, zip_output: str) -> None:
43
+ zip_path = Path(zip_output).expanduser().resolve()
44
+ zip_path.parent.mkdir(parents=True, exist_ok=True)
45
+ wanted = [
46
+ viewer_dir / "index.html",
47
+ viewer_dir / "css" / "style.css",
48
+ viewer_dir / "js" / "script.js",
49
+ viewer_dir / "json" / "grn.json",
50
+ viewer_dir / "json" / "annotation.json",
51
+ ]
52
+ with zipfile.ZipFile(zip_path, "w", compression=zipfile.ZIP_DEFLATED) as zf:
53
+ for f in wanted:
54
+ if f.exists():
55
+ zf.write(f, arcname=str(f.relative_to(viewer_dir)))
56
+
57
+
58
+ def cmd(args: Namespace) -> None:
59
+ root = Path(args.dir).expanduser().resolve()
60
+ view_mode = "expand" if args.expand else "auto"
61
+ _sync_assets(str(root), view_mode, args.threshold, args.max_nodes)
62
+ if args.grn_json:
63
+ shutil.copy(args.grn_json, root / "json" / "grn.json")
64
+ if args.annotation_json:
65
+ shutil.copy(args.annotation_json, root / "json" / "annotation.json")
66
+ if args.zip_output:
67
+ _zip_viewer(root, args.zip_output)
68
+
69
+
70
+ def register(subparsers: Any) -> None:
71
+ p = subparsers.add_parser("build", help="Build GRN viewer package")
72
+ p.add_argument("-d", "--dir", default=".", help="Output directory (default: .)")
73
+ p.add_argument("-g", "--grn-json", help="grn.json to copy into package")
74
+ p.add_argument(
75
+ "-n", "--annotation-json", help="annotation.json to copy into package"
76
+ )
77
+ p.add_argument("-z", "--zip-output", help="ZIP output path")
78
+ mode = p.add_mutually_exclusive_group()
79
+ mode.add_argument("-a", "--all", action="store_true", help="Full view mode")
80
+ mode.add_argument(
81
+ "-e", "--expand", action="store_true", help="Click-to-expand mode"
82
+ )
83
+ p.set_defaults(all=True)
84
+ p.add_argument(
85
+ "-t", "--threshold", type=int, default=300, help="Auto full-view threshold"
86
+ )
87
+ p.add_argument("--max-nodes", type=int, default=0, help="Max nodes (0 = all)")
88
+ p.set_defaults(func=cmd)
@@ -0,0 +1,45 @@
1
+ import csv
2
+ import logging
3
+ from argparse import Namespace
4
+ from typing import Any
5
+
6
+ from jsrc.grn.core import write_json
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+
11
+ def network_to_json(
12
+ input_path: str, output_path: str
13
+ ) -> tuple[list[dict[str, Any]], int]:
14
+ links = []
15
+ with open(input_path, "r", encoding="utf-8") as f:
16
+ reader = csv.reader(f, delimiter="\t")
17
+ for row in reader:
18
+ if len(row) < 3:
19
+ continue
20
+ source_id = str(row[0]).replace("_", "-")
21
+ target_id = str(row[1]).replace("_", "-")
22
+ try:
23
+ weight = float(row[2])
24
+ except ValueError:
25
+ continue
26
+ links.append({"source": source_id, "target": target_id, "val": weight})
27
+ write_json(output_path, links)
28
+ nodes = set()
29
+ for item in links:
30
+ nodes.add(item["source"])
31
+ nodes.add(item["target"])
32
+ logger.info("Network JSON written: %s", output_path)
33
+ logger.info("Genes: %d | Edges: %d", len(nodes), len(links))
34
+ return links, len(nodes)
35
+
36
+
37
+ def cmd(args: Namespace) -> None:
38
+ network_to_json(args.input, args.output)
39
+
40
+
41
+ def register(subparsers: Any) -> None:
42
+ p = subparsers.add_parser("net2json", help="Convert GRN edge table to grn.json")
43
+ p.add_argument("-i", "--input", required=True, help="Input edge table (TSV)")
44
+ p.add_argument("-o", dest="output", required=True, help="Output JSON path")
45
+ p.set_defaults(func=cmd)
@@ -6,17 +6,12 @@ from argparse import Namespace
6
6
  from typing import Any
7
7
 
8
8
  from jsrc.grn.core import ensure_dir, write_json
9
- from jsrc.grn.viewer import sync_viewer_assets
9
+ from jsrc.grn.build import _sync_assets
10
10
 
11
11
 
12
12
  def cmd(args: Namespace) -> None:
13
- view_mode = "expand" if args.some else "auto"
14
- sync_viewer_assets(
15
- args.dir,
16
- init_empty_json=False,
17
- view_mode=view_mode,
18
- full_view_threshold=args.threshold,
19
- )
13
+ view_mode = "expand" if args.expand else "auto"
14
+ _sync_assets(args.dir, view_mode, args.threshold, 0)
20
15
  ensure_dir(f"{args.dir}/json")
21
16
  src_grn = os.path.abspath(args.grn_json)
22
17
  dst_grn = os.path.abspath(f"{args.dir}/json/grn.json")
@@ -58,12 +53,12 @@ def register(subparsers: Any) -> None:
58
53
  help="Mode all: auto full-view when gene count <= threshold",
59
54
  )
60
55
  mode.add_argument(
61
- "-s",
62
- "--some",
56
+ "-e",
57
+ "--expand",
63
58
  action="store_true",
64
- help="Mode some: manual click-to-expand only",
59
+ help="Click-to-expand mode",
65
60
  )
66
- p.set_defaults(all=True, some=False)
61
+ p.set_defaults(all=True, expand=False)
67
62
  p.add_argument(
68
63
  "-t",
69
64
  "--threshold",
@@ -0,0 +1,37 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>GRN</title>
6
+ <link rel="stylesheet" href="css/style.css">
7
+ <script src="//unpkg.com/d3"></script>
8
+ <script src="//unpkg.com/force-graph"></script>
9
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
10
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
11
+ </head>
12
+ <body>
13
+ <div id="controls">
14
+ <div class="row">
15
+ <button onclick="goBack()" id="btnBack" disabled>&lt;</button>
16
+ <button onclick="goForward()" id="btnFwd" disabled>&gt;</button>
17
+ <button onclick="exportImage('pdf')" class="btn-export">PDF</button>
18
+ <button onclick="resetView()" class="btn-reset">Reset</button>
19
+ </div>
20
+
21
+ <div class="row search-row">
22
+ <input type="text" id="geneInput" placeholder="Enter Gene ID">
23
+ <button onclick="startNewSearch()" class="btn-search">Search</button>
24
+ </div>
25
+
26
+ <div id="legend">Line thickness indicates weight</div>
27
+ <div id="nodeCount">Nodes: 0</div>
28
+ <div id="neighborList"></div>
29
+ </div>
30
+
31
+ <div id="watermark">GRN</div>
32
+ <div id="emptyState">No Nodes</div>
33
+ <div id="graph"></div>
34
+
35
+ <script src="js/script.js"></script>
36
+ </body>
37
+ </html>