scitex 2.16.1__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.
- scitex/_mcp_resources/_cheatsheet.py +1 -1
- scitex/_mcp_resources/_modules.py +1 -1
- scitex/_mcp_tools/__init__.py +2 -0
- scitex/_mcp_tools/verify.py +256 -0
- scitex/cli/main.py +2 -0
- scitex/cli/verify.py +476 -0
- scitex/dev/plt/__init__.py +1 -1
- 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/io/_load.py +8 -1
- scitex/io/_save.py +12 -0
- scitex/session/README.md +2 -2
- scitex/session/__init__.py +1 -0
- scitex/session/_decorator.py +57 -33
- scitex/session/_lifecycle/__init__.py +23 -0
- scitex/session/_lifecycle/_close.py +225 -0
- scitex/session/_lifecycle/_config.py +112 -0
- scitex/session/_lifecycle/_matplotlib.py +83 -0
- scitex/session/_lifecycle/_start.py +246 -0
- scitex/session/_lifecycle/_utils.py +186 -0
- scitex/session/_manager.py +40 -3
- scitex/session/template.py +1 -1
- scitex/template/_templates/plt.py +1 -1
- scitex/template/_templates/session.py +1 -1
- scitex/verify/README.md +312 -0
- scitex/verify/__init__.py +212 -0
- scitex/verify/_chain.py +369 -0
- scitex/verify/_db.py +600 -0
- scitex/verify/_hash.py +187 -0
- scitex/verify/_integration.py +127 -0
- scitex/verify/_rerun.py +253 -0
- scitex/verify/_tracker.py +330 -0
- scitex/verify/_visualize.py +48 -0
- scitex/verify/_viz/__init__.py +56 -0
- scitex/verify/_viz/_colors.py +84 -0
- scitex/verify/_viz/_format.py +302 -0
- scitex/verify/_viz/_json.py +192 -0
- scitex/verify/_viz/_mermaid.py +440 -0
- scitex/verify/_viz/_plotly.py +193 -0
- scitex/verify/_viz/_templates.py +246 -0
- scitex/verify/_viz/_utils.py +56 -0
- {scitex-2.16.1.dist-info → scitex-2.17.0.dist-info}/METADATA +1 -1
- {scitex-2.16.1.dist-info → scitex-2.17.0.dist-info}/RECORD +47 -23
- scitex/session/_lifecycle.py +0 -827
- {scitex-2.16.1.dist-info → scitex-2.17.0.dist-info}/WHEEL +0 -0
- {scitex-2.16.1.dist-info → scitex-2.17.0.dist-info}/entry_points.txt +0 -0
- {scitex-2.16.1.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
|