svc-processing 0.1.1__tar.gz → 0.1.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 (26) hide show
  1. {svc_processing-0.1.1 → svc_processing-0.1.2}/PKG-INFO +15 -1
  2. {svc_processing-0.1.1 → svc_processing-0.1.2}/README.md +14 -0
  3. {svc_processing-0.1.1 → svc_processing-0.1.2}/pipeline/cli.py +12 -0
  4. {svc_processing-0.1.1 → svc_processing-0.1.2}/pipeline/run_config.py +37 -0
  5. {svc_processing-0.1.1 → svc_processing-0.1.2}/pyproject.toml +1 -1
  6. {svc_processing-0.1.1 → svc_processing-0.1.2}/svc_processing.egg-info/PKG-INFO +15 -1
  7. {svc_processing-0.1.1 → svc_processing-0.1.2}/tests/test_cli.py +30 -0
  8. {svc_processing-0.1.1 → svc_processing-0.1.2}/tests/test_run_config.py +21 -0
  9. {svc_processing-0.1.1 → svc_processing-0.1.2}/LICENSE +0 -0
  10. {svc_processing-0.1.1 → svc_processing-0.1.2}/pipeline/__init__.py +0 -0
  11. {svc_processing-0.1.1 → svc_processing-0.1.2}/pipeline/processor.py +0 -0
  12. {svc_processing-0.1.1 → svc_processing-0.1.2}/pipeline/resampler.py +0 -0
  13. {svc_processing-0.1.1 → svc_processing-0.1.2}/pipeline/runner.py +0 -0
  14. {svc_processing-0.1.1 → svc_processing-0.1.2}/pipeline/sig_processor.py +0 -0
  15. {svc_processing-0.1.1 → svc_processing-0.1.2}/setup.cfg +0 -0
  16. {svc_processing-0.1.1 → svc_processing-0.1.2}/svc_processing.egg-info/SOURCES.txt +0 -0
  17. {svc_processing-0.1.1 → svc_processing-0.1.2}/svc_processing.egg-info/dependency_links.txt +0 -0
  18. {svc_processing-0.1.1 → svc_processing-0.1.2}/svc_processing.egg-info/entry_points.txt +0 -0
  19. {svc_processing-0.1.1 → svc_processing-0.1.2}/svc_processing.egg-info/requires.txt +0 -0
  20. {svc_processing-0.1.1 → svc_processing-0.1.2}/svc_processing.egg-info/top_level.txt +0 -0
  21. {svc_processing-0.1.1 → svc_processing-0.1.2}/tests/test_pipeline_demo.py +0 -0
  22. {svc_processing-0.1.1 → svc_processing-0.1.2}/tests/test_processor.py +0 -0
  23. {svc_processing-0.1.1 → svc_processing-0.1.2}/tests/test_resampler_parity.py +0 -0
  24. {svc_processing-0.1.1 → svc_processing-0.1.2}/tests/test_resampler_public_api.py +0 -0
  25. {svc_processing-0.1.1 → svc_processing-0.1.2}/tests/test_runner.py +0 -0
  26. {svc_processing-0.1.1 → svc_processing-0.1.2}/tests/test_sig_processor.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: svc-processing
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: Pure-Python SVC HR-1024i hyperspectral .sig processing pipeline
5
5
  Author: Gold Lab (GrapeSPEC project), Cornell University
6
6
  Author-email: Cole Regnier <nr466@cornell.edu>
@@ -65,6 +65,20 @@ Generated outputs (gitignored): `pipeline_outputs/sig_processed/<run>/`, `pipeli
65
65
  > New to the pipeline? [`TUTORIAL.md`](TUTORIAL.md) starts with the interactive
66
66
  > Jupyter notebook (the gentle path) and has an advanced track for the CLI below.
67
67
 
68
+ **Installed from PyPI, in a new project folder** (no clone of this repo needed):
69
+
70
+ ```bash
71
+ python3.11 -m venv .venv
72
+ source .venv/bin/activate
73
+ python -m pip install svc-processing
74
+
75
+ svc-pipeline --init-config # writes a starter config/config.json here
76
+ # Edit config/config.json — replace "<PATH_TO_SIG_INPUT_ROOT>" with your data path.
77
+ svc-pipeline
78
+ ```
79
+
80
+ **Working from a clone of this repo** (config/config.json already exists):
81
+
68
82
  ```bash
69
83
  python3.11 -m venv .venv
70
84
  source .venv/bin/activate
