scitex 2.11.0__py3-none-any.whl → 2.13.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.
- scitex/__main__.py +24 -5
- scitex/__version__.py +1 -1
- scitex/_optional_deps.py +33 -0
- scitex/ai/classification/reporters/_ClassificationReporter.py +1 -1
- scitex/ai/classification/timeseries/_TimeSeriesBlockingSplit.py +2 -2
- scitex/ai/classification/timeseries/_TimeSeriesCalendarSplit.py +2 -2
- scitex/ai/classification/timeseries/_TimeSeriesSlidingWindowSplit.py +2 -2
- scitex/ai/classification/timeseries/_TimeSeriesSlidingWindowSplit_v01-not-using-n_splits.py +2 -2
- scitex/ai/classification/timeseries/_TimeSeriesStratifiedSplit.py +2 -2
- scitex/ai/classification/timeseries/_normalize_timestamp.py +1 -1
- scitex/ai/metrics/_calc_seizure_prediction_metrics.py +1 -1
- scitex/ai/plt/_plot_feature_importance.py +1 -1
- scitex/ai/plt/_plot_learning_curve.py +1 -1
- scitex/ai/plt/_plot_optuna_study.py +1 -1
- scitex/ai/plt/_plot_pre_rec_curve.py +1 -1
- scitex/ai/plt/_plot_roc_curve.py +1 -1
- scitex/ai/plt/_stx_conf_mat.py +1 -1
- scitex/ai/training/_LearningCurveLogger.py +1 -1
- scitex/audio/mcp_server.py +38 -8
- scitex/browser/automation/CookieHandler.py +1 -1
- scitex/browser/core/BrowserMixin.py +1 -1
- scitex/browser/core/ChromeProfileManager.py +1 -1
- scitex/browser/debugging/_browser_logger.py +1 -1
- scitex/browser/debugging/_highlight_element.py +1 -1
- scitex/browser/debugging/_show_grid.py +1 -1
- scitex/browser/interaction/click_center.py +1 -1
- scitex/browser/interaction/click_with_fallbacks.py +1 -1
- scitex/browser/interaction/close_popups.py +1 -1
- scitex/browser/interaction/fill_with_fallbacks.py +1 -1
- scitex/browser/pdf/click_download_for_chrome_pdf_viewer.py +1 -1
- scitex/browser/pdf/detect_chrome_pdf_viewer.py +1 -1
- scitex/browser/stealth/HumanBehavior.py +1 -1
- scitex/browser/stealth/StealthManager.py +1 -1
- scitex/canvas/_mcp_handlers.py +372 -0
- scitex/canvas/_mcp_tool_schemas.py +219 -0
- scitex/canvas/mcp_server.py +151 -0
- scitex/capture/mcp_server.py +41 -12
- scitex/cli/audio.py +233 -0
- scitex/cli/capture.py +307 -0
- scitex/cli/main.py +27 -4
- scitex/cli/repro.py +233 -0
- scitex/cli/resource.py +240 -0
- scitex/cli/stats.py +325 -0
- scitex/cli/template.py +236 -0
- scitex/cli/tex.py +286 -0
- scitex/cli/web.py +11 -12
- scitex/dev/__init__.py +3 -0
- scitex/dev/_pyproject.py +405 -0
- scitex/dev/plt/__init__.py +2 -2
- scitex/dev/plt/mpl/get_dir_ax.py +1 -1
- scitex/dev/plt/mpl/get_signatures.py +1 -1
- scitex/dev/plt/mpl/get_signatures_details.py +1 -1
- scitex/diagram/_mcp_handlers.py +400 -0
- scitex/diagram/_mcp_tool_schemas.py +157 -0
- scitex/diagram/mcp_server.py +151 -0
- scitex/dsp/_demo_sig.py +51 -5
- scitex/dsp/_mne.py +13 -2
- scitex/dsp/_modulation_index.py +15 -3
- scitex/dsp/_pac.py +23 -5
- scitex/dsp/_psd.py +16 -4
- scitex/dsp/_resample.py +24 -4
- scitex/dsp/_transform.py +16 -3
- scitex/dsp/add_noise.py +15 -1
- scitex/dsp/norm.py +17 -2
- scitex/dsp/reference.py +17 -1
- scitex/dsp/utils/_differential_bandpass_filters.py +20 -2
- scitex/dsp/utils/_zero_pad.py +18 -4
- scitex/dt/_normalize_timestamp.py +1 -1
- scitex/git/_session.py +1 -1
- scitex/io/_load_modules/_con.py +12 -1
- scitex/io/_load_modules/_eeg.py +12 -1
- scitex/io/_load_modules/_optuna.py +21 -63
- scitex/io/_load_modules/_torch.py +11 -3
- scitex/io/_save_modules/_optuna_study_as_csv_and_pngs.py +13 -2
- scitex/io/_save_modules/_torch.py +11 -3
- scitex/mcp_server.py +159 -0
- scitex/plt/_mcp_handlers.py +361 -0
- scitex/plt/_mcp_tool_schemas.py +169 -0
- scitex/plt/mcp_server.py +205 -0
- scitex/repro/README_RandomStateManager.md +3 -3
- scitex/repro/_RandomStateManager.py +14 -14
- scitex/repro/_gen_ID.py +1 -1
- scitex/repro/_gen_timestamp.py +1 -1
- scitex/repro/_hash_array.py +4 -4
- scitex/scholar/__main__.py +24 -2
- scitex/scholar/_mcp_handlers.py +685 -0
- scitex/scholar/_mcp_tool_schemas.py +339 -0
- scitex/scholar/docs/template.py +1 -1
- scitex/scholar/examples/07_storage_integration.py +1 -1
- scitex/scholar/impact_factor/jcr/ImpactFactorJCREngine.py +1 -1
- scitex/scholar/impact_factor/jcr/build_database.py +1 -1
- scitex/scholar/mcp_server.py +315 -0
- scitex/scholar/pdf_download/ScholarPDFDownloader.py +1 -1
- scitex/scholar/pipelines/ScholarPipelineBibTeX.py +1 -1
- scitex/scholar/pipelines/ScholarPipelineParallel.py +1 -1
- scitex/scholar/pipelines/ScholarPipelineSingle.py +1 -1
- scitex/scholar/storage/PaperIO.py +1 -1
- scitex/session/README.md +4 -4
- scitex/session/__init__.py +1 -1
- scitex/session/_decorator.py +9 -9
- scitex/session/_lifecycle.py +5 -5
- scitex/session/template.py +1 -1
- scitex/stats/__main__.py +281 -0
- scitex/stats/_mcp_handlers.py +1191 -0
- scitex/stats/_mcp_tool_schemas.py +384 -0
- scitex/stats/correct/_correct_bonferroni.py +1 -1
- scitex/stats/correct/_correct_fdr.py +1 -1
- scitex/stats/correct/_correct_fdr_.py +1 -1
- scitex/stats/correct/_correct_holm.py +1 -1
- scitex/stats/correct/_correct_sidak.py +1 -1
- scitex/stats/effect_sizes/_cliffs_delta.py +1 -1
- scitex/stats/effect_sizes/_cohens_d.py +1 -1
- scitex/stats/effect_sizes/_epsilon_squared.py +1 -1
- scitex/stats/effect_sizes/_eta_squared.py +1 -1
- scitex/stats/effect_sizes/_prob_superiority.py +1 -1
- scitex/stats/mcp_server.py +405 -0
- scitex/stats/posthoc/_dunnett.py +1 -1
- scitex/stats/posthoc/_games_howell.py +1 -1
- scitex/stats/posthoc/_tukey_hsd.py +1 -1
- scitex/stats/power/_power.py +1 -1
- scitex/stats/utils/_effect_size.py +1 -1
- scitex/stats/utils/_formatters.py +1 -1
- scitex/stats/utils/_power.py +1 -1
- scitex/template/_mcp_handlers.py +259 -0
- scitex/template/_mcp_tool_schemas.py +112 -0
- scitex/template/mcp_server.py +186 -0
- scitex/utils/_verify_scitex_format.py +2 -2
- scitex/utils/template.py +1 -1
- scitex/web/__init__.py +12 -11
- scitex/web/_scraping.py +26 -265
- scitex/web/download_images.py +316 -0
- scitex/writer/Writer.py +1 -1
- scitex/writer/_clone_writer_project.py +1 -1
- scitex/writer/_validate_tree_structures.py +1 -1
- scitex/writer/dataclasses/config/_WriterConfig.py +1 -1
- scitex/writer/dataclasses/contents/_ManuscriptContents.py +1 -1
- scitex/writer/dataclasses/core/_Document.py +1 -1
- scitex/writer/dataclasses/core/_DocumentSection.py +1 -1
- scitex/writer/dataclasses/results/_CompilationResult.py +1 -1
- scitex/writer/dataclasses/results/_LaTeXIssue.py +1 -1
- scitex/writer/utils/.legacy_git_retry.py +7 -5
- scitex/writer/utils/_parse_latex_logs.py +1 -1
- {scitex-2.11.0.dist-info → scitex-2.13.0.dist-info}/METADATA +431 -269
- {scitex-2.11.0.dist-info → scitex-2.13.0.dist-info}/RECORD +147 -118
- scitex-2.13.0.dist-info/entry_points.txt +11 -0
- scitex-2.11.0.dist-info/entry_points.txt +0 -2
- {scitex-2.11.0.dist-info → scitex-2.13.0.dist-info}/WHEEL +0 -0
- {scitex-2.11.0.dist-info → scitex-2.13.0.dist-info}/licenses/LICENSE +0 -0
scitex/cli/capture.py
ADDED
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
SciTeX CLI - Capture Commands (Screenshot/Monitoring)
|
|
4
|
+
|
|
5
|
+
Provides screen capture, monitoring, and GIF creation.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import sys
|
|
9
|
+
|
|
10
|
+
import click
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@click.group(context_settings={"help_option_names": ["-h", "--help"]})
|
|
14
|
+
def capture():
|
|
15
|
+
"""
|
|
16
|
+
Screen capture and monitoring utilities
|
|
17
|
+
|
|
18
|
+
\b
|
|
19
|
+
Commands:
|
|
20
|
+
snap Take a single screenshot
|
|
21
|
+
start Start continuous monitoring
|
|
22
|
+
stop Stop monitoring
|
|
23
|
+
gif Create GIF from session
|
|
24
|
+
info Display info (monitors, windows)
|
|
25
|
+
window Capture specific window by handle
|
|
26
|
+
|
|
27
|
+
\b
|
|
28
|
+
Examples:
|
|
29
|
+
scitex capture snap # Take screenshot
|
|
30
|
+
scitex capture snap --message "debug" # With message in filename
|
|
31
|
+
scitex capture start --interval 2 # Monitor every 2 seconds
|
|
32
|
+
scitex capture stop # Stop monitoring
|
|
33
|
+
scitex capture gif # Create GIF from latest session
|
|
34
|
+
scitex capture info # List monitors and windows
|
|
35
|
+
"""
|
|
36
|
+
pass
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@capture.command()
|
|
40
|
+
@click.option("--message", "-m", default="", help="Message to include in filename")
|
|
41
|
+
@click.option("--output", "-o", type=click.Path(), help="Output directory")
|
|
42
|
+
@click.option(
|
|
43
|
+
"--quality", "-q", type=int, default=85, help="JPEG quality 1-100 (default: 85)"
|
|
44
|
+
)
|
|
45
|
+
@click.option(
|
|
46
|
+
"--monitor", type=int, default=0, help="Monitor number (0-based, default: 0)"
|
|
47
|
+
)
|
|
48
|
+
@click.option("--all-monitors", is_flag=True, help="Capture all monitors combined")
|
|
49
|
+
def snap(message, output, quality, monitor, all_monitors):
|
|
50
|
+
"""
|
|
51
|
+
Take a single screenshot
|
|
52
|
+
|
|
53
|
+
\b
|
|
54
|
+
Examples:
|
|
55
|
+
scitex capture snap
|
|
56
|
+
scitex capture snap --message "before-change"
|
|
57
|
+
scitex capture snap --all-monitors
|
|
58
|
+
scitex capture snap --monitor 1 --quality 95
|
|
59
|
+
"""
|
|
60
|
+
try:
|
|
61
|
+
from scitex.capture import snap as take_snap
|
|
62
|
+
|
|
63
|
+
click.echo("Taking screenshot...")
|
|
64
|
+
|
|
65
|
+
# Build kwargs
|
|
66
|
+
kwargs = {"message": message}
|
|
67
|
+
if output:
|
|
68
|
+
kwargs["output_dir"] = output
|
|
69
|
+
if quality != 85:
|
|
70
|
+
kwargs["quality"] = quality
|
|
71
|
+
if all_monitors:
|
|
72
|
+
kwargs["capture_all"] = True
|
|
73
|
+
else:
|
|
74
|
+
kwargs["monitor_id"] = monitor
|
|
75
|
+
|
|
76
|
+
result = take_snap(**kwargs)
|
|
77
|
+
|
|
78
|
+
if result:
|
|
79
|
+
click.secho(f"Screenshot saved: {result}", fg="green")
|
|
80
|
+
else:
|
|
81
|
+
click.secho("Screenshot taken", fg="green")
|
|
82
|
+
|
|
83
|
+
except Exception as e:
|
|
84
|
+
click.secho(f"Error: {e}", fg="red", err=True)
|
|
85
|
+
sys.exit(1)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
@capture.command()
|
|
89
|
+
@click.option(
|
|
90
|
+
"--interval",
|
|
91
|
+
"-i",
|
|
92
|
+
type=float,
|
|
93
|
+
default=1.0,
|
|
94
|
+
help="Seconds between captures (default: 1.0)",
|
|
95
|
+
)
|
|
96
|
+
@click.option("--output", "-o", type=click.Path(), help="Output directory")
|
|
97
|
+
@click.option(
|
|
98
|
+
"--quality", "-q", type=int, default=60, help="JPEG quality 1-100 (default: 60)"
|
|
99
|
+
)
|
|
100
|
+
@click.option(
|
|
101
|
+
"--monitor", type=int, default=0, help="Monitor number (0-based, default: 0)"
|
|
102
|
+
)
|
|
103
|
+
@click.option("--all-monitors", is_flag=True, help="Capture all monitors combined")
|
|
104
|
+
def start(interval, output, quality, monitor, all_monitors):
|
|
105
|
+
"""
|
|
106
|
+
Start continuous screenshot monitoring
|
|
107
|
+
|
|
108
|
+
\b
|
|
109
|
+
Examples:
|
|
110
|
+
scitex capture start # Default 1 second interval
|
|
111
|
+
scitex capture start --interval 0.5 # Every 0.5 seconds
|
|
112
|
+
scitex capture start --all-monitors
|
|
113
|
+
"""
|
|
114
|
+
try:
|
|
115
|
+
from scitex.capture import start as start_monitor
|
|
116
|
+
|
|
117
|
+
click.echo(f"Starting monitoring (interval: {interval}s)...")
|
|
118
|
+
click.echo("Press Ctrl+C or run 'scitex capture stop' to stop")
|
|
119
|
+
|
|
120
|
+
kwargs = {"interval": interval}
|
|
121
|
+
if output:
|
|
122
|
+
kwargs["output_dir"] = output
|
|
123
|
+
if quality != 60:
|
|
124
|
+
kwargs["quality"] = quality
|
|
125
|
+
if all_monitors:
|
|
126
|
+
kwargs["capture_all"] = True
|
|
127
|
+
else:
|
|
128
|
+
kwargs["monitor_id"] = monitor
|
|
129
|
+
|
|
130
|
+
start_monitor(**kwargs)
|
|
131
|
+
click.secho("Monitoring started", fg="green")
|
|
132
|
+
|
|
133
|
+
except Exception as e:
|
|
134
|
+
click.secho(f"Error: {e}", fg="red", err=True)
|
|
135
|
+
sys.exit(1)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
@capture.command()
|
|
139
|
+
def stop():
|
|
140
|
+
"""
|
|
141
|
+
Stop continuous monitoring
|
|
142
|
+
|
|
143
|
+
\b
|
|
144
|
+
Example:
|
|
145
|
+
scitex capture stop
|
|
146
|
+
"""
|
|
147
|
+
try:
|
|
148
|
+
from scitex.capture import stop as stop_monitor
|
|
149
|
+
|
|
150
|
+
stop_monitor()
|
|
151
|
+
click.secho("Monitoring stopped", fg="green")
|
|
152
|
+
|
|
153
|
+
except Exception as e:
|
|
154
|
+
click.secho(f"Error: {e}", fg="red", err=True)
|
|
155
|
+
sys.exit(1)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
@capture.command()
|
|
159
|
+
@click.option(
|
|
160
|
+
"--session",
|
|
161
|
+
"-s",
|
|
162
|
+
help="Session ID (e.g., '20250823_104523'). Use 'latest' for most recent.",
|
|
163
|
+
)
|
|
164
|
+
@click.option("--output", "-o", type=click.Path(), help="Output GIF path")
|
|
165
|
+
@click.option(
|
|
166
|
+
"--duration",
|
|
167
|
+
"-d",
|
|
168
|
+
type=float,
|
|
169
|
+
default=0.5,
|
|
170
|
+
help="Duration per frame in seconds (default: 0.5)",
|
|
171
|
+
)
|
|
172
|
+
@click.option("--max-frames", type=int, help="Maximum number of frames to include")
|
|
173
|
+
@click.option(
|
|
174
|
+
"--pattern", "-p", help="Glob pattern for images (alternative to session)"
|
|
175
|
+
)
|
|
176
|
+
def gif(session, output, duration, max_frames, pattern):
|
|
177
|
+
"""
|
|
178
|
+
Create animated GIF from screenshots
|
|
179
|
+
|
|
180
|
+
\b
|
|
181
|
+
Examples:
|
|
182
|
+
scitex capture gif # From latest session
|
|
183
|
+
scitex capture gif --session 20250823_104523
|
|
184
|
+
scitex capture gif --duration 0.3 --max-frames 50
|
|
185
|
+
scitex capture gif --pattern "./screenshots/*.jpg"
|
|
186
|
+
"""
|
|
187
|
+
try:
|
|
188
|
+
if pattern:
|
|
189
|
+
from scitex.capture import create_gif_from_pattern
|
|
190
|
+
|
|
191
|
+
click.echo(f"Creating GIF from pattern: {pattern}")
|
|
192
|
+
result = create_gif_from_pattern(
|
|
193
|
+
pattern=pattern,
|
|
194
|
+
output_path=output,
|
|
195
|
+
duration=duration,
|
|
196
|
+
max_frames=max_frames,
|
|
197
|
+
)
|
|
198
|
+
elif session:
|
|
199
|
+
from scitex.capture import create_gif_from_session
|
|
200
|
+
|
|
201
|
+
click.echo(f"Creating GIF from session: {session}")
|
|
202
|
+
result = create_gif_from_session(
|
|
203
|
+
session_id=session,
|
|
204
|
+
output_path=output,
|
|
205
|
+
duration=duration,
|
|
206
|
+
max_frames=max_frames,
|
|
207
|
+
)
|
|
208
|
+
else:
|
|
209
|
+
from scitex.capture import create_gif_from_latest_session
|
|
210
|
+
|
|
211
|
+
click.echo("Creating GIF from latest session...")
|
|
212
|
+
result = create_gif_from_latest_session(
|
|
213
|
+
output_path=output,
|
|
214
|
+
duration=duration,
|
|
215
|
+
max_frames=max_frames,
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
if result:
|
|
219
|
+
click.secho(f"GIF created: {result}", fg="green")
|
|
220
|
+
else:
|
|
221
|
+
click.secho("No screenshots found to create GIF", fg="yellow")
|
|
222
|
+
|
|
223
|
+
except Exception as e:
|
|
224
|
+
click.secho(f"Error: {e}", fg="red", err=True)
|
|
225
|
+
sys.exit(1)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
@capture.command()
|
|
229
|
+
@click.option("--json", "as_json", is_flag=True, help="Output as JSON")
|
|
230
|
+
def info(as_json):
|
|
231
|
+
"""
|
|
232
|
+
Display system info (monitors, windows, virtual desktops)
|
|
233
|
+
|
|
234
|
+
\b
|
|
235
|
+
Examples:
|
|
236
|
+
scitex capture info
|
|
237
|
+
scitex capture info --json
|
|
238
|
+
"""
|
|
239
|
+
try:
|
|
240
|
+
from scitex.capture import get_info
|
|
241
|
+
|
|
242
|
+
info_data = get_info()
|
|
243
|
+
|
|
244
|
+
if as_json:
|
|
245
|
+
import json
|
|
246
|
+
|
|
247
|
+
click.echo(json.dumps(info_data, indent=2, default=str))
|
|
248
|
+
else:
|
|
249
|
+
click.secho("Display Information", fg="cyan", bold=True)
|
|
250
|
+
click.echo("=" * 50)
|
|
251
|
+
|
|
252
|
+
# Monitors
|
|
253
|
+
monitors = info_data.get("Monitors", {})
|
|
254
|
+
click.secho(f"\nMonitors ({monitors.get('Count', 0)}):", fg="yellow")
|
|
255
|
+
for i, mon in enumerate(monitors.get("Details", [])):
|
|
256
|
+
primary = " (Primary)" if mon.get("Primary") else ""
|
|
257
|
+
click.echo(f" [{i}] {mon.get('Width')}x{mon.get('Height')}{primary}")
|
|
258
|
+
|
|
259
|
+
# Windows
|
|
260
|
+
windows = info_data.get("Windows", {})
|
|
261
|
+
click.secho(f"\nWindows ({windows.get('Count', 0)}):", fg="yellow")
|
|
262
|
+
for win in windows.get("Details", [])[:10]: # Show first 10
|
|
263
|
+
title = win.get("Title", "")[:40]
|
|
264
|
+
handle = win.get("Handle", "")
|
|
265
|
+
click.echo(f" [{handle}] {title}")
|
|
266
|
+
if windows.get("Count", 0) > 10:
|
|
267
|
+
click.echo(f" ... and {windows.get('Count') - 10} more")
|
|
268
|
+
|
|
269
|
+
except Exception as e:
|
|
270
|
+
click.secho(f"Error: {e}", fg="red", err=True)
|
|
271
|
+
sys.exit(1)
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
@capture.command()
|
|
275
|
+
@click.argument("handle", type=int)
|
|
276
|
+
@click.option("--output", "-o", type=click.Path(), help="Output file path")
|
|
277
|
+
@click.option("--quality", "-q", type=int, default=85, help="JPEG quality 1-100")
|
|
278
|
+
def window(handle, output, quality):
|
|
279
|
+
"""
|
|
280
|
+
Capture a specific window by its handle
|
|
281
|
+
|
|
282
|
+
\b
|
|
283
|
+
Get window handles with: scitex capture info
|
|
284
|
+
|
|
285
|
+
\b
|
|
286
|
+
Examples:
|
|
287
|
+
scitex capture window 12345
|
|
288
|
+
scitex capture window 12345 --output ./window.jpg
|
|
289
|
+
"""
|
|
290
|
+
try:
|
|
291
|
+
from scitex.capture import capture_window
|
|
292
|
+
|
|
293
|
+
click.echo(f"Capturing window {handle}...")
|
|
294
|
+
result = capture_window(handle, output)
|
|
295
|
+
|
|
296
|
+
if result:
|
|
297
|
+
click.secho(f"Window captured: {result}", fg="green")
|
|
298
|
+
else:
|
|
299
|
+
click.secho("Window captured", fg="green")
|
|
300
|
+
|
|
301
|
+
except Exception as e:
|
|
302
|
+
click.secho(f"Error: {e}", fg="red", err=True)
|
|
303
|
+
sys.exit(1)
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
if __name__ == "__main__":
|
|
307
|
+
capture()
|
scitex/cli/main.py
CHANGED
|
@@ -8,7 +8,22 @@ import sys
|
|
|
8
8
|
|
|
9
9
|
import click
|
|
10
10
|
|
|
11
|
-
from . import
|
|
11
|
+
from . import (
|
|
12
|
+
audio,
|
|
13
|
+
capture,
|
|
14
|
+
cloud,
|
|
15
|
+
config,
|
|
16
|
+
convert,
|
|
17
|
+
repro,
|
|
18
|
+
resource,
|
|
19
|
+
scholar,
|
|
20
|
+
security,
|
|
21
|
+
stats,
|
|
22
|
+
template,
|
|
23
|
+
tex,
|
|
24
|
+
web,
|
|
25
|
+
writer,
|
|
26
|
+
)
|
|
12
27
|
|
|
13
28
|
|
|
14
29
|
@click.group(context_settings={"help_option_names": ["-h", "--help"]})
|
|
@@ -28,6 +43,10 @@ def cli():
|
|
|
28
43
|
scitex security check --save
|
|
29
44
|
scitex web get-urls https://example.com
|
|
30
45
|
scitex web download-images https://example.com --output ./downloads
|
|
46
|
+
scitex audio speak "Hello world"
|
|
47
|
+
scitex capture snap --output screenshot.jpg
|
|
48
|
+
scitex resource usage
|
|
49
|
+
scitex stats recommend --data data.csv
|
|
31
50
|
|
|
32
51
|
\b
|
|
33
52
|
Enable tab-completion:
|
|
@@ -38,11 +57,18 @@ def cli():
|
|
|
38
57
|
|
|
39
58
|
|
|
40
59
|
# Add command groups
|
|
60
|
+
cli.add_command(audio.audio)
|
|
61
|
+
cli.add_command(capture.capture)
|
|
41
62
|
cli.add_command(cloud.cloud)
|
|
42
63
|
cli.add_command(config.config)
|
|
43
64
|
cli.add_command(convert.convert)
|
|
65
|
+
cli.add_command(repro.repro)
|
|
66
|
+
cli.add_command(resource.resource)
|
|
44
67
|
cli.add_command(scholar.scholar)
|
|
45
68
|
cli.add_command(security.security)
|
|
69
|
+
cli.add_command(stats.stats)
|
|
70
|
+
cli.add_command(template.template)
|
|
71
|
+
cli.add_command(tex.tex)
|
|
46
72
|
cli.add_command(web.web)
|
|
47
73
|
cli.add_command(writer.writer)
|
|
48
74
|
|
|
@@ -111,15 +137,12 @@ def completion(shell, show):
|
|
|
111
137
|
|
|
112
138
|
# Generate completion script
|
|
113
139
|
if shell == "bash":
|
|
114
|
-
completion_script = f"_SCITEX_COMPLETE=bash_source {scitex_full}"
|
|
115
140
|
rc_file = os.path.expanduser("~/.bashrc")
|
|
116
141
|
eval_line = f'eval "$(_SCITEX_COMPLETE=bash_source {scitex_full})"'
|
|
117
142
|
elif shell == "zsh":
|
|
118
|
-
completion_script = f"_SCITEX_COMPLETE=zsh_source {scitex_full}"
|
|
119
143
|
rc_file = os.path.expanduser("~/.zshrc")
|
|
120
144
|
eval_line = f'eval "$(_SCITEX_COMPLETE=zsh_source {scitex_full})"'
|
|
121
145
|
elif shell == "fish":
|
|
122
|
-
completion_script = f"_SCITEX_COMPLETE=fish_source {scitex_full}"
|
|
123
146
|
rc_file = os.path.expanduser("~/.config/fish/config.fish")
|
|
124
147
|
eval_line = f"eval (env _SCITEX_COMPLETE=fish_source {scitex_full})"
|
|
125
148
|
|
scitex/cli/repro.py
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
SciTeX CLI - Repro Commands (Reproducibility)
|
|
4
|
+
|
|
5
|
+
Provides reproducibility utilities: ID generation, timestamps, hashing.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import sys
|
|
9
|
+
|
|
10
|
+
import click
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@click.group(context_settings={"help_option_names": ["-h", "--help"]})
|
|
14
|
+
def repro():
|
|
15
|
+
"""
|
|
16
|
+
Reproducibility utilities
|
|
17
|
+
|
|
18
|
+
\b
|
|
19
|
+
Commands:
|
|
20
|
+
gen-id Generate unique identifier
|
|
21
|
+
gen-timestamp Generate timestamp
|
|
22
|
+
hash Hash array/file for reproducibility
|
|
23
|
+
seed Set random seed across all libraries
|
|
24
|
+
|
|
25
|
+
\b
|
|
26
|
+
Examples:
|
|
27
|
+
scitex repro gen-id # Generate unique ID
|
|
28
|
+
scitex repro gen-timestamp # Generate timestamp
|
|
29
|
+
scitex repro hash data.npy # Hash array file
|
|
30
|
+
scitex repro seed 42 # Set random seed
|
|
31
|
+
"""
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@repro.command("gen-id")
|
|
36
|
+
@click.option("--length", "-l", type=int, default=8, help="ID length (default: 8)")
|
|
37
|
+
@click.option("--prefix", "-p", default="", help="Prefix to add to ID")
|
|
38
|
+
@click.option("--count", "-n", type=int, default=1, help="Number of IDs to generate")
|
|
39
|
+
def gen_id(length, prefix, count):
|
|
40
|
+
"""
|
|
41
|
+
Generate unique identifier(s)
|
|
42
|
+
|
|
43
|
+
\b
|
|
44
|
+
Examples:
|
|
45
|
+
scitex repro gen-id
|
|
46
|
+
scitex repro gen-id --length 12
|
|
47
|
+
scitex repro gen-id --prefix exp_
|
|
48
|
+
scitex repro gen-id --count 5
|
|
49
|
+
"""
|
|
50
|
+
try:
|
|
51
|
+
from scitex.repro import gen_ID
|
|
52
|
+
|
|
53
|
+
for _ in range(count):
|
|
54
|
+
id_str = gen_ID()
|
|
55
|
+
if length != 8:
|
|
56
|
+
id_str = id_str[:length]
|
|
57
|
+
if prefix:
|
|
58
|
+
id_str = f"{prefix}{id_str}"
|
|
59
|
+
click.echo(id_str)
|
|
60
|
+
|
|
61
|
+
except Exception as e:
|
|
62
|
+
click.secho(f"Error: {e}", fg="red", err=True)
|
|
63
|
+
sys.exit(1)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@repro.command("gen-timestamp")
|
|
67
|
+
@click.option(
|
|
68
|
+
"--format",
|
|
69
|
+
"-f",
|
|
70
|
+
"fmt",
|
|
71
|
+
type=click.Choice(["iso", "file", "compact", "human"]),
|
|
72
|
+
default="iso",
|
|
73
|
+
help="Timestamp format (default: iso)",
|
|
74
|
+
)
|
|
75
|
+
@click.option("--utc", is_flag=True, help="Use UTC timezone")
|
|
76
|
+
def gen_timestamp(fmt, utc):
|
|
77
|
+
"""
|
|
78
|
+
Generate timestamp
|
|
79
|
+
|
|
80
|
+
\b
|
|
81
|
+
Formats:
|
|
82
|
+
iso - ISO 8601 format (2025-01-08T12:30:45)
|
|
83
|
+
file - File-safe format (20250108_123045)
|
|
84
|
+
compact - Compact format (20250108123045)
|
|
85
|
+
human - Human readable (Jan 08, 2025 12:30:45)
|
|
86
|
+
|
|
87
|
+
\b
|
|
88
|
+
Examples:
|
|
89
|
+
scitex repro gen-timestamp
|
|
90
|
+
scitex repro gen-timestamp --format file
|
|
91
|
+
scitex repro gen-timestamp --format human --utc
|
|
92
|
+
"""
|
|
93
|
+
try:
|
|
94
|
+
from datetime import datetime, timezone
|
|
95
|
+
|
|
96
|
+
from scitex.repro import gen_timestamp as make_timestamp
|
|
97
|
+
|
|
98
|
+
if utc:
|
|
99
|
+
now = datetime.now(timezone.utc)
|
|
100
|
+
else:
|
|
101
|
+
now = datetime.now()
|
|
102
|
+
|
|
103
|
+
if fmt == "iso":
|
|
104
|
+
ts = now.isoformat()
|
|
105
|
+
elif fmt == "file":
|
|
106
|
+
ts = now.strftime("%Y%m%d_%H%M%S")
|
|
107
|
+
elif fmt == "compact":
|
|
108
|
+
ts = now.strftime("%Y%m%d%H%M%S")
|
|
109
|
+
elif fmt == "human":
|
|
110
|
+
ts = now.strftime("%b %d, %Y %H:%M:%S")
|
|
111
|
+
else:
|
|
112
|
+
ts = make_timestamp()
|
|
113
|
+
|
|
114
|
+
click.echo(ts)
|
|
115
|
+
|
|
116
|
+
except Exception as e:
|
|
117
|
+
click.secho(f"Error: {e}", fg="red", err=True)
|
|
118
|
+
sys.exit(1)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@repro.command()
|
|
122
|
+
@click.argument("file_path", type=click.Path(exists=True))
|
|
123
|
+
@click.option(
|
|
124
|
+
"--algorithm", "-a", default="sha256", help="Hash algorithm (default: sha256)"
|
|
125
|
+
)
|
|
126
|
+
@click.option("--short", "-s", is_flag=True, help="Output short hash (first 8 chars)")
|
|
127
|
+
def hash(file_path, algorithm, short):
|
|
128
|
+
"""
|
|
129
|
+
Hash array or file for reproducibility verification
|
|
130
|
+
|
|
131
|
+
\b
|
|
132
|
+
Supported file types:
|
|
133
|
+
.npy, .npz - NumPy arrays
|
|
134
|
+
.pt, .pth - PyTorch tensors
|
|
135
|
+
.pkl - Pickle files
|
|
136
|
+
* - Any file (raw bytes hash)
|
|
137
|
+
|
|
138
|
+
\b
|
|
139
|
+
Examples:
|
|
140
|
+
scitex repro hash data.npy
|
|
141
|
+
scitex repro hash model.pt --short
|
|
142
|
+
scitex repro hash weights.npz --algorithm md5
|
|
143
|
+
"""
|
|
144
|
+
try:
|
|
145
|
+
import hashlib
|
|
146
|
+
from pathlib import Path
|
|
147
|
+
|
|
148
|
+
path = Path(file_path)
|
|
149
|
+
|
|
150
|
+
# Try to load as array first
|
|
151
|
+
hash_val = None
|
|
152
|
+
try:
|
|
153
|
+
if path.suffix in (".npy", ".npz"):
|
|
154
|
+
import numpy as np
|
|
155
|
+
|
|
156
|
+
from scitex.repro import hash_array
|
|
157
|
+
|
|
158
|
+
arr = np.load(path, allow_pickle=True)
|
|
159
|
+
if isinstance(arr, np.lib.npyio.NpzFile):
|
|
160
|
+
# For npz, hash all arrays
|
|
161
|
+
hashes = []
|
|
162
|
+
for key in arr.files:
|
|
163
|
+
hashes.append(hash_array(arr[key]))
|
|
164
|
+
hash_val = hashlib.sha256("".join(hashes).encode()).hexdigest()
|
|
165
|
+
else:
|
|
166
|
+
hash_val = hash_array(arr)
|
|
167
|
+
elif path.suffix in (".pt", ".pth"):
|
|
168
|
+
import torch
|
|
169
|
+
|
|
170
|
+
data = torch.load(path, map_location="cpu")
|
|
171
|
+
if isinstance(data, torch.Tensor):
|
|
172
|
+
hash_val = hashlib.sha256(data.numpy().tobytes()).hexdigest()
|
|
173
|
+
else:
|
|
174
|
+
# State dict or other
|
|
175
|
+
hash_val = hashlib.sha256(str(data).encode()).hexdigest()
|
|
176
|
+
except ImportError:
|
|
177
|
+
pass
|
|
178
|
+
|
|
179
|
+
# Fall back to file hash
|
|
180
|
+
if hash_val is None:
|
|
181
|
+
h = hashlib.new(algorithm)
|
|
182
|
+
with open(path, "rb") as f:
|
|
183
|
+
for chunk in iter(lambda: f.read(8192), b""):
|
|
184
|
+
h.update(chunk)
|
|
185
|
+
hash_val = h.hexdigest()
|
|
186
|
+
|
|
187
|
+
if short:
|
|
188
|
+
hash_val = hash_val[:8]
|
|
189
|
+
|
|
190
|
+
click.echo(f"{hash_val} {path.name}")
|
|
191
|
+
|
|
192
|
+
except Exception as e:
|
|
193
|
+
click.secho(f"Error: {e}", fg="red", err=True)
|
|
194
|
+
sys.exit(1)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
@repro.command()
|
|
198
|
+
@click.argument("seed", type=int)
|
|
199
|
+
@click.option("--verbose", "-v", is_flag=True, help="Show what was seeded")
|
|
200
|
+
def seed(seed, verbose):
|
|
201
|
+
"""
|
|
202
|
+
Set random seed across all available libraries
|
|
203
|
+
|
|
204
|
+
\b
|
|
205
|
+
Affects: os, random, numpy, torch, tensorflow, jax
|
|
206
|
+
|
|
207
|
+
\b
|
|
208
|
+
Examples:
|
|
209
|
+
scitex repro seed 42
|
|
210
|
+
scitex repro seed 12345 --verbose
|
|
211
|
+
"""
|
|
212
|
+
try:
|
|
213
|
+
from scitex.repro import RandomStateManager
|
|
214
|
+
|
|
215
|
+
rsm = RandomStateManager(seed=seed, verbose=verbose)
|
|
216
|
+
|
|
217
|
+
click.secho(f"Random seed set to: {seed}", fg="green")
|
|
218
|
+
if verbose:
|
|
219
|
+
click.echo("Seeded libraries:")
|
|
220
|
+
click.echo(" - os.environ['PYTHONHASHSEED']")
|
|
221
|
+
click.echo(" - random")
|
|
222
|
+
click.echo(" - numpy (if available)")
|
|
223
|
+
click.echo(" - torch (if available)")
|
|
224
|
+
click.echo(" - tensorflow (if available)")
|
|
225
|
+
click.echo(" - jax (if available)")
|
|
226
|
+
|
|
227
|
+
except Exception as e:
|
|
228
|
+
click.secho(f"Error: {e}", fg="red", err=True)
|
|
229
|
+
sys.exit(1)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
if __name__ == "__main__":
|
|
233
|
+
repro()
|