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.
Files changed (74) hide show
  1. slcli/__init__.py +1 -0
  2. slcli/__main__.py +23 -0
  3. slcli/_version.py +4 -0
  4. slcli/asset_click.py +1289 -0
  5. slcli/cli_formatters.py +218 -0
  6. slcli/cli_utils.py +504 -0
  7. slcli/comment_click.py +602 -0
  8. slcli/completion_click.py +418 -0
  9. slcli/config.py +81 -0
  10. slcli/config_click.py +498 -0
  11. slcli/dff_click.py +979 -0
  12. slcli/dff_decorators.py +24 -0
  13. slcli/example_click.py +404 -0
  14. slcli/example_loader.py +274 -0
  15. slcli/example_provisioner.py +2777 -0
  16. slcli/examples/README.md +134 -0
  17. slcli/examples/_schema/schema-v1.0.json +169 -0
  18. slcli/examples/demo-complete-workflow/README.md +323 -0
  19. slcli/examples/demo-complete-workflow/config.yaml +638 -0
  20. slcli/examples/demo-test-plans/README.md +132 -0
  21. slcli/examples/demo-test-plans/config.yaml +154 -0
  22. slcli/examples/exercise-5-1-parametric-insights/README.md +101 -0
  23. slcli/examples/exercise-5-1-parametric-insights/config.yaml +1589 -0
  24. slcli/examples/exercise-7-1-test-plans/README.md +93 -0
  25. slcli/examples/exercise-7-1-test-plans/config.yaml +323 -0
  26. slcli/examples/spec-compliance-notebooks/README.md +140 -0
  27. slcli/examples/spec-compliance-notebooks/config.yaml +112 -0
  28. slcli/examples/spec-compliance-notebooks/notebooks/SpecAnalysis_ComplianceCalculation.ipynb +1553 -0
  29. slcli/examples/spec-compliance-notebooks/notebooks/SpecComplianceCalculation.ipynb +1577 -0
  30. slcli/examples/spec-compliance-notebooks/notebooks/SpecfileExtractionAndIngestion.ipynb +912 -0
  31. slcli/examples/spec-compliance-notebooks/spec_template.xlsx +0 -0
  32. slcli/feed_click.py +892 -0
  33. slcli/file_click.py +932 -0
  34. slcli/function_click.py +1400 -0
  35. slcli/function_templates.py +85 -0
  36. slcli/main.py +406 -0
  37. slcli/mcp_click.py +269 -0
  38. slcli/mcp_server.py +748 -0
  39. slcli/notebook_click.py +1770 -0
  40. slcli/platform.py +345 -0
  41. slcli/policy_click.py +679 -0
  42. slcli/policy_utils.py +411 -0
  43. slcli/profiles.py +411 -0
  44. slcli/response_handlers.py +359 -0
  45. slcli/routine_click.py +763 -0
  46. slcli/skill_click.py +253 -0
  47. slcli/skills/slcli/SKILL.md +713 -0
  48. slcli/skills/slcli/references/analysis-recipes.md +474 -0
  49. slcli/skills/slcli/references/filtering.md +236 -0
  50. slcli/skills/systemlink-webapp/SKILL.md +744 -0
  51. slcli/skills/systemlink-webapp/references/deployment.md +123 -0
  52. slcli/skills/systemlink-webapp/references/nimble-angular.md +380 -0
  53. slcli/skills/systemlink-webapp/references/systemlink-services.md +192 -0
  54. slcli/ssl_trust.py +93 -0
  55. slcli/system_click.py +2216 -0
  56. slcli/table_utils.py +124 -0
  57. slcli/tag_click.py +794 -0
  58. slcli/templates_click.py +599 -0
  59. slcli/testmonitor_click.py +1667 -0
  60. slcli/universal_handlers.py +305 -0
  61. slcli/user_click.py +1218 -0
  62. slcli/utils.py +832 -0
  63. slcli/web_editor.py +295 -0
  64. slcli/webapp_click.py +981 -0
  65. slcli/workflow_preview.py +287 -0
  66. slcli/workflows_click.py +988 -0
  67. slcli/workitem_click.py +2258 -0
  68. slcli/workspace_click.py +576 -0
  69. slcli/workspace_utils.py +206 -0
  70. systemlink_cli-1.3.1.dist-info/METADATA +20 -0
  71. systemlink_cli-1.3.1.dist-info/RECORD +74 -0
  72. systemlink_cli-1.3.1.dist-info/WHEEL +4 -0
  73. systemlink_cli-1.3.1.dist-info/entry_points.txt +7 -0
  74. systemlink_cli-1.3.1.dist-info/licenses/LICENSE +21 -0
@@ -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())