systemlink-cli 1.7.0__tar.gz → 1.8.0__tar.gz

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 (79) hide show
  1. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/PKG-INFO +1 -1
  2. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/pyproject.toml +1 -1
  3. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/_version.py +1 -1
  4. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/skill_click.py +3 -3
  5. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/skills/slcli/SKILL.md +1 -1
  6. systemlink_cli-1.8.0/slcli/skills/systemlink-notebook/SKILL.md +310 -0
  7. systemlink_cli-1.8.0/slcli/skills/systemlink-notebook/references/interfaces.md +82 -0
  8. systemlink_cli-1.8.0/slcli/skills/systemlink-notebook/references/notebook-patterns.md +252 -0
  9. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/LICENSE +0 -0
  10. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/dff-editor/editor.js +0 -0
  11. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/dff-editor/index.html +0 -0
  12. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/__init__.py +0 -0
  13. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/__main__.py +0 -0
  14. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/asset_click.py +0 -0
  15. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/cli_formatters.py +0 -0
  16. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/cli_utils.py +0 -0
  17. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/comment_click.py +0 -0
  18. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/completion_click.py +0 -0
  19. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/config.py +0 -0
  20. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/config_click.py +0 -0
  21. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/dff_click.py +0 -0
  22. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/dff_decorators.py +0 -0
  23. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/example_click.py +0 -0
  24. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/example_loader.py +0 -0
  25. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/example_provisioner.py +0 -0
  26. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/examples/README.md +0 -0
  27. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/examples/_schema/schema-v1.0.json +0 -0
  28. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/examples/demo-complete-workflow/README.md +0 -0
  29. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/examples/demo-complete-workflow/config.yaml +0 -0
  30. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/examples/demo-test-plans/README.md +0 -0
  31. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/examples/demo-test-plans/config.yaml +0 -0
  32. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/examples/exercise-5-1-parametric-insights/README.md +0 -0
  33. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/examples/exercise-5-1-parametric-insights/config.yaml +0 -0
  34. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/examples/exercise-7-1-test-plans/README.md +0 -0
  35. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/examples/exercise-7-1-test-plans/config.yaml +0 -0
  36. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/examples/spec-compliance-notebooks/README.md +0 -0
  37. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/examples/spec-compliance-notebooks/config.yaml +0 -0
  38. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/examples/spec-compliance-notebooks/notebooks/SpecAnalysis_ComplianceCalculation.ipynb +0 -0
  39. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/examples/spec-compliance-notebooks/notebooks/SpecComplianceCalculation.ipynb +0 -0
  40. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/examples/spec-compliance-notebooks/notebooks/SpecfileExtractionAndIngestion.ipynb +0 -0
  41. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/examples/spec-compliance-notebooks/spec_template.xlsx +0 -0
  42. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/feed_click.py +0 -0
  43. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/file_click.py +0 -0
  44. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/function_click.py +0 -0
  45. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/function_templates.py +0 -0
  46. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/main.py +0 -0
  47. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/mcp_click.py +0 -0
  48. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/mcp_server.py +0 -0
  49. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/notebook_click.py +0 -0
  50. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/platform.py +0 -0
  51. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/policy_click.py +0 -0
  52. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/policy_utils.py +0 -0
  53. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/profiles.py +0 -0
  54. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/response_handlers.py +0 -0
  55. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/rich_output.py +0 -0
  56. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/routine_click.py +0 -0
  57. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/skills/slcli/references/analysis-recipes.md +0 -0
  58. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/skills/slcli/references/filtering.md +0 -0
  59. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/skills/systemlink-webapp/SKILL.md +0 -0
  60. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/skills/systemlink-webapp/references/deployment.md +0 -0
  61. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/skills/systemlink-webapp/references/layout-patterns.md +0 -0
  62. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/skills/systemlink-webapp/references/nimble-angular.md +0 -0
  63. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/skills/systemlink-webapp/references/systemlink-services.md +0 -0
  64. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/ssl_trust.py +0 -0
  65. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/system_click.py +0 -0
  66. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/table_utils.py +0 -0
  67. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/tag_click.py +0 -0
  68. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/templates_click.py +0 -0
  69. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/testmonitor_click.py +0 -0
  70. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/universal_handlers.py +0 -0
  71. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/user_click.py +0 -0
  72. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/utils.py +0 -0
  73. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/web_editor.py +0 -0
  74. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/webapp_click.py +0 -0
  75. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/workflow_preview.py +0 -0
  76. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/workflows_click.py +0 -0
  77. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/workitem_click.py +0 -0
  78. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/workspace_click.py +0 -0
  79. {systemlink_cli-1.7.0 → systemlink_cli-1.8.0}/slcli/workspace_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: systemlink-cli
