brkraw 0.3.11__py3-none-any.whl → 0.5.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.
- brkraw/__init__.py +9 -3
- brkraw/apps/__init__.py +12 -0
- brkraw/apps/addon/__init__.py +30 -0
- brkraw/apps/addon/core.py +35 -0
- brkraw/apps/addon/dependencies.py +402 -0
- brkraw/apps/addon/installation.py +500 -0
- brkraw/apps/addon/io.py +21 -0
- brkraw/apps/hook/__init__.py +25 -0
- brkraw/apps/hook/core.py +636 -0
- brkraw/apps/loader/__init__.py +10 -0
- brkraw/apps/loader/core.py +622 -0
- brkraw/apps/loader/formatter.py +288 -0
- brkraw/apps/loader/helper.py +797 -0
- brkraw/apps/loader/info/__init__.py +11 -0
- brkraw/apps/loader/info/scan.py +85 -0
- brkraw/apps/loader/info/scan.yaml +90 -0
- brkraw/apps/loader/info/study.py +69 -0
- brkraw/apps/loader/info/study.yaml +156 -0
- brkraw/apps/loader/info/transform.py +92 -0
- brkraw/apps/loader/types.py +220 -0
- brkraw/cli/__init__.py +5 -0
- brkraw/cli/commands/__init__.py +2 -0
- brkraw/cli/commands/addon.py +327 -0
- brkraw/cli/commands/config.py +205 -0
- brkraw/cli/commands/convert.py +903 -0
- brkraw/cli/commands/hook.py +348 -0
- brkraw/cli/commands/info.py +74 -0
- brkraw/cli/commands/init.py +214 -0
- brkraw/cli/commands/params.py +106 -0
- brkraw/cli/commands/prune.py +288 -0
- brkraw/cli/commands/session.py +371 -0
- brkraw/cli/hook_args.py +80 -0
- brkraw/cli/main.py +83 -0
- brkraw/cli/utils.py +60 -0
- brkraw/core/__init__.py +13 -0
- brkraw/core/config.py +380 -0
- brkraw/core/entrypoints.py +25 -0
- brkraw/core/formatter.py +367 -0
- brkraw/core/fs.py +495 -0
- brkraw/core/jcamp.py +600 -0
- brkraw/core/layout.py +451 -0
- brkraw/core/parameters.py +781 -0
- brkraw/core/zip.py +1121 -0
- brkraw/dataclasses/__init__.py +14 -0
- brkraw/dataclasses/node.py +139 -0
- brkraw/dataclasses/reco.py +33 -0
- brkraw/dataclasses/scan.py +61 -0
- brkraw/dataclasses/study.py +131 -0
- brkraw/default/__init__.py +3 -0
- brkraw/default/pruner_specs/deid4share.yaml +42 -0
- brkraw/default/rules/00_default.yaml +4 -0
- brkraw/default/specs/metadata_dicom.yaml +236 -0
- brkraw/default/specs/metadata_transforms.py +92 -0
- brkraw/resolver/__init__.py +7 -0
- brkraw/resolver/affine.py +539 -0
- brkraw/resolver/datatype.py +69 -0
- brkraw/resolver/fid.py +90 -0
- brkraw/resolver/helpers.py +36 -0
- brkraw/resolver/image.py +188 -0
- brkraw/resolver/nifti.py +370 -0
- brkraw/resolver/shape.py +235 -0
- brkraw/schema/__init__.py +3 -0
- brkraw/schema/context_map.yaml +62 -0
- brkraw/schema/meta.yaml +57 -0
- brkraw/schema/niftiheader.yaml +95 -0
- brkraw/schema/pruner.yaml +55 -0
- brkraw/schema/remapper.yaml +128 -0
- brkraw/schema/rules.yaml +154 -0
- brkraw/specs/__init__.py +10 -0
- brkraw/specs/hook/__init__.py +12 -0
- brkraw/specs/hook/logic.py +31 -0
- brkraw/specs/hook/validator.py +22 -0
- brkraw/specs/meta/__init__.py +5 -0
- brkraw/specs/meta/validator.py +156 -0
- brkraw/specs/pruner/__init__.py +15 -0
- brkraw/specs/pruner/logic.py +361 -0
- brkraw/specs/pruner/validator.py +119 -0
- brkraw/specs/remapper/__init__.py +27 -0
- brkraw/specs/remapper/logic.py +924 -0
- brkraw/specs/remapper/validator.py +314 -0
- brkraw/specs/rules/__init__.py +6 -0
- brkraw/specs/rules/logic.py +263 -0
- brkraw/specs/rules/validator.py +103 -0
- brkraw-0.5.0.dist-info/METADATA +81 -0
- brkraw-0.5.0.dist-info/RECORD +88 -0
- {brkraw-0.3.11.dist-info → brkraw-0.5.0.dist-info}/WHEEL +1 -2
- brkraw-0.5.0.dist-info/entry_points.txt +13 -0
- brkraw/lib/__init__.py +0 -4
- brkraw/lib/backup.py +0 -641
- brkraw/lib/bids.py +0 -0
- brkraw/lib/errors.py +0 -125
- brkraw/lib/loader.py +0 -1220
- brkraw/lib/orient.py +0 -194
- brkraw/lib/parser.py +0 -48
- brkraw/lib/pvobj.py +0 -301
- brkraw/lib/reference.py +0 -245
- brkraw/lib/utils.py +0 -471
- brkraw/scripts/__init__.py +0 -0
- brkraw/scripts/brk_backup.py +0 -106
- brkraw/scripts/brkraw.py +0 -744
- brkraw/ui/__init__.py +0 -0
- brkraw/ui/config.py +0 -17
- brkraw/ui/main_win.py +0 -214
- brkraw/ui/previewer.py +0 -225
- brkraw/ui/scan_info.py +0 -72
- brkraw/ui/scan_list.py +0 -73
- brkraw/ui/subj_info.py +0 -128
- brkraw-0.3.11.dist-info/METADATA +0 -25
- brkraw-0.3.11.dist-info/RECORD +0 -28
- brkraw-0.3.11.dist-info/entry_points.txt +0 -3
- brkraw-0.3.11.dist-info/top_level.txt +0 -2
- tests/__init__.py +0 -0
- {brkraw-0.3.11.dist-info → brkraw-0.5.0.dist-info/licenses}/LICENSE +0 -0
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import dataclasses
|
|
4
|
+
import importlib
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
import inspect
|
|
7
|
+
from typing import Any, Dict, Mapping
|
|
8
|
+
|
|
9
|
+
import argparse
|
|
10
|
+
import logging
|
|
11
|
+
|
|
12
|
+
from brkraw.apps import hook as hook_app
|
|
13
|
+
from brkraw.core import config as config_core
|
|
14
|
+
from brkraw.core import formatter
|
|
15
|
+
from brkraw.specs import hook as converter_core
|
|
16
|
+
import yaml
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger("brkraw")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def cmd_hook(args: argparse.Namespace) -> int:
|
|
22
|
+
handler = getattr(args, "hook_func", None)
|
|
23
|
+
if handler is None:
|
|
24
|
+
args.parser.print_help()
|
|
25
|
+
return 2
|
|
26
|
+
return handler(args)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _normalize_row(row: Dict[str, str]) -> Dict[str, object]:
|
|
30
|
+
name = row.get("name", "")
|
|
31
|
+
version = row.get("version", "")
|
|
32
|
+
entrypoints = row.get("entrypoints", "")
|
|
33
|
+
description = row.get("description", "")
|
|
34
|
+
install_status = row.get("install_status", "")
|
|
35
|
+
name_cell: object = name
|
|
36
|
+
version_cell: object = version
|
|
37
|
+
entrypoints_cell: object = entrypoints
|
|
38
|
+
description_cell: object = description
|
|
39
|
+
install_cell: object = install_status
|
|
40
|
+
if row.get("name_unknown") == "1":
|
|
41
|
+
name_cell = {"value": name, "color": "gray"}
|
|
42
|
+
if row.get("version_unknown") == "1":
|
|
43
|
+
version_cell = {"value": version, "color": "gray"}
|
|
44
|
+
if row.get("entrypoints_unknown") == "1":
|
|
45
|
+
entrypoints_cell = {"value": entrypoints, "color": "gray"}
|
|
46
|
+
if row.get("description_unknown") == "1":
|
|
47
|
+
description_cell = {"value": description, "color": "gray"}
|
|
48
|
+
if row.get("install_status_color"):
|
|
49
|
+
install_cell = {"value": install_status, "color": row["install_status_color"]}
|
|
50
|
+
return {
|
|
51
|
+
"name": name_cell,
|
|
52
|
+
"version": version_cell,
|
|
53
|
+
"entrypoints": entrypoints_cell,
|
|
54
|
+
"description": description_cell,
|
|
55
|
+
"installed": install_cell,
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def cmd_list(args: argparse.Namespace) -> int:
|
|
60
|
+
hooks = hook_app.list_hooks(root=args.root)
|
|
61
|
+
width = config_core.output_width(root=args.root)
|
|
62
|
+
rows = []
|
|
63
|
+
for hook in hooks:
|
|
64
|
+
install_status = hook.get("install_status", "No")
|
|
65
|
+
if install_status == "Yes":
|
|
66
|
+
status_color = "green"
|
|
67
|
+
elif install_status == "Partially":
|
|
68
|
+
status_color = "yellow"
|
|
69
|
+
else:
|
|
70
|
+
status_color = "red"
|
|
71
|
+
rows.append(
|
|
72
|
+
_normalize_row(
|
|
73
|
+
{
|
|
74
|
+
"name": hook.get("name", "<Unknown>"),
|
|
75
|
+
"version": hook.get("version", "<Unknown>"),
|
|
76
|
+
"entrypoints": ", ".join(hook.get("entrypoints") or []) or "<Unknown>",
|
|
77
|
+
"description": hook.get("description", "<Unknown>"),
|
|
78
|
+
"install_status": install_status,
|
|
79
|
+
"install_status_color": status_color,
|
|
80
|
+
"name_unknown": "1" if hook.get("name") in (None, "<Unknown>") else "0",
|
|
81
|
+
"version_unknown": "1" if hook.get("version") in (None, "<Unknown>") else "0",
|
|
82
|
+
"entrypoints_unknown": "1"
|
|
83
|
+
if not hook.get("entrypoints")
|
|
84
|
+
else "0",
|
|
85
|
+
"description_unknown": "1"
|
|
86
|
+
if hook.get("description") in (None, "<Unknown>")
|
|
87
|
+
else "0",
|
|
88
|
+
}
|
|
89
|
+
)
|
|
90
|
+
)
|
|
91
|
+
columns = ("name", "version", "installed", "entrypoints", "description")
|
|
92
|
+
table = formatter.format_table(
|
|
93
|
+
"Hooks",
|
|
94
|
+
columns,
|
|
95
|
+
rows,
|
|
96
|
+
width=width,
|
|
97
|
+
colors={"name": "cyan", "description": "gray", "entrypoints": "yellow"},
|
|
98
|
+
title_color="cyan",
|
|
99
|
+
col_widths=formatter.compute_column_widths(columns, rows),
|
|
100
|
+
min_last_col_width=40,
|
|
101
|
+
)
|
|
102
|
+
logger.info("%s", table)
|
|
103
|
+
return 0
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def cmd_install(args: argparse.Namespace) -> int:
|
|
107
|
+
if args.target == "all":
|
|
108
|
+
result = hook_app.install_all(root=args.root, upgrade=args.upgrade, force=args.force)
|
|
109
|
+
logger.info("Installed %d hook(s).", len(result["installed"]))
|
|
110
|
+
if result["skipped"]:
|
|
111
|
+
logger.info("Skipped %d hook(s).", len(result["skipped"]))
|
|
112
|
+
return 0
|
|
113
|
+
status = hook_app.install_hook(
|
|
114
|
+
args.target,
|
|
115
|
+
root=args.root,
|
|
116
|
+
upgrade=args.upgrade,
|
|
117
|
+
force=args.force,
|
|
118
|
+
)
|
|
119
|
+
if status == "installed":
|
|
120
|
+
logger.info("Installed hook: %s", args.target)
|
|
121
|
+
else:
|
|
122
|
+
logger.info("Hook already installed: %s", args.target)
|
|
123
|
+
return 0
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def cmd_uninstall(args: argparse.Namespace) -> int:
|
|
127
|
+
try:
|
|
128
|
+
hook_name, removed, module_missing = hook_app.uninstall_hook(
|
|
129
|
+
args.target,
|
|
130
|
+
root=args.root,
|
|
131
|
+
force=args.force,
|
|
132
|
+
)
|
|
133
|
+
except LookupError as exc:
|
|
134
|
+
logger.error("%s", exc)
|
|
135
|
+
return 2
|
|
136
|
+
except RuntimeError as exc:
|
|
137
|
+
logger.error("%s", exc)
|
|
138
|
+
return 2
|
|
139
|
+
removed_count = sum(len(items) for items in removed.values())
|
|
140
|
+
logger.info("Removed %d file(s).", removed_count)
|
|
141
|
+
if module_missing:
|
|
142
|
+
logger.info(
|
|
143
|
+
"Hook module is not installed; removed registry entries only."
|
|
144
|
+
)
|
|
145
|
+
logger.info("To uninstall the package, run: pip uninstall %s", hook_name)
|
|
146
|
+
return 0
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def cmd_docs(args: argparse.Namespace) -> int:
|
|
150
|
+
try:
|
|
151
|
+
hook_name, text = hook_app.read_hook_docs(args.target, root=args.root)
|
|
152
|
+
except (LookupError, FileNotFoundError) as exc:
|
|
153
|
+
logger.error("%s", exc)
|
|
154
|
+
return 2
|
|
155
|
+
logger.info("[Hook Docs] %s", hook_name)
|
|
156
|
+
if args.render:
|
|
157
|
+
try:
|
|
158
|
+
import importlib
|
|
159
|
+
|
|
160
|
+
console_mod = importlib.import_module("rich.console")
|
|
161
|
+
markdown_mod = importlib.import_module("rich.markdown")
|
|
162
|
+
Console = getattr(console_mod, "Console")
|
|
163
|
+
Markdown = getattr(markdown_mod, "Markdown")
|
|
164
|
+
except Exception:
|
|
165
|
+
logger.warning("rich is not available; printing raw text.")
|
|
166
|
+
print(text)
|
|
167
|
+
return 0
|
|
168
|
+
console = Console()
|
|
169
|
+
console.print(Markdown(text))
|
|
170
|
+
return 0
|
|
171
|
+
print(text)
|
|
172
|
+
return 0
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
_PRESET_IGNORE_PARAMS = frozenset(
|
|
176
|
+
{
|
|
177
|
+
"self",
|
|
178
|
+
"scan",
|
|
179
|
+
"scan_id",
|
|
180
|
+
"reco_id",
|
|
181
|
+
"format",
|
|
182
|
+
"space",
|
|
183
|
+
"override_header",
|
|
184
|
+
"override_subject_type",
|
|
185
|
+
"override_subject_pose",
|
|
186
|
+
"flip_x",
|
|
187
|
+
"xyz_units",
|
|
188
|
+
"t_units",
|
|
189
|
+
"decimals",
|
|
190
|
+
"spec",
|
|
191
|
+
"context_map",
|
|
192
|
+
"return_spec",
|
|
193
|
+
"hook_args_by_name",
|
|
194
|
+
}
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def _infer_hook_preset(entry: Mapping[str, Any]) -> Dict[str, Any]:
|
|
199
|
+
preset: Dict[str, Any] = {}
|
|
200
|
+
modules: list[object] = []
|
|
201
|
+
|
|
202
|
+
for func in entry.values():
|
|
203
|
+
if callable(func):
|
|
204
|
+
mod_name = getattr(func, "__module__", None)
|
|
205
|
+
if isinstance(mod_name, str) and mod_name:
|
|
206
|
+
try:
|
|
207
|
+
modules.append(importlib.import_module(mod_name))
|
|
208
|
+
except Exception:
|
|
209
|
+
pass
|
|
210
|
+
|
|
211
|
+
for module in modules:
|
|
212
|
+
module_preset = _infer_hook_preset_from_module(module)
|
|
213
|
+
if module_preset:
|
|
214
|
+
return dict(sorted(module_preset.items(), key=lambda item: item[0]))
|
|
215
|
+
|
|
216
|
+
for func in entry.values():
|
|
217
|
+
if not callable(func):
|
|
218
|
+
continue
|
|
219
|
+
try:
|
|
220
|
+
sig = inspect.signature(func)
|
|
221
|
+
except (TypeError, ValueError):
|
|
222
|
+
continue
|
|
223
|
+
for param in sig.parameters.values():
|
|
224
|
+
if param.kind in (inspect.Parameter.VAR_POSITIONAL, inspect.Parameter.VAR_KEYWORD):
|
|
225
|
+
continue
|
|
226
|
+
name = param.name
|
|
227
|
+
if name in _PRESET_IGNORE_PARAMS:
|
|
228
|
+
continue
|
|
229
|
+
if name in preset:
|
|
230
|
+
continue
|
|
231
|
+
if param.default is inspect.Parameter.empty:
|
|
232
|
+
preset[name] = None
|
|
233
|
+
else:
|
|
234
|
+
preset[name] = param.default
|
|
235
|
+
return dict(sorted(preset.items(), key=lambda item: item[0]))
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def _infer_hook_preset_from_module(module: object) -> Dict[str, Any]:
|
|
239
|
+
for attr in ("HOOK_PRESET", "HOOK_ARGS", "HOOK_DEFAULTS"):
|
|
240
|
+
value = getattr(module, attr, None)
|
|
241
|
+
if isinstance(value, Mapping):
|
|
242
|
+
return dict(value)
|
|
243
|
+
|
|
244
|
+
build_options = getattr(module, "_build_options", None)
|
|
245
|
+
if callable(build_options):
|
|
246
|
+
try:
|
|
247
|
+
options = build_options({})
|
|
248
|
+
except Exception:
|
|
249
|
+
return {}
|
|
250
|
+
if dataclasses.is_dataclass(options):
|
|
251
|
+
if not isinstance(options, type):
|
|
252
|
+
return dict(dataclasses.asdict(options))
|
|
253
|
+
defaults: Dict[str, Any] = {}
|
|
254
|
+
for field in dataclasses.fields(options):
|
|
255
|
+
if field.default is not dataclasses.MISSING:
|
|
256
|
+
defaults[field.name] = field.default
|
|
257
|
+
continue
|
|
258
|
+
if field.default_factory is not dataclasses.MISSING: # type: ignore[comparison-overlap]
|
|
259
|
+
try:
|
|
260
|
+
defaults[field.name] = field.default_factory() # type: ignore[misc]
|
|
261
|
+
except Exception:
|
|
262
|
+
defaults[field.name] = None
|
|
263
|
+
continue
|
|
264
|
+
defaults[field.name] = None
|
|
265
|
+
return defaults
|
|
266
|
+
if hasattr(options, "__dict__"):
|
|
267
|
+
return dict(vars(options))
|
|
268
|
+
return {}
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def cmd_preset(args: argparse.Namespace) -> int:
|
|
272
|
+
try:
|
|
273
|
+
entry = converter_core.resolve_hook(args.target)
|
|
274
|
+
except LookupError as exc:
|
|
275
|
+
logger.error("%s", exc)
|
|
276
|
+
return 2
|
|
277
|
+
preset = _infer_hook_preset(entry)
|
|
278
|
+
payload = {"hooks": {args.target: preset}}
|
|
279
|
+
text = yaml.safe_dump(payload, sort_keys=False)
|
|
280
|
+
if args.output:
|
|
281
|
+
Path(args.output).expanduser().write_text(text, encoding="utf-8")
|
|
282
|
+
logger.info("Wrote preset: %s", args.output)
|
|
283
|
+
return 0
|
|
284
|
+
print(text, end="" if text.endswith("\n") else "\n")
|
|
285
|
+
return 0
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def register(subparsers: argparse._SubParsersAction) -> None: # type: ignore[name-defined]
|
|
289
|
+
hook_parser = subparsers.add_parser(
|
|
290
|
+
"hook",
|
|
291
|
+
help="Manage converter hook packages.",
|
|
292
|
+
)
|
|
293
|
+
hook_parser.add_argument(
|
|
294
|
+
"--root",
|
|
295
|
+
help="Override config root directory (default: BRKRAW_CONFIG_HOME or ~/.brkraw).",
|
|
296
|
+
)
|
|
297
|
+
hook_parser.set_defaults(func=cmd_hook, parser=hook_parser)
|
|
298
|
+
hook_sub = hook_parser.add_subparsers(dest="hook_command")
|
|
299
|
+
|
|
300
|
+
list_parser = hook_sub.add_parser("list", help="List installed hook packages.")
|
|
301
|
+
list_parser.set_defaults(hook_func=cmd_list)
|
|
302
|
+
|
|
303
|
+
install_parser = hook_sub.add_parser("install", help="Install hook addons.")
|
|
304
|
+
install_parser.add_argument("target", help="Hook name or entrypoint name, or 'all'.")
|
|
305
|
+
install_parser.add_argument(
|
|
306
|
+
"--upgrade",
|
|
307
|
+
action="store_true",
|
|
308
|
+
help="Reinstall when a newer version is available.",
|
|
309
|
+
)
|
|
310
|
+
install_parser.add_argument(
|
|
311
|
+
"--force",
|
|
312
|
+
action="store_true",
|
|
313
|
+
help="Reinstall even if the same or older version is installed.",
|
|
314
|
+
)
|
|
315
|
+
install_parser.set_defaults(hook_func=cmd_install)
|
|
316
|
+
|
|
317
|
+
uninstall_parser = hook_sub.add_parser("uninstall", help="Remove hook addons.")
|
|
318
|
+
uninstall_parser.add_argument("target", help="Hook name or entrypoint name.")
|
|
319
|
+
uninstall_parser.add_argument(
|
|
320
|
+
"--force",
|
|
321
|
+
action="store_true",
|
|
322
|
+
help="Remove even if dependencies are detected.",
|
|
323
|
+
)
|
|
324
|
+
uninstall_parser.set_defaults(hook_func=cmd_uninstall)
|
|
325
|
+
|
|
326
|
+
docs_parser = hook_sub.add_parser("docs", help="Show hook documentation.")
|
|
327
|
+
docs_parser.add_argument("target", help="Hook name or entrypoint name.")
|
|
328
|
+
docs_parser.add_argument(
|
|
329
|
+
"--render",
|
|
330
|
+
action="store_true",
|
|
331
|
+
help="Render markdown using rich (if installed).",
|
|
332
|
+
)
|
|
333
|
+
docs_parser.set_defaults(hook_func=cmd_docs)
|
|
334
|
+
|
|
335
|
+
preset_parser = hook_sub.add_parser(
|
|
336
|
+
"preset",
|
|
337
|
+
help="Generate a YAML hook-args preset template.",
|
|
338
|
+
)
|
|
339
|
+
preset_parser.add_argument(
|
|
340
|
+
"target",
|
|
341
|
+
help="Hook entrypoint name.",
|
|
342
|
+
)
|
|
343
|
+
preset_parser.add_argument(
|
|
344
|
+
"-o",
|
|
345
|
+
"--output",
|
|
346
|
+
help="Write the preset YAML to a file instead of stdout.",
|
|
347
|
+
)
|
|
348
|
+
preset_parser.set_defaults(hook_func=cmd_preset)
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import logging
|
|
5
|
+
import os
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from brkraw.core import config as config_core
|
|
8
|
+
from brkraw.cli.utils import load
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger("brkraw")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def cmd_info(args: argparse.Namespace) -> int:
|
|
14
|
+
if args.path is None:
|
|
15
|
+
args.path = os.environ.get("BRKRAW_PATH")
|
|
16
|
+
if args.path is None:
|
|
17
|
+
args.parser.print_help()
|
|
18
|
+
return 2
|
|
19
|
+
if args.scan_id is None:
|
|
20
|
+
env_scan = os.environ.get("BRKRAW_SCAN_ID")
|
|
21
|
+
if env_scan:
|
|
22
|
+
parts = [p.strip() for p in env_scan.split(",") if p.strip()]
|
|
23
|
+
try:
|
|
24
|
+
args.scan_id = [int(p) for p in parts]
|
|
25
|
+
except ValueError:
|
|
26
|
+
logger.error("Invalid BRKRAW_SCAN_ID: %s", env_scan)
|
|
27
|
+
return 2
|
|
28
|
+
if not Path(args.path).exists():
|
|
29
|
+
logger.error("Path not found: %s", args.path)
|
|
30
|
+
return 2
|
|
31
|
+
loader = load(args.path, prefix="Loading")
|
|
32
|
+
width = config_core.output_width(root=args.root)
|
|
33
|
+
text = loader.info(
|
|
34
|
+
scope=args.scope,
|
|
35
|
+
scan_id=args.scan_id,
|
|
36
|
+
as_dict=False,
|
|
37
|
+
scan_transpose=True,
|
|
38
|
+
show_reco=args.show_reco,
|
|
39
|
+
width=width,
|
|
40
|
+
)
|
|
41
|
+
if text is not None:
|
|
42
|
+
logger.info("%s", text)
|
|
43
|
+
return 0
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def register(subparsers: argparse._SubParsersAction) -> None: # type: ignore[name-defined]
|
|
47
|
+
info_parser = subparsers.add_parser(
|
|
48
|
+
"info",
|
|
49
|
+
help="Show scan/study info from a dataset path.",
|
|
50
|
+
)
|
|
51
|
+
info_parser.add_argument("path", nargs="?", help="Path to the Bruker study.")
|
|
52
|
+
info_parser.add_argument(
|
|
53
|
+
"--scope",
|
|
54
|
+
choices=["full", "study", "scan"],
|
|
55
|
+
default="full",
|
|
56
|
+
help="Select info scope (default: full).",
|
|
57
|
+
)
|
|
58
|
+
info_parser.add_argument(
|
|
59
|
+
"-s",
|
|
60
|
+
"--scan-id",
|
|
61
|
+
nargs="*",
|
|
62
|
+
type=int,
|
|
63
|
+
help="Scan id(s) to include when scope is scan/full.",
|
|
64
|
+
)
|
|
65
|
+
info_parser.add_argument(
|
|
66
|
+
"--show-reco",
|
|
67
|
+
action="store_true",
|
|
68
|
+
help="Include reco entries in output.",
|
|
69
|
+
)
|
|
70
|
+
info_parser.add_argument(
|
|
71
|
+
"--root",
|
|
72
|
+
help="Override config root directory (default: BRKRAW_CONFIG_HOME or ~/.brkraw).",
|
|
73
|
+
)
|
|
74
|
+
info_parser.set_defaults(func=cmd_info, parser=info_parser)
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from typing import Optional, Dict, Any
|
|
3
|
+
from pprint import pprint
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import logging
|
|
7
|
+
import os
|
|
8
|
+
from datetime import date
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
import yaml
|
|
12
|
+
|
|
13
|
+
from brkraw.core import config as config_core
|
|
14
|
+
from brkraw.apps import addon as addon_app
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger("brkraw")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def cmd_init(args: argparse.Namespace) -> int:
|
|
20
|
+
if args.config:
|
|
21
|
+
config_core.init(
|
|
22
|
+
root=args.root,
|
|
23
|
+
create_config=False,
|
|
24
|
+
exist_ok=not args.no_exist_ok,
|
|
25
|
+
)
|
|
26
|
+
paths = config_core.paths(root=args.root)
|
|
27
|
+
existing = config_core.load(root=args.root)
|
|
28
|
+
if paths.config_file.exists():
|
|
29
|
+
pprint(existing or {})
|
|
30
|
+
replace = _prompt_bool(
|
|
31
|
+
"Replace existing config.yaml?",
|
|
32
|
+
default=False,
|
|
33
|
+
)
|
|
34
|
+
if not replace:
|
|
35
|
+
return 0
|
|
36
|
+
defaults = existing
|
|
37
|
+
else:
|
|
38
|
+
defaults = config_core.default_config()
|
|
39
|
+
config_values = _prompt_config_values(defaults=defaults)
|
|
40
|
+
config_core.write_config(config_values, root=args.root)
|
|
41
|
+
logger.info("Wrote config at %s", config_core.paths(root=args.root).config_file)
|
|
42
|
+
return 0
|
|
43
|
+
|
|
44
|
+
interactive = not args.yes
|
|
45
|
+
create_config = True
|
|
46
|
+
install_defaults = args.install_default
|
|
47
|
+
shellrc = Path(args.shellrc) if args.shellrc else _default_shell_rc()
|
|
48
|
+
explicit_actions = args.install_default or args.shellrc
|
|
49
|
+
config_values: Optional[Dict[str, Any]] = None
|
|
50
|
+
|
|
51
|
+
if interactive and explicit_actions:
|
|
52
|
+
interactive = False
|
|
53
|
+
create_config = False
|
|
54
|
+
install_defaults = args.install_default
|
|
55
|
+
shellrc = Path(args.shellrc) if args.shellrc else None
|
|
56
|
+
|
|
57
|
+
if interactive:
|
|
58
|
+
create_config = _prompt_bool("Create config.yaml?", default=create_config)
|
|
59
|
+
if create_config:
|
|
60
|
+
config_values = _prompt_config_values()
|
|
61
|
+
install_defaults = _prompt_bool(
|
|
62
|
+
"Install default specs/rules?", default=install_defaults
|
|
63
|
+
)
|
|
64
|
+
install_helpers = _prompt_bool(
|
|
65
|
+
"Install shell helpers?", default=shellrc is not None
|
|
66
|
+
)
|
|
67
|
+
if install_helpers:
|
|
68
|
+
if shellrc is None:
|
|
69
|
+
shellrc = _prompt_path("Shell rc path", default=None)
|
|
70
|
+
if shellrc is not None:
|
|
71
|
+
_install_shell_helpers(shellrc)
|
|
72
|
+
else:
|
|
73
|
+
shellrc = None
|
|
74
|
+
|
|
75
|
+
config_core.init(
|
|
76
|
+
root=args.root,
|
|
77
|
+
create_config=False,
|
|
78
|
+
exist_ok=not args.no_exist_ok,
|
|
79
|
+
)
|
|
80
|
+
logger.info("Initialized config at %s", config_core.paths(root=args.root).root)
|
|
81
|
+
if create_config:
|
|
82
|
+
if config_values is None:
|
|
83
|
+
config_values = config_core.default_config()
|
|
84
|
+
config_core.write_config(config_values, root=args.root)
|
|
85
|
+
if install_defaults:
|
|
86
|
+
installed = addon_app.install_defaults(root=args.root)
|
|
87
|
+
if installed:
|
|
88
|
+
logger.info("Installed %d default file(s).", len(installed))
|
|
89
|
+
if not interactive and shellrc is not None:
|
|
90
|
+
_install_shell_helpers(shellrc)
|
|
91
|
+
return 0
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def register(subparsers: argparse._SubParsersAction) -> None: # type: ignore[name-defined]
|
|
95
|
+
init_parser = subparsers.add_parser(
|
|
96
|
+
"init",
|
|
97
|
+
help="Initialize config and install defaults.",
|
|
98
|
+
)
|
|
99
|
+
init_parser.add_argument(
|
|
100
|
+
"--root",
|
|
101
|
+
help="Override config root directory (default: BRKRAW_CONFIG_HOME or ~/.brkraw).",
|
|
102
|
+
)
|
|
103
|
+
init_parser.add_argument(
|
|
104
|
+
"--no-exist-ok",
|
|
105
|
+
action="store_true",
|
|
106
|
+
help="Fail if the root directory already exists.",
|
|
107
|
+
)
|
|
108
|
+
init_parser.add_argument(
|
|
109
|
+
"--config",
|
|
110
|
+
action="store_true",
|
|
111
|
+
help="Create or replace config.yaml only.",
|
|
112
|
+
)
|
|
113
|
+
init_parser.add_argument(
|
|
114
|
+
"--yes",
|
|
115
|
+
action="store_true",
|
|
116
|
+
help="Skip prompts and use defaults.",
|
|
117
|
+
)
|
|
118
|
+
init_parser.add_argument(
|
|
119
|
+
"--install-default",
|
|
120
|
+
action="store_true",
|
|
121
|
+
help="Install default specs and rules.",
|
|
122
|
+
)
|
|
123
|
+
init_parser.add_argument(
|
|
124
|
+
"--shell-rc",
|
|
125
|
+
dest="shellrc",
|
|
126
|
+
help="Append shell helpers to the specified rc file (defaults to ~/.zshrc or ~/.bashrc).",
|
|
127
|
+
)
|
|
128
|
+
init_parser.set_defaults(func=cmd_init)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _prompt_bool(label: str, *, default: bool) -> bool:
|
|
132
|
+
prompt = "Y/n" if default else "y/N"
|
|
133
|
+
while True:
|
|
134
|
+
reply = input(f"{label} [{prompt}]: ").strip().lower()
|
|
135
|
+
if not reply:
|
|
136
|
+
return default
|
|
137
|
+
if reply in {"y", "yes"}:
|
|
138
|
+
return True
|
|
139
|
+
if reply in {"n", "no"}:
|
|
140
|
+
return False
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _prompt_config_values(*, defaults: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
|
|
144
|
+
base = config_core.default_config()
|
|
145
|
+
if defaults:
|
|
146
|
+
base.update(defaults)
|
|
147
|
+
result: Dict[str, Any] = {}
|
|
148
|
+
keys = list(base.keys())
|
|
149
|
+
for key in keys:
|
|
150
|
+
if key == "config_version":
|
|
151
|
+
result[key] = base.get(key)
|
|
152
|
+
continue
|
|
153
|
+
default = base.get(key)
|
|
154
|
+
display = "null" if default is None else str(default)
|
|
155
|
+
reply = input(f"{key} [{display}]: ").strip()
|
|
156
|
+
if reply == "":
|
|
157
|
+
result[key] = default
|
|
158
|
+
else:
|
|
159
|
+
result[key] = yaml.safe_load(reply)
|
|
160
|
+
return result
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _prompt_path(label: str, *, default: Optional[Path]) -> Optional[Path]:
|
|
164
|
+
display = str(default) if default else ""
|
|
165
|
+
reply = input(f"{label} [{display}]: ").strip()
|
|
166
|
+
if not reply:
|
|
167
|
+
return default
|
|
168
|
+
return Path(reply).expanduser()
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _install_shell_helpers(path: Path) -> None:
|
|
172
|
+
marker = "# brkraw shell helpers"
|
|
173
|
+
snippet = "\n".join(
|
|
174
|
+
[
|
|
175
|
+
f"{marker} (added {date.today().isoformat()})",
|
|
176
|
+
"brkraw-set() {",
|
|
177
|
+
" if [ \"$#\" -eq 0 ]; then",
|
|
178
|
+
" brkraw session set",
|
|
179
|
+
" else",
|
|
180
|
+
" eval \"$(brkraw session set \"$@\")\"",
|
|
181
|
+
" fi",
|
|
182
|
+
"}",
|
|
183
|
+
"brkraw-unset() {",
|
|
184
|
+
" if [ \"$#\" -eq 0 ]; then",
|
|
185
|
+
" eval \"$(brkraw session unset)\"",
|
|
186
|
+
" elif [ \"$1\" = \"-h\" ] || [ \"$1\" = \"--help\" ]; then",
|
|
187
|
+
" brkraw session unset \"$@\"",
|
|
188
|
+
" else",
|
|
189
|
+
" eval \"$(brkraw session unset \"$@\")\"",
|
|
190
|
+
" fi",
|
|
191
|
+
"}",
|
|
192
|
+
"",
|
|
193
|
+
]
|
|
194
|
+
)
|
|
195
|
+
if path.exists():
|
|
196
|
+
content = path.read_text(encoding="utf-8")
|
|
197
|
+
if marker in content:
|
|
198
|
+
logger.info("Shell helpers already present in %s", path)
|
|
199
|
+
return
|
|
200
|
+
else:
|
|
201
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
202
|
+
content = ""
|
|
203
|
+
path.write_text(content + ("\n" if content and not content.endswith("\n") else "") + snippet, encoding="utf-8")
|
|
204
|
+
logger.info("Appended shell helpers to %s", path)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def _default_shell_rc() -> Optional[Path]:
|
|
208
|
+
shell = os.environ.get("SHELL", "")
|
|
209
|
+
home = Path.home()
|
|
210
|
+
if shell.endswith("zsh"):
|
|
211
|
+
return home / ".zshrc"
|
|
212
|
+
if shell.endswith("bash"):
|
|
213
|
+
return home / ".bashrc"
|
|
214
|
+
return None
|