scitex 2.16.2__py3-none-any.whl → 2.17.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.
Files changed (85) hide show
  1. scitex/_mcp_resources/_cheatsheet.py +1 -1
  2. scitex/_mcp_resources/_modules.py +1 -1
  3. scitex/_mcp_tools/__init__.py +2 -0
  4. scitex/_mcp_tools/verify.py +256 -0
  5. scitex/cli/main.py +2 -0
  6. scitex/cli/verify.py +476 -0
  7. scitex/dev/plt/__init__.py +1 -1
  8. scitex/dev/plt/data/mpl/PLOTTING_FUNCTIONS.yaml +90 -0
  9. scitex/dev/plt/data/mpl/PLOTTING_SIGNATURES.yaml +1571 -0
  10. scitex/dev/plt/data/mpl/PLOTTING_SIGNATURES_DETAILED.yaml +6262 -0
  11. scitex/dev/plt/data/mpl/SIGNATURES_FLATTENED.yaml +1274 -0
  12. scitex/dev/plt/data/mpl/dir_ax.txt +459 -0
  13. scitex/dev/plt/mpl/get_dir_ax.py +1 -1
  14. scitex/dev/plt/mpl/get_signatures.py +1 -1
  15. scitex/dev/plt/mpl/get_signatures_details.py +1 -1
  16. scitex/io/_load.py +8 -1
  17. scitex/io/_save.py +12 -0
  18. scitex/scholar/data/.gitkeep +0 -0
  19. scitex/scholar/data/README.md +44 -0
  20. scitex/scholar/data/bib_files/bibliography.bib +1952 -0
  21. scitex/scholar/data/bib_files/neurovista.bib +277 -0
  22. scitex/scholar/data/bib_files/neurovista_enriched.bib +441 -0
  23. scitex/scholar/data/bib_files/neurovista_enriched_enriched.bib +441 -0
  24. scitex/scholar/data/bib_files/neurovista_processed.bib +338 -0
  25. scitex/scholar/data/bib_files/openaccess.bib +89 -0
  26. scitex/scholar/data/bib_files/pac-seizure_prediction_enriched.bib +2178 -0
  27. scitex/scholar/data/bib_files/pac.bib +698 -0
  28. scitex/scholar/data/bib_files/pac_enriched.bib +1061 -0
  29. scitex/scholar/data/bib_files/pac_processed.bib +0 -0
  30. scitex/scholar/data/bib_files/pac_titles.txt +75 -0
  31. scitex/scholar/data/bib_files/paywalled.bib +98 -0
  32. scitex/scholar/data/bib_files/related-papers-by-coauthors.bib +58 -0
  33. scitex/scholar/data/bib_files/related-papers-by-coauthors_enriched.bib +87 -0
  34. scitex/scholar/data/bib_files/seizure_prediction.bib +694 -0
  35. scitex/scholar/data/bib_files/seizure_prediction_processed.bib +0 -0
  36. scitex/scholar/data/bib_files/test_complete_enriched.bib +437 -0
  37. scitex/scholar/data/bib_files/test_final_enriched.bib +437 -0
  38. scitex/scholar/data/bib_files/test_seizure.bib +46 -0
  39. scitex/scholar/data/impact_factor/JCR_IF_2022.xlsx +0 -0
  40. scitex/scholar/data/impact_factor/JCR_IF_2024.db +0 -0
  41. scitex/scholar/data/impact_factor/JCR_IF_2024.xlsx +0 -0
  42. scitex/scholar/data/impact_factor/JCR_IF_2024_v01.db +0 -0
  43. scitex/scholar/data/impact_factor.db +0 -0
  44. scitex/session/README.md +2 -2
  45. scitex/session/__init__.py +1 -0
  46. scitex/session/_decorator.py +57 -33
  47. scitex/session/_lifecycle/__init__.py +23 -0
  48. scitex/session/_lifecycle/_close.py +225 -0
  49. scitex/session/_lifecycle/_config.py +112 -0
  50. scitex/session/_lifecycle/_matplotlib.py +83 -0
  51. scitex/session/_lifecycle/_start.py +246 -0
  52. scitex/session/_lifecycle/_utils.py +186 -0
  53. scitex/session/_manager.py +40 -3
  54. scitex/session/template.py +1 -1
  55. scitex/template/_templates/plt.py +1 -1
  56. scitex/template/_templates/session.py +1 -1
  57. scitex/verify/README.md +312 -0
  58. scitex/verify/__init__.py +212 -0
  59. scitex/verify/_chain.py +369 -0
  60. scitex/verify/_db.py +600 -0
  61. scitex/verify/_hash.py +187 -0
  62. scitex/verify/_integration.py +127 -0
  63. scitex/verify/_rerun.py +253 -0
  64. scitex/verify/_tracker.py +330 -0
  65. scitex/verify/_visualize.py +48 -0
  66. scitex/verify/_viz/__init__.py +56 -0
  67. scitex/verify/_viz/_colors.py +84 -0
  68. scitex/verify/_viz/_format.py +302 -0
  69. scitex/verify/_viz/_json.py +192 -0
  70. scitex/verify/_viz/_mermaid.py +440 -0
  71. scitex/verify/_viz/_plotly.py +193 -0
  72. scitex/verify/_viz/_templates.py +246 -0
  73. scitex/verify/_viz/_utils.py +56 -0
  74. {scitex-2.16.2.dist-info → scitex-2.17.0.dist-info}/METADATA +1 -1
  75. {scitex-2.16.2.dist-info → scitex-2.17.0.dist-info}/RECORD +78 -29
  76. scitex/scholar/url_finder/.tmp/open_url/KNOWN_RESOLVERS.py +0 -462
  77. scitex/scholar/url_finder/.tmp/open_url/README.md +0 -223
  78. scitex/scholar/url_finder/.tmp/open_url/_DOIToURLResolver.py +0 -694
  79. scitex/scholar/url_finder/.tmp/open_url/_OpenURLResolver.py +0 -1160
  80. scitex/scholar/url_finder/.tmp/open_url/_ResolverLinkFinder.py +0 -344
  81. scitex/scholar/url_finder/.tmp/open_url/__init__.py +0 -24
  82. scitex/session/_lifecycle.py +0 -827
  83. {scitex-2.16.2.dist-info → scitex-2.17.0.dist-info}/WHEEL +0 -0
  84. {scitex-2.16.2.dist-info → scitex-2.17.0.dist-info}/entry_points.txt +0 -0
  85. {scitex-2.16.2.dist-info → scitex-2.17.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,330 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: "2026-02-01 (ywatanabe)"
3
+ # File: /home/ywatanabe/proj/scitex-python/src/scitex/verify/_tracker.py
4
+ """Session tracker for automatic verification."""
5
+
6
+ from __future__ import annotations
7
+
8
+ from pathlib import Path
9
+ from typing import Any, Dict, List, Optional, Union
10
+
11
+ from ._db import get_db
12
+ from ._hash import combine_hashes, hash_file
13
+
14
+
15
+ class SessionTracker:
16
+ """
17
+ Track inputs/outputs during a session for verification.
18
+
19
+ Automatically records file hashes when files are loaded or saved
20
+ through stx.io, and stores them in the verification database.
21
+
22
+ Examples
23
+ --------
24
+ >>> tracker = SessionTracker("2025Y-11M-18D-09h12m03s_HmH5")
25
+ >>> tracker.record_input("data.csv")
26
+ >>> tracker.record_output("result.png")
27
+ >>> tracker.finalize()
28
+ """
29
+
30
+ def __init__(
31
+ self,
32
+ session_id: str,
33
+ script_path: Optional[str] = None,
34
+ parent_session: Optional[str] = None,
35
+ ):
36
+ """
37
+ Initialize a session tracker.
38
+
39
+ Parameters
40
+ ----------
41
+ session_id : str
42
+ Unique session identifier
43
+ script_path : str, optional
44
+ Path to the script being run
45
+ parent_session : str, optional
46
+ Parent session ID for chain tracking
47
+ """
48
+ self.session_id = session_id
49
+ self.script_path = script_path
50
+ self.parent_session = parent_session
51
+
52
+ self._inputs: Dict[str, str] = {}
53
+ self._outputs: Dict[str, str] = {}
54
+ self._script_hash: Optional[str] = None
55
+ self._finalized = False
56
+
57
+ self._db = get_db()
58
+
59
+ # Compute script hash if provided
60
+ if script_path and Path(script_path).exists():
61
+ self._script_hash = hash_file(script_path)
62
+
63
+ # Register run in database
64
+ self._db.add_run(
65
+ session_id=session_id,
66
+ script_path=script_path or "",
67
+ script_hash=self._script_hash,
68
+ parent_session=parent_session,
69
+ )
70
+
71
+ def record_input(
72
+ self,
73
+ path: Union[str, Path],
74
+ track: bool = True,
75
+ ) -> Optional[str]:
76
+ """
77
+ Record a file as an input.
78
+
79
+ Parameters
80
+ ----------
81
+ path : str or Path
82
+ Path to the input file
83
+ track : bool, optional
84
+ Whether to track this file (default: True)
85
+
86
+ Returns
87
+ -------
88
+ str or None
89
+ Hash of the file, or None if not tracked
90
+ """
91
+ if not track or self._finalized:
92
+ return None
93
+
94
+ path = Path(path)
95
+ if not path.exists():
96
+ return None
97
+
98
+ path_str = str(path.resolve())
99
+ if path_str not in self._inputs:
100
+ file_hash = hash_file(path)
101
+ self._inputs[path_str] = file_hash
102
+ self._db.add_file_hash(
103
+ session_id=self.session_id,
104
+ file_path=path_str,
105
+ hash_value=file_hash,
106
+ role="input",
107
+ )
108
+
109
+ # Auto-link parent: if this file was created by another session,
110
+ # set that session as our parent (first one found)
111
+ if self.parent_session is None:
112
+ producer_sessions = self._db.find_session_by_file(
113
+ path_str, role="output"
114
+ )
115
+ if producer_sessions:
116
+ self.parent_session = producer_sessions[0]
117
+ self._db.set_parent(self.session_id, self.parent_session)
118
+
119
+ return self._inputs[path_str]
120
+
121
+ def record_output(
122
+ self,
123
+ path: Union[str, Path],
124
+ track: bool = True,
125
+ ) -> Optional[str]:
126
+ """
127
+ Record a file as an output.
128
+
129
+ Parameters
130
+ ----------
131
+ path : str or Path
132
+ Path to the output file
133
+ track : bool, optional
134
+ Whether to track this file (default: True)
135
+
136
+ Returns
137
+ -------
138
+ str or None
139
+ Hash of the file, or None if not tracked
140
+ """
141
+ if not track or self._finalized:
142
+ return None
143
+
144
+ path = Path(path)
145
+ if not path.exists():
146
+ return None
147
+
148
+ path_str = str(path.resolve())
149
+ file_hash = hash_file(path)
150
+ self._outputs[path_str] = file_hash
151
+ self._db.add_file_hash(
152
+ session_id=self.session_id,
153
+ file_path=path_str,
154
+ hash_value=file_hash,
155
+ role="output",
156
+ )
157
+
158
+ return file_hash
159
+
160
+ def record_inputs(
161
+ self,
162
+ paths: List[Union[str, Path]],
163
+ track: bool = True,
164
+ ) -> Dict[str, str]:
165
+ """Record multiple input files."""
166
+ result = {}
167
+ for path in paths:
168
+ h = self.record_input(path, track=track)
169
+ if h:
170
+ result[str(path)] = h
171
+ return result
172
+
173
+ def record_outputs(
174
+ self,
175
+ paths: List[Union[str, Path]],
176
+ track: bool = True,
177
+ ) -> Dict[str, str]:
178
+ """Record multiple output files."""
179
+ result = {}
180
+ for path in paths:
181
+ h = self.record_output(path, track=track)
182
+ if h:
183
+ result[str(path)] = h
184
+ return result
185
+
186
+ @property
187
+ def inputs(self) -> Dict[str, str]:
188
+ """Get all recorded inputs."""
189
+ return self._inputs.copy()
190
+
191
+ @property
192
+ def outputs(self) -> Dict[str, str]:
193
+ """Get all recorded outputs."""
194
+ return self._outputs.copy()
195
+
196
+ @property
197
+ def combined_hash(self) -> str:
198
+ """Get combined hash of all inputs, script, and outputs."""
199
+ all_hashes = {}
200
+ all_hashes.update({f"input:{k}": v for k, v in self._inputs.items()})
201
+ if self._script_hash:
202
+ all_hashes["script"] = self._script_hash
203
+ all_hashes.update({f"output:{k}": v for k, v in self._outputs.items()})
204
+ return combine_hashes(all_hashes)
205
+
206
+ def finalize(
207
+ self,
208
+ status: str = "success",
209
+ exit_code: int = 0,
210
+ ) -> Dict[str, Any]:
211
+ """
212
+ Finalize the session tracking.
213
+
214
+ Parameters
215
+ ----------
216
+ status : str, optional
217
+ Final status (success, failed, error)
218
+ exit_code : int, optional
219
+ Exit code of the script
220
+
221
+ Returns
222
+ -------
223
+ dict
224
+ Summary of the tracked session
225
+ """
226
+ if self._finalized:
227
+ return self.summary()
228
+
229
+ combined = self.combined_hash
230
+
231
+ self._db.finish_run(
232
+ session_id=self.session_id,
233
+ status=status,
234
+ exit_code=exit_code,
235
+ combined_hash=combined,
236
+ )
237
+
238
+ self._finalized = True
239
+
240
+ return self.summary()
241
+
242
+ def summary(self) -> Dict[str, Any]:
243
+ """Get summary of tracked files."""
244
+ return {
245
+ "session_id": self.session_id,
246
+ "script_path": self.script_path,
247
+ "script_hash": self._script_hash,
248
+ "parent_session": self.parent_session,
249
+ "inputs": self._inputs,
250
+ "outputs": self._outputs,
251
+ "combined_hash": self.combined_hash,
252
+ "finalized": self._finalized,
253
+ }
254
+
255
+
256
+ # Global tracker for current session
257
+ _CURRENT_TRACKER: Optional[SessionTracker] = None
258
+
259
+
260
+ def get_tracker() -> Optional[SessionTracker]:
261
+ """Get the current session tracker."""
262
+ return _CURRENT_TRACKER
263
+
264
+
265
+ def set_tracker(tracker: Optional[SessionTracker]) -> None:
266
+ """Set the current session tracker."""
267
+ global _CURRENT_TRACKER
268
+ _CURRENT_TRACKER = tracker
269
+
270
+
271
+ def start_tracking(
272
+ session_id: str,
273
+ script_path: Optional[str] = None,
274
+ parent_session: Optional[str] = None,
275
+ ) -> SessionTracker:
276
+ """
277
+ Start tracking a new session.
278
+
279
+ Parameters
280
+ ----------
281
+ session_id : str
282
+ Unique session identifier
283
+ script_path : str, optional
284
+ Path to the script being run
285
+ parent_session : str, optional
286
+ Parent session ID for chain tracking
287
+
288
+ Returns
289
+ -------
290
+ SessionTracker
291
+ The new tracker instance
292
+ """
293
+ tracker = SessionTracker(
294
+ session_id=session_id,
295
+ script_path=script_path,
296
+ parent_session=parent_session,
297
+ )
298
+ set_tracker(tracker)
299
+ return tracker
300
+
301
+
302
+ def stop_tracking(
303
+ status: str = "success",
304
+ exit_code: int = 0,
305
+ ) -> Optional[Dict[str, Any]]:
306
+ """
307
+ Stop tracking the current session.
308
+
309
+ Parameters
310
+ ----------
311
+ status : str, optional
312
+ Final status
313
+ exit_code : int, optional
314
+ Exit code
315
+
316
+ Returns
317
+ -------
318
+ dict or None
319
+ Summary of the tracked session, or None if no tracker
320
+ """
321
+ tracker = get_tracker()
322
+ if tracker is None:
323
+ return None
324
+
325
+ result = tracker.finalize(status=status, exit_code=exit_code)
326
+ set_tracker(None)
327
+ return result
328
+
329
+
330
+ # EOF
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: "2026-02-01 (ywatanabe)"
3
+ # File: /home/ywatanabe/proj/scitex-python/src/scitex/verify/_visualize.py
4
+ """Visualization utilities for verification status.
5
+
6
+ This module re-exports from the _viz subpackage for backward compatibility.
7
+
8
+ Provides:
9
+ - Terminal output with colored status icons
10
+ - Mermaid DAG generation
11
+ - Interactive HTML visualization
12
+ - PNG/SVG export via scitex.diagram
13
+ """
14
+
15
+ from ._viz import (
16
+ Colors,
17
+ VerificationLevel,
18
+ format_chain_verification,
19
+ format_list,
20
+ format_run_detailed,
21
+ format_run_verification,
22
+ format_status,
23
+ generate_html_dag,
24
+ generate_mermaid_dag,
25
+ generate_plotly_dag,
26
+ print_verification_summary,
27
+ render_dag,
28
+ render_plotly_dag,
29
+ )
30
+
31
+ __all__ = [
32
+ "Colors",
33
+ "VerificationLevel",
34
+ "format_run_verification",
35
+ "format_run_detailed",
36
+ "format_chain_verification",
37
+ "format_status",
38
+ "format_list",
39
+ "generate_mermaid_dag",
40
+ "generate_html_dag",
41
+ "generate_plotly_dag",
42
+ "render_dag",
43
+ "render_plotly_dag",
44
+ "print_verification_summary",
45
+ ]
46
+
47
+
48
+ # EOF
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: "2026-02-01 (ywatanabe)"
3
+ # File: /home/ywatanabe/proj/scitex-python/src/scitex/verify/_viz/__init__.py
4
+ """Visualization subpackage for verification status.
5
+
6
+ Provides multiple visualization backends:
7
+ - Terminal: Colored text output with status icons
8
+ - Mermaid: Text-based diagrams for docs/GitHub
9
+ - HTML: Interactive web visualization
10
+ - Plotly: Interactive Python-based visualization (optional)
11
+ """
12
+
13
+ from ._colors import Colors, VerificationLevel
14
+ from ._format import (
15
+ format_chain_verification,
16
+ format_list,
17
+ format_run_detailed,
18
+ format_run_verification,
19
+ format_status,
20
+ )
21
+ from ._mermaid import generate_html_dag, generate_mermaid_dag, render_dag
22
+ from ._utils import print_verification_summary
23
+
24
+ # Optional Plotly support
25
+ try:
26
+ from ._plotly import generate_plotly_dag, render_plotly_dag
27
+
28
+ _HAS_PLOTLY = True
29
+ except ImportError:
30
+ _HAS_PLOTLY = False
31
+
32
+ def generate_plotly_dag(*args, **kwargs):
33
+ raise ImportError("plotly required: pip install plotly")
34
+
35
+ def render_plotly_dag(*args, **kwargs):
36
+ raise ImportError("plotly required: pip install plotly")
37
+
38
+
39
+ __all__ = [
40
+ "Colors",
41
+ "VerificationLevel",
42
+ "format_run_verification",
43
+ "format_chain_verification",
44
+ "format_status",
45
+ "format_list",
46
+ "format_run_detailed",
47
+ "generate_mermaid_dag",
48
+ "generate_html_dag",
49
+ "render_dag",
50
+ "print_verification_summary",
51
+ "generate_plotly_dag",
52
+ "render_plotly_dag",
53
+ ]
54
+
55
+
56
+ # EOF
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env python3
2
+ # Timestamp: "2026-02-01 (ywatanabe)"
3
+ # File: /home/ywatanabe/proj/scitex-python/src/scitex/verify/_viz/_colors.py
4
+ """Color constants and status icons for verification visualization."""
5
+
6
+ from __future__ import annotations
7
+
8
+ from .._chain import VerificationStatus
9
+
10
+
11
+ class Colors:
12
+ """ANSI color codes for terminal output."""
13
+
14
+ GREEN = "\033[92m"
15
+ RED = "\033[91m"
16
+ YELLOW = "\033[93m"
17
+ CYAN = "\033[96m"
18
+ GRAY = "\033[90m"
19
+ RESET = "\033[0m"
20
+ BOLD = "\033[1m"
21
+
22
+
23
+ class VerificationLevel:
24
+ """Verification level for display badges."""
25
+
26
+ CACHE = "cache" # verified-by-cache (fast hash comparison) - 🟢
27
+ SCRATCH = "scratch" # verified-from-scratch (re-executed) - 🟢🟢
28
+
29
+
30
+ def status_icon(
31
+ status: VerificationStatus,
32
+ level: str = VerificationLevel.CACHE,
33
+ ) -> str:
34
+ """
35
+ Get colored icon for verification status.
36
+
37
+ Parameters
38
+ ----------
39
+ status : VerificationStatus
40
+ The verification status
41
+ level : str
42
+ Verification level: 'cache' (🟢) or 'scratch' (🟢🟢)
43
+
44
+ Returns
45
+ -------
46
+ str
47
+ Colored status icon
48
+ """
49
+ if level == VerificationLevel.SCRATCH and status == VerificationStatus.VERIFIED:
50
+ return f"{Colors.GREEN}●●{Colors.RESET}"
51
+
52
+ icons = {
53
+ VerificationStatus.VERIFIED: f"{Colors.GREEN}●{Colors.RESET}",
54
+ VerificationStatus.MISMATCH: f"{Colors.RED}●{Colors.RESET}",
55
+ VerificationStatus.MISSING: f"{Colors.YELLOW}○{Colors.RESET}",
56
+ VerificationStatus.UNKNOWN: f"{Colors.CYAN}?{Colors.RESET}",
57
+ }
58
+ return icons.get(status, "?")
59
+
60
+
61
+ def status_text(status: VerificationStatus) -> str:
62
+ """
63
+ Get colored text for verification status.
64
+
65
+ Parameters
66
+ ----------
67
+ status : VerificationStatus
68
+ The verification status
69
+
70
+ Returns
71
+ -------
72
+ str
73
+ Colored status text
74
+ """
75
+ texts = {
76
+ VerificationStatus.VERIFIED: f"{Colors.GREEN}verified{Colors.RESET}",
77
+ VerificationStatus.MISMATCH: f"{Colors.RED}mismatch{Colors.RESET}",
78
+ VerificationStatus.MISSING: f"{Colors.YELLOW}missing{Colors.RESET}",
79
+ VerificationStatus.UNKNOWN: f"{Colors.CYAN}unknown{Colors.RESET}",
80
+ }
81
+ return texts.get(status, "unknown")
82
+
83
+
84
+ # EOF