3
- Version: 1.7.0
3
+ Version: 1.8.0
4
4
  Summary: SystemLink Integrator CLI - cross-platform CLI for SystemLink workflows and templates.
5
5
  License-File: LICENSE
6
6
  Author: Fred Visser
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "systemlink-cli"
3
- version = "1.7.0"
3
+ version = "1.8.0"
4
4
  description = "SystemLink Integrator CLI - cross-platform CLI for SystemLink workflows and templates."
5
5
  authors = ["Fred Visser <fred.visser@emerson.com>"]
6
6
  packages = [{ include = "slcli" }]
@@ -1,4 +1,4 @@
1
1
  """Version information for slcli."""
2
2
 
3
3
  # This file is auto-generated. Do not edit manually.
4
- __version__ = "1.7.0"
4
+ __version__ = "1.8.0"
@@ -11,7 +11,7 @@ import questionary
11
11
  from .utils import ExitCodes
12
12
 
13
13
  SKILL_NAME = "slcli"
14
- SKILL_CHOICES = ["slcli", "systemlink-webapp"]
14
+ SKILL_CHOICES = ["slcli", "systemlink-webapp", "systemlink-notebook"]
15
15
 
16
16
  # Mapping of client name -> (personal skills dir, project subdir relative to repo root)
17
17
  # personal dir uses Path.home() so it's always resolved at call time via _personal_dir().
@@ -178,7 +178,7 @@ def register_skill_commands(cli: Any) -> None:
178
178
  "-k",
179
179
  type=click.Choice(SKILL_CHOICES + ["all"], case_sensitive=False),
180
180
  default=None,
181
- help="Skill to install (slcli, systemlink-webapp, or all).",
181
+ help="Skill to install (slcli, systemlink-webapp, systemlink-notebook, or all).",
182
182
  )