@@ -32,6 +32,20 @@ Generated outputs (gitignored): `pipeline_outputs/sig_processed/<run>/`, `pipeli
32
32
  > New to the pipeline? [`TUTORIAL.md`](TUTORIAL.md) starts with the interactive
33
33
  > Jupyter notebook (the gentle path) and has an advanced track for the CLI below.
34
34
 
35
+ **Installed from PyPI, in a new project folder** (no clone of this repo needed):
36
+
37
+ ```bash
38
+ python3.11 -m venv .venv
39
+ source .venv/bin/activate
40
+ python -m pip install svc-processing
41
+
42
+ svc-pipeline --init-config # writes a starter config/config.json here
43
+ # Edit config/config.json — replace "<PATH_TO_SIG_INPUT_ROOT>" with your data path.
44
+ svc-pipeline
45
+ ```
46
+
47
+ **Working from a clone of this repo** (config/config.json already exists):
48
+
35
49
  ```bash
36
50
  python3.11 -m venv .venv
37
51
  source .venv/bin/activate
@@ -50,6 +50,11 @@ def _parse_args() -> argparse.Namespace:
50
50
  action="store_true",
51
51
  help="Enable verbose logging before and after each processing step",
52
52
  )
53
+ parser.add_argument(
54
+ "--init-config",
55
+ action="store_true",
56
+ help="Write a starter config/config.json in the current directory and exit.",
57
+ )
53
58
  return parser.parse_args()
54
59
 
55
60
 
@@ -61,6 +66,13 @@ def main() -> None:
61
66
  # working directory so the installed `svc-pipeline` command works from any
62
67
  # directory. Absolute paths in the config are always honoured as-is.
63
68
  base_dir = Path.cwd()
69
+
70
+ if args.init_config:
71
+ target = RunConfig.write_starter_config(base_dir)
72
+ print(f"Wrote starter config: {target.resolve()}")
73
+ print('Edit "sig_input_dir" to point at your .sig data, then run: svc-pipeline')
74
+ return
75
+
64
76
  run_config = RunConfig.load(base_dir, str(args.config), logger)
65
77
  run_config.ensure_no_placeholder(args.input_dir)
66
78
 
