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.
- {svc_processing-0.1.1 → svc_processing-0.1.2}/PKG-INFO +15 -1
- {svc_processing-0.1.1 → svc_processing-0.1.2}/README.md +14 -0
- {svc_processing-0.1.1 → svc_processing-0.1.2}/pipeline/cli.py +12 -0
- {svc_processing-0.1.1 → svc_processing-0.1.2}/pipeline/run_config.py +37 -0
- {svc_processing-0.1.1 → svc_processing-0.1.2}/pyproject.toml +1 -1
- {svc_processing-0.1.1 → svc_processing-0.1.2}/svc_processing.egg-info/PKG-INFO +15 -1
- {svc_processing-0.1.1 → svc_processing-0.1.2}/tests/test_cli.py +30 -0
- {svc_processing-0.1.1 → svc_processing-0.1.2}/tests/test_run_config.py +21 -0
- {svc_processing-0.1.1 → svc_processing-0.1.2}/LICENSE +0 -0
- {svc_processing-0.1.1 → svc_processing-0.1.2}/pipeline/__init__.py +0 -0
- {svc_processing-0.1.1 → svc_processing-0.1.2}/pipeline/processor.py +0 -0
- {svc_processing-0.1.1 → svc_processing-0.1.2}/pipeline/resampler.py +0 -0
- {svc_processing-0.1.1 → svc_processing-0.1.2}/pipeline/runner.py +0 -0
- {svc_processing-0.1.1 → svc_processing-0.1.2}/pipeline/sig_processor.py +0 -0
- {svc_processing-0.1.1 → svc_processing-0.1.2}/setup.cfg +0 -0
- {svc_processing-0.1.1 → svc_processing-0.1.2}/svc_processing.egg-info/SOURCES.txt +0 -0
- {svc_processing-0.1.1 → svc_processing-0.1.2}/svc_processing.egg-info/dependency_links.txt +0 -0
- {svc_processing-0.1.1 → svc_processing-0.1.2}/svc_processing.egg-info/entry_points.txt +0 -0
- {svc_processing-0.1.1 → svc_processing-0.1.2}/svc_processing.egg-info/requires.txt +0 -0
- {svc_processing-0.1.1 → svc_processing-0.1.2}/svc_processing.egg-info/top_level.txt +0 -0
- {svc_processing-0.1.1 → svc_processing-0.1.2}/tests/test_pipeline_demo.py +0 -0
- {svc_processing-0.1.1 → svc_processing-0.1.2}/tests/test_processor.py +0 -0
- {svc_processing-0.1.1 → svc_processing-0.1.2}/tests/test_resampler_parity.py +0 -0
- {svc_processing-0.1.1 → svc_processing-0.1.2}/tests/test_resampler_public_api.py +0 -0
- {svc_processing-0.1.1 → svc_processing-0.1.2}/tests/test_runner.py +0 -0
- {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.
|
|
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.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: svc-processing
|
|
3
|
-
Version: 0.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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|