183
183
  @click.option(
184
184
  "--client",
@@ -207,7 +207,7 @@ def register_skill_commands(cli: Any) -> None:
207
207
  """Install agent skills for AI coding assistants.
208
208
 
209
209
  Copies bundled skills into the skills directory of one or more AI clients.
210
- Available skills: slcli, systemlink-webapp.
210
+ Available skills: slcli, systemlink-webapp, systemlink-notebook.
211
211
  Supported clients and their skill locations:
212
212
 
213
213
  \b
@@ -703,7 +703,7 @@ generated `.nipkg`, and emits a thin `manifest.json` with `schemaVersion`, `nipk
703
703
  Install bundled skills for supported AI clients.
704
704
 
705
705
  ```bash
706
- slcli skill install --skill [slcli|systemlink-webapp|all] --client [agents|claude|all] --scope [personal|project|both]
706
+ slcli skill install --skill [slcli|systemlink-webapp|systemlink-notebook|all] --client [agents|claude|all] --scope [personal|project|both]
707
707
  ```
708
708
 
709
709
  Client paths:
@@ -0,0 +1,310 @@
1
+ ---
2
+ name: systemlink-notebook
3
+ description: >-
4
+ Create, structure, and deploy Jupyter Notebooks for NI SystemLink.
5
+ Use when the user asks to create a notebook, report, or analysis that runs on
6
+ SystemLink — including Systems Grid reports, test data analysis notebooks,
7
+ asset reports, scheduled notebooks, or any notebook that uses scrapbook (sb.glue)
8
+ to return results. Covers parameter cells, systemlink metadata, output formats,
9
+ papermill integration, and deployment via slcli.
10
+ argument-hint: 'Describe the notebook purpose and what data it should report on'
11
+ ---
12
+
13
+ # SystemLink Notebook Creation
14
+
15
+ ## When to Use
16
+
17
+ - Creating a new Jupyter Notebook that will run on SystemLink
18
+ - Adding parameters or outputs to a notebook for the SystemLink Notebook Execution Service
19
+ - Building a Systems Grid column report
20
+ - Creating a test data analysis notebook
21
+ - Deploying a notebook to SystemLink via `slcli`
22
+
23
+ ## Notebook Structure
24
+
25
+ Every SystemLink notebook follows this cell pattern:
26
+
27
+ 1. **Imports (markdown)** — describe dependencies
28
+ 2. **Imports (code)** — import modules
29
+ 3. **Parameters (markdown)** — describe each parameter
30
+ 4. **Parameters (code)** — declare parameter variables with defaults *(requires special metadata)*
31
+ 5. **Logic (markdown + code)** — one or more pairs of markdown/code cells
32
+ 6. **Output (markdown)** — describe the output format
33
+ 7. **Output (code)** — format results and call `sb.glue('result', result)`
34
+ 8. **Instructions (markdown)** — how to use the notebook in SystemLink UI
35
+
36
+ ## Parameters Cell Metadata
37
+
38
+ The parameters cell is the most critical part. It **must** have this metadata structure
39
+ in the cell's `.metadata` field for SystemLink to recognize the parameters and outputs.
40
+
41
+ ```json
42
+ {
43
+ "papermill": {
44
+ "parameters": {
45
+ "param_name": "default_value"
46
+ }
47
+ },
48
+ "systemlink": {
49
+ "namespaces": [],
50
+ "outputs": [
51
+ {
52
+ "display_name": "Human Readable Output Name",
53
+ "id": "output_snake_case_id",
54
+ "type": "data_frame"
55
+ }
56
+ ],
57
+ "parameters": [
58
+ {
59
+ "display_name": "Human Readable Param Name",
60
+ "id": "param_name",
61
+ "type": "string"
62
+ }
63
+ ],
64
+ "version": 2
65
+ },
66
+ "tags": ["parameters"]
67
+ }
68
+ ```
69
+
70
+ ### Key rules for parameter metadata
71
+
72
+ - `papermill.parameters` keys **must** match the variable names in the code cell
73
+ - `systemlink.parameters[].id` **must** match the variable names in the code cell
74
+ - `systemlink.outputs[].id` **must** match the key used in the result dict passed to `sb.glue`
75
+ - `tags: ["parameters"]` is **required** — this is how papermill identifies the cell
76
+ - `systemlink.version` must be `2` for most notebooks, or `1` for **Work Item Automations**
77
+ - Supported parameter types: `string`, `integer`, `float`, `boolean`, `string[]`
78
+ - Supported output types: `data_frame`, `scalar`, `string`, `string[]`
79
+
80
+ ### Work Item Automations pattern
81
+
82
+ Notebooks with the **Work Item Automations** interface receive work item IDs as a
83
+ **list** (`string[]`), not a comma-separated string. Key differences from other notebooks:
84
+
85
+ - `systemlink.version` must be `1`
86
+ - `work_item_ids` parameter type is `"string[]"` and its default value in
87
+ `papermill.parameters` is `[]` (empty list)
88
+ - The Python variable must also default to a list: `work_item_ids = []`
89
+ - Do NOT split by comma — the parameter is already a list of strings
90
+
91
+ Example parameters cell metadata for Work Item Automations:
92
+ ```json
93
+ {
94
+ "papermill": {
95
+ "parameters": {
96
+ "work_item_ids": []
97
+ }
98
+ },
99
+ "systemlink": {
100
+ "outputs": [...],
101
+ "parameters": [
102
+ {
103
+ "display_name": "Work item IDs",
104
+ "id": "work_item_ids",
105
+ "type": "string[]"
106
+ }
107
+ ],
108
+ "version": 1
109
+ },
110
+ "tags": ["parameters"]
111
+ }
112
+ ```
113
+
114
+ ## Output Format (sb.glue)
115
+
116
+ All notebooks must use `scrapbook` to return results. The output cell should:
117
+
118
+ ```python
119
+ import scrapbook as sb
120
+
121
+ # For data_frame outputs:
122
+ result = [{
123
+ "display_name": "Human Readable Name",
124
+ "id": "output_id", # Must match systemlink.outputs[].id
125
+ "type": "data_frame",
126
+ "data": {
127
+ "columns": ["column1", "column2"],
128
+ "values": df.reset_index().values.tolist()
129
+ }
130
+ }]
131
+ sb.glue('result', result)
132
+
133
+ # For scalar outputs:
134
+ result = [{
135
+ "display_name": "Count",
136
+ "id": "count_output",
137
+ "type": "scalar",
138
+ "data": 42
139
+ }]
140
+ sb.glue('result', result)
141
+ ```
142
+
143
+ ### Systems Grid reports
144
+
145
+ When the notebook is used as a Systems Grid column, the `data_frame` output
146
+ **must** include `minion id` as the first column. This maps rows to systems.
147
+
148
+ ```python
149
+ df_dict = {
150
+ 'columns': ['minion id', 'your column name'],
151
+ 'values': df.reset_index().values.tolist() # index = system IDs
152
+ }
153
+ ```
154
+
155
+ ## Common Imports
156
+
157
+ ```python
158
+ # Always needed
159
+ import pandas as pd
160
+ import scrapbook as sb
161
+
162
+ # Systems queries
163
+ from systemlink.clients.nisysmgmt.api.systems_api import SystemsApi
164
+ from systemlink.clients.nisysmgmt.models.query_systems_request import QuerySystemsRequest
165
+
166
+ # Test results
167
+ from nisystemlink.clients.testmonitor import TestMonitorClient
168
+ from nisystemlink.clients.core import HttpConfigurationManager
169
+
170
+ # Assets
171
+ from nisystemlink.clients.assetmanagement import AssetManagementClient
172
+
173
+ # Direct HTTP (when no typed client exists)
174
+ import requests
175
+ from nisystemlink.clients.core import HttpConfigurationManager
176
+ config = HttpConfigurationManager.get_configuration()
177
+ base_url = config.server_uri.rstrip("/")
178
+ headers = {"x-ni-api-key": config.api_keys[0]}
179
+ ```
180
+
181
+ ## Systems Query Pattern
182
+
183
+ The `SystemsApi` uses a projection/filter pattern for querying:
184
+
185
+ ```python
186
+ api = SystemsApi()
187
+
188
+ # Projection selects which fields to return
189
+ projection = 'new(id, alias, state, packages.data["ni-daqmx"].displayversion)'
190
+
191
+ # Filter selects which systems to include
192
+ filter = '!string.IsNullOrEmpty(id) && packages.data.keys.Contains("ni-daqmx")'
193
+
194
+ query = QuerySystemsRequest(skip=0, projection=projection, filter=filter)
195
+ query_result = api.get_systems_by_query(query=query)
196
+ data = await query_result
197
+ ```
198
+
199
+ ### Common filter expressions
200
+
201
+ | Filter | Description |
202
+ |--------|-------------|
203
+ | `!string.IsNullOrEmpty(id)` | All systems (default/fallback) |
204
+ | `connected.data.state == "CONNECTED"` | Connected systems only |
205
+ | `packages.data.keys.Contains("pkg")` | Systems with a specific package |
206
+ | `grains.data.kernel == "Windows"` | Windows systems |
207
+
208
+ ## Notebook Interfaces
209
+
210
+ When deploying, set the notebook interface to tell SystemLink how it will be used.
211
+ See [interfaces reference](./references/interfaces.md) for the full list.
212
+
213
+ Common interfaces:
214
+ - **Systems Grid** — report that adds a column to the Systems management grid
215
+ - **Test Data Analysis** — analysis of test monitor results
216
+ - **Periodic Execution** — scheduled recurring notebook
217
+ - **Work Item Automations** — triggered by work item state changes
218
+
219
+ ## Setting Cell Metadata
220
+
221
+ The VS Code notebook editor tools do **not** persist custom cell metadata (like
222
+ `papermill`, `systemlink`, `tags`). You must write cell metadata directly into
223
+ the `.ipynb` JSON file using a Python script:
224
+
225
+ ```python
226
+ import json
227
+
228
+ with open('notebook.ipynb') as f:
229
+ nb = json.load(f)
230
+
231
+ # Find the parameters cell and set its metadata
232
+ for cell in nb['cells']:
233
+ src = ''.join(cell.get('source', []))
234
+ if 'parameters' in src and cell['cell_type'] == 'code':
235
+ cell['metadata'] = {
236
+ "papermill": {
237
+ "parameters": {
238
+ "param_name": "default_value"
239
+ }
240
+ },
241
+ "systemlink": {
242
+ "namespaces": [],
243
+ "outputs": [...],
244
+ "parameters": [...],
245
+ "version": 2
246
+ },
247
+ "tags": ["parameters"],
248
+ "trusted": False,
249
+ "editable": True,
250
+ "slideshow": {"slide_type": ""}
251
+ }
252
+ break
253
+
254
+ with open('notebook.ipynb', 'w') as f:
255
+ json.dump(nb, f, indent=1)
256
+ f.write('\n')
257
+ ```
258
+
259
+ **Critical:** Without this metadata, SystemLink will not display parameters or
260
+ outputs in the UI. Always verify metadata was written by inspecting the raw JSON.
261
+
262
+ Also ensure the notebook-level `kernelspec` metadata is set. Papermill requires
263
+ this to execute the notebook — without it you get
264
+ `ValueError: No kernel name found in notebook`. The VS Code notebook editor may
265
+ clear this when editing cells. Always verify and restore if needed:
266
+
267
+ ```python
268
+ nb['metadata']['kernelspec'] = {
269
+ 'display_name': 'Python 3',
270
+ 'language': 'python',
271
+ 'name': 'python3'
272
+ }
273
+ ```
274
+
275
+ ## Deployment
276
+
277
+ Deploy via `slcli`. **Always read the user's configured workspace** from `slcli info`
278
+ and pass it with `--workspace` so the notebook lands in the correct workspace:
279
+
280
+ ```bash
281
+ # 1. Read the configured workspace name from the active slcli profile
282
+ # (look for the "Workspace" row in `slcli info` output)
283
+
284
+ # Create a new notebook with interface and workspace set at creation time (preferred)
285
+ slcli notebook manage create --file notebook.ipynb --name "My Notebook" --interface "Systems Grid" --workspace "<WORKSPACE_NAME>"
286
+
287
+ # Update content of an existing notebook
288
+ slcli notebook manage update --id <NOTEBOOK_ID> --content notebook.ipynb
289
+
290
+ # Update interface or metadata in place when needed
291
+ slcli notebook manage update --id <NOTEBOOK_ID> --interface "Systems Grid"
292
+
293
+ # Delete and re-create only as a fallback if the server rejects the update
294
+ slcli notebook manage delete --id <NOTEBOOK_ID> --yes
295
+ slcli notebook manage create --file notebook.ipynb --name "My Notebook" --interface "Systems Grid" --workspace "<WORKSPACE_NAME>"
296
+ ```
297
+
298
+ **Important:** Prefer setting `--interface` at creation time, but use
299
+ `slcli notebook manage update --interface ...` for in-place interface changes when
300
+ you are updating an existing notebook. Delete and re-create only as a fallback if
301
+ the server rejects the update.
302
+
303
+ **Important:** Always determine the target workspace by running `slcli info` and
304
+ reading the `Workspace` field from the active profile. Do not assume "Default" or
305
+ prompt the user unless `slcli info` shows no workspace configured.
306
+
307
+ ## Example
308
+
309
+ See [notebook-patterns.md](./references/notebook-patterns.md) for a complete
310
+ annotated example based on the NI PackageVersionExample pattern.
@@ -0,0 +1,82 @@
1
+ # Notebook Interfaces
2
+
3
+ When deploying a notebook to SystemLink, you can assign an **interface** that tells
4
+ SystemLink how the notebook will be used. This affects how parameters are presented
5
+ and how the notebook is triggered.
6
+
7
+ Set the interface during creation or update:
8
+
9
+ ```bash
10
+ slcli notebook manage update --id <ID> --interface "Systems Grid"
11
+ ```
12
+
13
+ Prefer `create --interface ...` when you are creating a new notebook. Use
14
+ `update --interface ...` for in-place interface changes on an existing notebook.
15
+ Delete and re-create only if the server rejects the update.
16
+
17
+ ## Available Interfaces
18
+
19
+ | Interface | Use Case |
20
+ | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
21
+ | **Assets Grid** | Report that adds a column to the Assets management grid |
22
+ | **Data Table Analysis** | Analysis of data table contents |
23
+ | **Data Space Analysis** | Analysis of data space contents |
24
+ | **File Analysis** | Analysis of uploaded files |
25
+ | **Periodic Execution** | Scheduled recurring notebook (e.g. daily/hourly reports) |
26
+ | **Resource Changed Routine** | Triggered when a resource changes (via v1 routines) |
27
+ | **Specification Analysis** | Analysis against specifications/limits |
28
+ | **Systems Grid** | Report that adds a column to the Systems management grid |
29
+ | **Test Data Analysis** | Analysis of test monitor results |
30
+ | **Test Data Extraction** | Extract and transform test data |
31
+ | **Work Item Automations** | Notebooks that can be manually executed on selected work items, or triggered by work item lifecycle events. **Use this for any notebook that acts on work items** (e.g. close, update status, assign). |
32
+ | **Work Item Operations** | Internal operations performed on work items by the system |
33
+ | **Work Item Scheduler** | Scheduling logic for work items |
34
+
35
+ ## Common Interface Patterns
36
+
37
+ ### Systems Grid
38
+
39
+ Parameters typically include:
40
+
41
+ - `group_by` (string) — always support "System"
42
+ - `systems_filter` (string) — filter expression for which systems
43
+ - Domain-specific params (e.g. `package` for package version)
44
+
45
+ Output must be `data_frame` with `minion id` as the first column.
46
+
47
+ ### Test Data Analysis
48
+
49
+ Parameters typically include:
50
+
51
+ - `group_by` (string)
52
+ - `program_name` (string)
53
+ - `status_filter` (string)
54
+ - `systems_filter` (string)
55
+
56
+ ### Periodic Execution
57
+
58
+ No special parameter requirements. Typically uses fixed configuration
59
+ or reads from tags/files. Can be scheduled via routines:
60
+
61
+ ```bash
62
+ slcli routine create --api-version v1 \
63
+ --name "Daily Report" \
64
+ --type SCHEDULED \
65
+ --notebook-id <NOTEBOOK_ID> \
66
+ --schedule '{"startTime":"2026-01-01T00:00:00Z","repeat":"DAY"}'
67
+ ```
68
+
69
+ ### Work Item Automations
70
+
71
+ Use this interface for notebooks that act on selected work items (close, update,
72
+ assign, etc.). The notebook appears in the work items UI and can be manually
73
+ triggered on one or more selected work items.
74
+
75
+ Parameters are injected by the work item system:
76
+
77
+ - `work_item_ids` (string[]) — list of selected work item IDs. Default: `[]`
78
+ - `workspace` (string) — workspace context (optional)
79
+
80
+ **Critical:** `work_item_ids` must be typed as `"string[]"` (not `"string"`),
81
+ default to `[]` in both papermill and the code cell, and `systemlink.version`
82
+ must be `1`. See the systemlink-notebook skill for the full metadata example.
@@ -0,0 +1,252 @@
1
+ # Notebook Patterns
2
+
3
+ Annotated examples of common SystemLink notebook patterns.
4
+
5
+ ---
6
+
7
+ ## Pattern 1: Systems Grid Column Report
8
+
9
+ This is the most common pattern. The notebook queries system data and returns
10
+ a `data_frame` output that the Systems Grid can display as a column.
11
+
12
+ ### Full annotated example (Package Version)
13
+
14
+ **Cell 1 — Imports description (markdown)**
15
+ ```markdown
16
+ ### Imports
17
+ Import Python modules for executing the notebook.
18
+ - Pandas is used for building and handling dataframes.
19
+ - Scrapbook is used for recording data for the Notebook Execution Service.
20
+ - SystemsApi is an NI provided package for communicating with the SystemLink Systems service.
21
+ ```
22
+
23
+ **Cell 2 — Imports (code)**
24
+ ```python
25
+ import pandas as pd
26
+ import scrapbook as sb
27
+
28
+ from systemlink.clients.nisysmgmt.api.systems_api import SystemsApi
29
+ from systemlink.clients.nisysmgmt.models.query_systems_request import QuerySystemsRequest
30
+ ```
31
+
32
+ **Cell 3 — Parameters description (markdown)**
33
+ ```markdown
34
+ ### Parameters
35
+ - `group_by`: The property by which data is grouped. For the data to appear
36
+ as a column in the Systems Grid, we must support 'System' here.
37
+ - `package`: The Package Name of the software to display the version of.
38
+ - `systems_filter`: A filter specifying which systems to query.
39
+ An empty filter matches all systems.
40
+ ```
41
+
42
+ **Cell 4 — Parameters (code, with metadata)**
43
+
44
+ The code cell declares variables with defaults:
45
+ ```python
46
+ group_by = "System"
47
+ package = "ni-daqmx"
48
+ systems_filter = ""
49
+ ```
50
+
51
+ The cell metadata must include:
52
+ ```json
53
+ {
54
+ "papermill": {
55
+ "parameters": {
56
+ "group_by": "System",
57
+ "package": "ni-daqmx",
58
+ "systems_filter": ""
59
+ }
60
+ },
61
+ "systemlink": {
62
+ "namespaces": [],
63
+ "outputs": [
64
+ {
65
+ "display_name": "Package Version",
66
+ "id": "package_version",
67
+ "type": "data_frame"
68
+ }
69
+ ],
70
+ "parameters": [
71
+ {
72
+ "display_name": "Group by",
73
+ "id": "group_by",
74
+ "type": "string"
75
+ },
76
+ {
77
+ "display_name": "Package",
78
+ "id": "package",
79
+ "type": "string"
80
+ },
81
+ {
82
+ "display_name": "Systems Filter",
83
+ "id": "systems_filter",
84
+ "type": "string"
85
+ }
86
+ ],
87
+ "version": 2
88
+ },
89
+ "tags": ["parameters"]
90
+ }
91
+ ```
92
+
93
+ **Cell 5 — Query description (markdown)**
94
+ ```markdown
95
+ ### Query for Systems with the specified package and get the Package Version
96
+ ```
97
+
98
+ **Cell 6 — Query (code)**
99
+ ```python
100
+ api = SystemsApi()
101
+
102
+ projection = f'new(id, packages.data["{package}"].displayversion, packages.data["{package}"].version)'
103
+ filter = (systems_filter or "!string.IsNullOrEmpty(id)") + f' && packages.data.keys.Contains("{package}")'
104
+
105
+ query_sys_request = QuerySystemsRequest(skip=0, projection=projection, filter=filter)
106
+ query_result = api.get_systems_by_query(query=query_sys_request)
107
+ data = await query_result
108
+ ```
109
+
110
+ **Cell 7 — Dataframe description (markdown)**
111
+ ```markdown
112
+ ### Extract Package data from query results and create pandas dataframe
113
+ ```
114
+
115
+ **Cell 8 — Dataframe (code)**
116
+ ```python
117
+ pkg_version = { item['id'] : item['displayversion'] for item in data.data }
118
+ df = pd.DataFrame.from_dict(pkg_version, orient='index', columns=['Package Version'])
119
+ df
120
+ ```
121
+
122
+ **Cell 9 — Output description (markdown)**
123
+ ```markdown
124
+ ### Convert dataframe to result format that the Systems Grid can interpret
125
+ ```
126
+
127
+ **Cell 10 — Output with sb.glue (code)**
128
+ ```python
129
+ df_dict = {
130
+ 'columns': ['minion id', 'package version'],
131
+ 'values': df.reset_index().values.tolist()
132
+ }
133
+
134
+ result = [{
135
+ "display_name": "Package Version",
136
+ "id": "package_version",
137
+ "type": "data_frame",
138
+ "data": df_dict
139
+ }]
140
+
141
+ sb.glue('result', result)
142
+ ```
143
+
144
+ **Cell 11 — Usage instructions (markdown)**
145
+ ```markdown
146
+ ### View the output of this report in the Systems Grid
147
+ 1. Upload this notebook to the reports folder in SystemLink Jupyter
148
+ 2. From the Systems page, press the edit grid button
149
+ 3. Press '+ ADD' and select 'Notebook' as the data source
150
+ 4. Select this report, choose 'Package Version' as the output
151
+ 5. Enter the package name and update interval
152
+ 6. Enter a column name and press Done
153
+ ```
154
+
155
+ ---
156
+
157
+ ## Pattern 2: Test Data Analysis
158
+
159
+ Query test results and return a summary. Uses `nisystemlink.clients.testmonitor`.
160
+
161
+ **Parameters cell metadata:**
162
+ ```json
163
+ {
164
+ "papermill": {
165
+ "parameters": {
166
+ "group_by": "System",
167
+ "program_name": "",
168
+ "status_filter": "",
169
+ "systems_filter": ""
170
+ }
171
+ },
172
+ "systemlink": {
173
+ "namespaces": [],
174
+ "outputs": [
175
+ {
176
+ "display_name": "Test Summary",
177
+ "id": "test_summary",
178
+ "type": "data_frame"
179
+ }
180
+ ],
181
+ "parameters": [
182
+ {"display_name": "Group by", "id": "group_by", "type": "string"},
183
+ {"display_name": "Program Name", "id": "program_name", "type": "string"},
184
+ {"display_name": "Status Filter", "id": "status_filter", "type": "string"},
185
+ {"display_name": "Systems Filter", "id": "systems_filter", "type": "string"}
186
+ ],
187
+ "version": 2
188
+ },
189
+ "tags": ["parameters"]
190
+ }
191
+ ```
192
+
193
+ **Query pattern:**
194
+ ```python
195
+ from nisystemlink.clients.testmonitor import TestMonitorClient
196
+ from nisystemlink.clients.core import HttpConfigurationManager
197
+
198
+ config = HttpConfigurationManager.get_configuration()
199
+ client = TestMonitorClient(config)
200
+
201
+ # Use the client to query results with filters
202
+ results = client.get_results(
203
+ filter=f'status.statusType == "{status_filter}"' if status_filter else None,
204
+ take=1000
205
+ )
206
+ ```
207
+
208
+ **Output pattern (same as Pattern 1):**
209
+ ```python
210
+ result = [{
211
+ "display_name": "Test Summary",
212
+ "id": "test_summary",
213
+ "type": "data_frame",
214
+ "data": {
215
+ "columns": ["minion id", "pass rate"],
216
+ "values": df.reset_index().values.tolist()
217
+ }
218
+ }]
219
+ sb.glue('result', result)
220
+ ```
221
+
222
+ ---
223
+
224
+ ## Pattern 3: Scalar Output
225
+
226
+ When the notebook returns a single value instead of a table.
227
+
228
+ **Output metadata:**
229
+ ```json
230
+ {
231
+ "systemlink": {
232
+ "outputs": [
233
+ {
234
+ "display_name": "Total Count",
235
+ "id": "total_count",
236
+ "type": "scalar"
237
+ }
238
+ ]
239
+ }
240
+ }
241
+ ```
242
+
243
+ **Output code:**
244
+ ```python
245
+ result = [{
246
+ "display_name": "Total Count",
247
+ "id": "total_count",
248
+ "type": "scalar",
249
+ "data": len(df)
250
+ }]
251
+ sb.glue('result', result)
252
+ ```
File without changes