@@ -34,6 +34,24 @@ _PARITY_DEFAULTS: dict[str, Any] = {
34
34
 
35
35
  _PLACEHOLDER = "<PATH_TO_SIG_INPUT_ROOT>"
36
36
 
37
+ # Starter template written by `svc-pipeline --init-config`. Mirrors the
38
+ # repo's own config/config.json — keep the two in sync if either changes.
39
+ _STARTER_CONFIG_TEMPLATE: dict[str, Any] = {
40
+ "sig_input_dir": _PLACEHOLDER,
41
+ "process_all_subdirs": True,
42
+ "processed_dir": "sig_processed",
43
+ "resampled_dir": "sig_resampled",
44
+ "output_dir": "pipeline_outputs",
45
+ "summary_csv_name": "processed_sig_summary.csv",
46
+ "merged_csv_name": "merged_spectra.csv",
47
+ "end_line_overrides": {},
48
+ "instrument": {
49
+ "bronze": {"end_line": "2520.4", "serial": "2212118"},
50
+ "silver": {"end_line": "2517.9", "serial": "1202103"},
51
+ },
52
+ "processing": dict(_PARITY_DEFAULTS),
53
+ }
54
+
37
55
 
38
56
  def _resolve_under(base: Path, value: str) -> Path:
39
57
  """Resolve ``value`` against ``base`` unless it is already absolute."""
@@ -87,6 +105,25 @@ class RunConfig:
87
105
  logger.info("Using config: %s", path)
88
106
  return cls(data, path=path, base_dir=base_dir, logger=logger)
89
107
 
108
+ @classmethod
109
+ def write_starter_config(cls, base_dir: Path, *, filename: str = "config.json") -> Path:
110
+ """Write a starter ``config/<filename>`` under ``base_dir`` and return its path.
111
+
112
+ Refuses to overwrite an existing file, so re-running this never clobbers
113
+ edits you've already made.
114
+ """
115
+ target = base_dir / "config" / filename
116
+ if target.exists():
117
+ raise SystemExit(
118
+ f"{target} already exists — not overwriting.\n"
119
+ f" Edit it directly, or remove it first to regenerate the starter template."
120
+ )
121
+ target.parent.mkdir(parents=True, exist_ok=True)
122
+ with target.open("w") as handle:
123
+ json.dump(_STARTER_CONFIG_TEMPLATE, handle, indent=2)
124
+ handle.write("\n")
125
+ return target
126
+
90
127
  @staticmethod
91
128
  def _resolve_path(base_dir: Path, value: str) -> Path:
92
129
  """Resolve a run-config argument with friendly fallbacks.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "svc-processing"
7
- version = "0.1.1"
7
+ version = "0.1.2"
8
8
  description = "Pure-Python SVC HR-1024i hyperspectral .sig processing pipeline"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: svc-processing
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: Pure-Python SVC HR-1024i hyperspectral .sig processing pipeline
5
5
  Author: Gold Lab (GrapeSPEC project), Cornell University
6
6
  Author-email: Cole Regnier <nr466@cornell.edu>
@@ -65,6 +65,20 @@ Generated outputs (gitignored): `pipeline_outputs/sig_processed/<run>/`, `pipeli
65
65
  > New to the pipeline? [`TUTORIAL.md`](TUTORIAL.md) starts with the interactive
66
66
  > Jupyter notebook (the gentle path) and has an advanced track for the CLI below.
67
67
 
68
+ **Installed from PyPI, in a new project folder** (no clone of this repo needed):
69
+
70
+ ```bash
71
+ python3.11 -m venv .venv
72
+ source .venv/bin/activate
73
+ python -m pip install svc-processing
74
+
75
+ svc-pipeline --init-config # writes a starter config/config.json here
76
+ # Edit config/config.json — replace "<PATH_TO_SIG_INPUT_ROOT>" with your data path.
77
+ svc-pipeline
78
+ ```
79
+
80
+ **Working from a clone of this repo** (config/config.json already exists):
81
+
68
82
  ```bash
69
83
  python3.11 -m venv .venv
70
84
  source .venv/bin/activate
@@ -73,6 +73,36 @@ def test_main_input_dir_override_bypasses_config_input_and_placeholder(
73
73
  assert "not produced" in captured.out
74
74
 
75
75
 
76
+ def test_main_init_config_writes_starter_and_exits(
77
+ tmp_path: Path,
78
+ monkeypatch: pytest.MonkeyPatch,
79
+ capsys: pytest.CaptureFixture[str],
80
+ ) -> None:
81
+ monkeypatch.chdir(tmp_path)
82
+ monkeypatch.setattr(sys, "argv", ["svc-pipeline", "--init-config"])
83
+
84
+ main()
85
+
86
+ written = tmp_path / "config" / "config.json"
87
+ assert written.is_file()
88
+ assert json.loads(written.read_text())["sig_input_dir"] == "<PATH_TO_SIG_INPUT_ROOT>"
89
+ assert str(written.resolve()) in capsys.readouterr().out
90
+
91
+
92
+ def test_main_init_config_refuses_to_overwrite_existing(
93
+ tmp_path: Path,
94
+ monkeypatch: pytest.MonkeyPatch,
95
+ ) -> None:
96
+ monkeypatch.chdir(tmp_path)
97
+ existing = tmp_path / "config" / "config.json"
98
+ existing.parent.mkdir()
99
+ existing.write_text('{"already": "here"}')
100
+ monkeypatch.setattr(sys, "argv", ["svc-pipeline", "--init-config"])
101
+
102
+ with pytest.raises(SystemExit, match="already exists"):
103
+ main()
104
+
105
+
76
106
  def test_main_exits_on_placeholder_without_input_dir_override(
77
107
  tmp_path: Path,
78
108
  monkeypatch: pytest.MonkeyPatch,
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import json
3
4
  import logging
4
5
  from pathlib import Path
5
6
 
@@ -21,6 +22,26 @@ def test_resolve_path_finds_bare_config_name(tmp_path: Path) -> None:
21
22
  assert RunConfig._resolve_path(tmp_path, "demo") == config_path
22
23
 
23
24
 
25
+ def test_write_starter_config_creates_config_json_with_placeholder(tmp_path: Path) -> None:
26
+ target = RunConfig.write_starter_config(tmp_path)
27
+
28
+ assert target == tmp_path / "config" / "config.json"
29
+ data = json.loads(target.read_text())
30
+ assert data["sig_input_dir"] == "<PATH_TO_SIG_INPUT_ROOT>"
31
+ assert data["processing"]["band_min"] == 400
32
+
33
+
34
+ def test_write_starter_config_refuses_to_overwrite_existing(tmp_path: Path) -> None:
35
+ existing = tmp_path / "config" / "config.json"
36
+ existing.parent.mkdir()
37
+ existing.write_text('{"already": "here"}')
38
+
39
+ with pytest.raises(SystemExit, match="already exists"):
40
+ RunConfig.write_starter_config(tmp_path)
41
+
42
+ assert json.loads(existing.read_text()) == {"already": "here"}
43
+
44
+
24
45
  def test_placeholder_guard_exits_with_guidance(tmp_path: Path) -> None:
25
46
  config_path = tmp_path / "config.json"
26
47
  config = RunConfig(
File without changes
File without changes