systemlink-cli 1.3.1__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.
- slcli/__init__.py +1 -0
- slcli/__main__.py +23 -0
- slcli/_version.py +4 -0
- slcli/asset_click.py +1289 -0
- slcli/cli_formatters.py +218 -0
- slcli/cli_utils.py +504 -0
- slcli/comment_click.py +602 -0
- slcli/completion_click.py +418 -0
- slcli/config.py +81 -0
- slcli/config_click.py +498 -0
- slcli/dff_click.py +979 -0
- slcli/dff_decorators.py +24 -0
- slcli/example_click.py +404 -0
- slcli/example_loader.py +274 -0
- slcli/example_provisioner.py +2777 -0
- slcli/examples/README.md +134 -0
- slcli/examples/_schema/schema-v1.0.json +169 -0
- slcli/examples/demo-complete-workflow/README.md +323 -0
- slcli/examples/demo-complete-workflow/config.yaml +638 -0
- slcli/examples/demo-test-plans/README.md +132 -0
- slcli/examples/demo-test-plans/config.yaml +154 -0
- slcli/examples/exercise-5-1-parametric-insights/README.md +101 -0
- slcli/examples/exercise-5-1-parametric-insights/config.yaml +1589 -0
- slcli/examples/exercise-7-1-test-plans/README.md +93 -0
- slcli/examples/exercise-7-1-test-plans/config.yaml +323 -0
- slcli/examples/spec-compliance-notebooks/README.md +140 -0
- slcli/examples/spec-compliance-notebooks/config.yaml +112 -0
- slcli/examples/spec-compliance-notebooks/notebooks/SpecAnalysis_ComplianceCalculation.ipynb +1553 -0
- slcli/examples/spec-compliance-notebooks/notebooks/SpecComplianceCalculation.ipynb +1577 -0
- slcli/examples/spec-compliance-notebooks/notebooks/SpecfileExtractionAndIngestion.ipynb +912 -0
- slcli/examples/spec-compliance-notebooks/spec_template.xlsx +0 -0
- slcli/feed_click.py +892 -0
- slcli/file_click.py +932 -0
- slcli/function_click.py +1400 -0
- slcli/function_templates.py +85 -0
- slcli/main.py +406 -0
- slcli/mcp_click.py +269 -0
- slcli/mcp_server.py +748 -0
- slcli/notebook_click.py +1770 -0
- slcli/platform.py +345 -0
- slcli/policy_click.py +679 -0
- slcli/policy_utils.py +411 -0
- slcli/profiles.py +411 -0
- slcli/response_handlers.py +359 -0
- slcli/routine_click.py +763 -0
- slcli/skill_click.py +253 -0
- slcli/skills/slcli/SKILL.md +713 -0
- slcli/skills/slcli/references/analysis-recipes.md +474 -0
- slcli/skills/slcli/references/filtering.md +236 -0
- slcli/skills/systemlink-webapp/SKILL.md +744 -0
- slcli/skills/systemlink-webapp/references/deployment.md +123 -0
- slcli/skills/systemlink-webapp/references/nimble-angular.md +380 -0
- slcli/skills/systemlink-webapp/references/systemlink-services.md +192 -0
- slcli/ssl_trust.py +93 -0
- slcli/system_click.py +2216 -0
- slcli/table_utils.py +124 -0
- slcli/tag_click.py +794 -0
- slcli/templates_click.py +599 -0
- slcli/testmonitor_click.py +1667 -0
- slcli/universal_handlers.py +305 -0
- slcli/user_click.py +1218 -0
- slcli/utils.py +832 -0
- slcli/web_editor.py +295 -0
- slcli/webapp_click.py +981 -0
- slcli/workflow_preview.py +287 -0
- slcli/workflows_click.py +988 -0
- slcli/workitem_click.py +2258 -0
- slcli/workspace_click.py +576 -0
- slcli/workspace_utils.py +206 -0
- systemlink_cli-1.3.1.dist-info/METADATA +20 -0
- systemlink_cli-1.3.1.dist-info/RECORD +74 -0
- systemlink_cli-1.3.1.dist-info/WHEEL +4 -0
- systemlink_cli-1.3.1.dist-info/entry_points.txt +7 -0
- systemlink_cli-1.3.1.dist-info/licenses/LICENSE +21 -0
slcli/cli_formatters.py
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
"""Unified table formatters for all CLI resource types."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from typing import Any, Callable, Dict, List, Optional
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def format_templates_table(template: Dict[str, Any]) -> List[str]:
|
|
8
|
+
"""Format template data for table display."""
|
|
9
|
+
name = template.get("name", "Unknown")
|
|
10
|
+
version = template.get("version", "N/A")
|
|
11
|
+
created = _format_timestamp(template.get("createdTimestamp"))
|
|
12
|
+
status = template.get("status", "Unknown")
|
|
13
|
+
|
|
14
|
+
return [name, version, created, status]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def format_users_table(user: Dict[str, Any]) -> List[str]:
|
|
18
|
+
"""Format user data for table display."""
|
|
19
|
+
username = user.get("username", "Unknown")
|
|
20
|
+
email = user.get("email", "N/A")
|
|
21
|
+
role = user.get("role", "N/A")
|
|
22
|
+
status = "Active" if user.get("enabled", True) else "Disabled"
|
|
23
|
+
|
|
24
|
+
return [username, email, role, status]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def format_workflows_table(workflow: Dict[str, Any]) -> List[str]:
|
|
28
|
+
"""Format workflow data for table display."""
|
|
29
|
+
name = workflow.get("name", "Unknown")
|
|
30
|
+
status = workflow.get("status", "Unknown")
|
|
31
|
+
last_run = _format_timestamp(workflow.get("lastRunTimestamp"))
|
|
32
|
+
duration = _format_duration(workflow.get("lastRunDuration"))
|
|
33
|
+
|
|
34
|
+
return [name, status, last_run, duration]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def format_notebooks_table(notebook: Dict[str, Any]) -> List[str]:
|
|
38
|
+
"""Format notebook data for table display."""
|
|
39
|
+
name = notebook.get("name", "Unknown")
|
|
40
|
+
notebook_type = notebook.get("type", "Unknown")
|
|
41
|
+
modified = _format_timestamp(notebook.get("modifiedTimestamp"))
|
|
42
|
+
size = _format_file_size(notebook.get("size", 0))
|
|
43
|
+
|
|
44
|
+
return [name, notebook_type, modified, size]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def format_workspaces_table(workspace: Dict[str, Any]) -> List[str]:
|
|
48
|
+
"""Format workspace data for table display."""
|
|
49
|
+
name = workspace.get("name", "Unknown")
|
|
50
|
+
workspace_type = workspace.get("type", "Unknown")
|
|
51
|
+
file_count = str(workspace.get("fileCount", 0))
|
|
52
|
+
modified = _format_timestamp(workspace.get("modifiedTimestamp"))
|
|
53
|
+
|
|
54
|
+
return [name, workspace_type, file_count, modified]
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def format_dff_files_table(file_info: Dict[str, Any]) -> List[str]:
|
|
58
|
+
"""Format DFF file data for table display."""
|
|
59
|
+
name = file_info.get("name", "Unknown")
|
|
60
|
+
size = _format_file_size(file_info.get("size", 0))
|
|
61
|
+
modified = _format_timestamp(file_info.get("modifiedTimestamp"))
|
|
62
|
+
file_type = file_info.get("type", "file").title()
|
|
63
|
+
|
|
64
|
+
return [name, size, modified, file_type]
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def format_dff_data_table(data_info: Dict[str, Any]) -> List[str]:
|
|
68
|
+
"""Format DFF data entry for table display."""
|
|
69
|
+
id_val = data_info.get("id", "Unknown")
|
|
70
|
+
name = data_info.get("name", "N/A")
|
|
71
|
+
created = _format_timestamp(data_info.get("createdTimestamp"))
|
|
72
|
+
size = _format_file_size(data_info.get("size", 0))
|
|
73
|
+
|
|
74
|
+
return [id_val, name, created, size]
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def format_tags_table(tag: Dict[str, Any]) -> List[str]:
|
|
78
|
+
"""Format tag data for table display."""
|
|
79
|
+
name = tag.get("name", "Unknown")
|
|
80
|
+
tag_type = tag.get("type", "Unknown")
|
|
81
|
+
count = str(tag.get("dataCount", 0))
|
|
82
|
+
created = _format_timestamp(tag.get("createdTimestamp"))
|
|
83
|
+
|
|
84
|
+
return [name, tag_type, count, created]
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def format_systems_table(system: Dict[str, Any]) -> List[str]:
|
|
88
|
+
"""Format system data for table display."""
|
|
89
|
+
name = system.get("name", "Unknown")
|
|
90
|
+
status = system.get("status", "Unknown")
|
|
91
|
+
ip_address = system.get("ipAddress", "N/A")
|
|
92
|
+
last_seen = _format_timestamp(system.get("lastSeenTimestamp"))
|
|
93
|
+
|
|
94
|
+
return [name, status, ip_address, last_seen]
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def format_assets_table(asset: Dict[str, Any]) -> List[str]:
|
|
98
|
+
"""Format asset data for table display."""
|
|
99
|
+
name = asset.get("name", "Unknown")
|
|
100
|
+
asset_type = asset.get("type", "Unknown")
|
|
101
|
+
location = asset.get("location", "N/A")
|
|
102
|
+
status = asset.get("status", "Unknown")
|
|
103
|
+
|
|
104
|
+
return [name, asset_type, location, status]
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _format_timestamp(timestamp: Any) -> str:
|
|
108
|
+
"""Format timestamp for table display."""
|
|
109
|
+
if not timestamp:
|
|
110
|
+
return "N/A"
|
|
111
|
+
|
|
112
|
+
try:
|
|
113
|
+
if isinstance(timestamp, str):
|
|
114
|
+
# Handle ISO format timestamps
|
|
115
|
+
dt = datetime.fromisoformat(timestamp.replace("Z", "+00:00"))
|
|
116
|
+
elif isinstance(timestamp, (int, float)):
|
|
117
|
+
# Handle Unix timestamps
|
|
118
|
+
dt = datetime.fromtimestamp(timestamp)
|
|
119
|
+
else:
|
|
120
|
+
return "N/A"
|
|
121
|
+
|
|
122
|
+
# Return relative time if recent, otherwise date
|
|
123
|
+
now = datetime.now()
|
|
124
|
+
diff = now - dt.replace(tzinfo=None)
|
|
125
|
+
|
|
126
|
+
if diff.days == 0:
|
|
127
|
+
return dt.strftime("%H:%M")
|
|
128
|
+
elif diff.days < 7:
|
|
129
|
+
return f"{diff.days}d ago"
|
|
130
|
+
else:
|
|
131
|
+
return dt.strftime("%Y-%m-%d")
|
|
132
|
+
|
|
133
|
+
except (ValueError, TypeError, AttributeError):
|
|
134
|
+
return "N/A"
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _format_duration(duration: Any) -> str:
|
|
138
|
+
"""Format duration for table display."""
|
|
139
|
+
if not duration:
|
|
140
|
+
return "N/A"
|
|
141
|
+
|
|
142
|
+
try:
|
|
143
|
+
if isinstance(duration, str):
|
|
144
|
+
return duration
|
|
145
|
+
elif isinstance(duration, (int, float)):
|
|
146
|
+
# Convert seconds to readable format
|
|
147
|
+
if duration < 60:
|
|
148
|
+
return f"{duration:.1f}s"
|
|
149
|
+
elif duration < 3600:
|
|
150
|
+
return f"{duration // 60:.0f}m {duration % 60:.0f}s"
|
|
151
|
+
else:
|
|
152
|
+
hours = duration // 3600
|
|
153
|
+
minutes = (duration % 3600) // 60
|
|
154
|
+
return f"{hours:.0f}h {minutes:.0f}m"
|
|
155
|
+
else:
|
|
156
|
+
return "N/A"
|
|
157
|
+
except (ValueError, TypeError):
|
|
158
|
+
return "N/A"
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _format_file_size(size: Any) -> str:
|
|
162
|
+
"""Format file size for table display."""
|
|
163
|
+
if not size or size == 0:
|
|
164
|
+
return "0 B"
|
|
165
|
+
|
|
166
|
+
try:
|
|
167
|
+
size = float(size)
|
|
168
|
+
units = ["B", "KB", "MB", "GB", "TB"]
|
|
169
|
+
unit_index = 0
|
|
170
|
+
|
|
171
|
+
while size >= 1024 and unit_index < len(units) - 1:
|
|
172
|
+
size /= 1024
|
|
173
|
+
unit_index += 1
|
|
174
|
+
|
|
175
|
+
if unit_index == 0:
|
|
176
|
+
return f"{size:.0f} {units[unit_index]}"
|
|
177
|
+
else:
|
|
178
|
+
return f"{size:.1f} {units[unit_index]}"
|
|
179
|
+
|
|
180
|
+
except (ValueError, TypeError):
|
|
181
|
+
return "N/A"
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
# Formatter mapping for dynamic lookup
|
|
185
|
+
FORMATTER_MAP: Dict[str, Callable[[Dict[str, Any]], List[str]]] = {
|
|
186
|
+
"template": format_templates_table,
|
|
187
|
+
"templates": format_templates_table,
|
|
188
|
+
"user": format_users_table,
|
|
189
|
+
"users": format_users_table,
|
|
190
|
+
"workflow": format_workflows_table,
|
|
191
|
+
"workflows": format_workflows_table,
|
|
192
|
+
"notebook": format_notebooks_table,
|
|
193
|
+
"notebooks": format_notebooks_table,
|
|
194
|
+
"workspace": format_workspaces_table,
|
|
195
|
+
"workspaces": format_workspaces_table,
|
|
196
|
+
"file": format_dff_files_table,
|
|
197
|
+
"files": format_dff_files_table,
|
|
198
|
+
"data": format_dff_data_table,
|
|
199
|
+
"tag": format_tags_table,
|
|
200
|
+
"tags": format_tags_table,
|
|
201
|
+
"system": format_systems_table,
|
|
202
|
+
"systems": format_systems_table,
|
|
203
|
+
"asset": format_assets_table,
|
|
204
|
+
"assets": format_assets_table,
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def get_formatter(resource_type: str) -> Optional[Callable[[Dict[str, Any]], List[str]]]:
|
|
209
|
+
"""Get the appropriate formatter function for a resource type.
|
|
210
|
+
|
|
211
|
+
Args:
|
|
212
|
+
resource_type: The resource type key (e.g., 'workflow', 'user').
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
A formatter function that accepts a mapping and returns a list of strings, or
|
|
216
|
+
None if no formatter exists for the provided resource type.
|
|
217
|
+
"""
|
|
218
|
+
return FORMATTER_MAP.get(resource_type.lower())
|