Skip to content

Commit cc21d76

Browse files
committed
Update - 2026-02-22 17:09:25
1 parent 3b507f7 commit cc21d76

186 files changed

Lines changed: 2050030 additions & 2 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ Version source of truth:
5757

5858
Detailed instructions were moved to `docs/`:
5959

60-
- `docs/README.md` (docs index)
60+
- `docs/docs-index.md` (docs index)
6161
- `docs/user-guide.md` (full Postman import/use walkthrough)
6262
- `docs/tools.md` (sanitize/version/release/git helpers)
6363
- `docs/release.md` (release process and versioning)

docs/README.md renamed to docs/docs-index.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ This directory contains user documentation for working with the Postman PyPNMApp
77
- `docs/user-guide.md` - End-user setup and Postman import/use instructions.
88
- `docs/tools.md` - Local developer tooling (`sanitize`, `bump_version`, `release`, `git-save`, `git-push`).
99
- `docs/release.md` - Versioning and release workflow (`VERSION` source of truth).
10+
- `docs/visual/index.md` - Generated visual examples catalog (from `visual/`).
11+
- `docs/visual-mkdocs-plan.md` - MkDocs visual catalog source-of-truth plan.
1012

1113
## Quick Start
1214

@@ -18,3 +20,7 @@ This directory contains user documentation for working with the Postman PyPNMApp
1820
- `cat VERSION`
1921
4. Run tests:
2022
- `.venv/bin/python -m pytest -q`
23+
5. Generate visual docs pages:
24+
- `.venv/bin/python tools/docs/build_visual_docs.py`
25+
6. Serve docs locally:
26+
- `.venv/bin/mkdocs serve`

docs/index.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Postman PyPNMApps API Docs
2+
3+
Use this site to browse:
4+
5+
- Postman user setup (`user-guide.md`)
6+
- Repo tooling (`tools.md`)
7+
- Release workflow (`release.md`)
8+
- Generated visual example catalog (`visual/index.md`)
9+
10+
The visual catalog is generated from `visual/`, which is the current source of truth for both example visualizer scripts (`.html`) and sample payloads (`.json`).

docs/tools.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,31 @@ Create the local virtual environment and install dev dependencies:
1010
./install.sh
1111
```
1212

13+
## Visual Docs Generator (MkDocs)
14+
15+
Path:
16+
17+
- `tools/docs/build_visual_docs.py`
18+
19+
Purpose:
20+
21+
- Scans `visual/` for `.html` + `.json` examples
22+
- Generates MkDocs pages under `docs/visual/`
23+
- Generates local preview wrappers under `docs/visual-previews/`
24+
25+
Usage:
26+
27+
```bash
28+
.venv/bin/python tools/docs/build_visual_docs.py
29+
.venv/bin/python tools/docs/build_visual_docs.py --check
30+
.venv/bin/mkdocs serve
31+
```
32+
33+
Notes:
34+
35+
- In this version, `visual/` is the source of truth for both visualizer HTML/script and sample data.
36+
- Preview rendering uses a lightweight Postman API shim and is best-effort.
37+
1338
## Sanitization Tool
1439

1540
Path:

docs/visual-mkdocs-plan.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Visual MkDocs Plan
2+
3+
## Current Source of Truth (This Version)
4+
5+
- `visual/**/*.html` - Postman visualizer scripts/templates
6+
- `visual/**/*.json` - sample response payloads
7+
8+
The MkDocs visual catalog is generated directly from these files.
9+
10+
## Current Implementation (MVP)
11+
12+
- `tools/docs/build_visual_docs.py` scans `visual/`
13+
- Generates pages under `docs/visual/`
14+
- Generates preview wrappers under `docs/visual-previews/`
15+
- Uses a lightweight Postman shim for previews:
16+
- `pm.response.json()`
17+
- `pm.getData(...)`
18+
- `pm.visualizer.set(...)`
19+
20+
## Future Direction (Planned)
21+
22+
Later, the HTML/script source of truth will move into the Postman collection files.
23+
24+
Planned migration path:
25+
26+
1. Keep `visual/*.json` as sample data source
27+
2. Add a collection extractor adapter for visualizer scripts
28+
3. Preserve generated MkDocs page URLs
29+
4. Switch generator input from filesystem HTML to collection-derived scripts
30+
31+
## Commands
32+
33+
```bash
34+
.venv/bin/python tools/docs/build_visual_docs.py
35+
.venv/bin/mkdocs serve
36+
```
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1">
6+
<title>PyPNM / DOCSIS-3.0 / DS-SCQAM-CodeErrorRate</title>
7+
<style>
8+
body {
9+
margin: 0;
10+
padding: 12px;
11+
background: #f8fafc;
12+
color: #111827;
13+
font-family: ui-sans-serif, system-ui, sans-serif;
14+
}
15+
.frame {
16+
background: #ffffff;
17+
border: 1px solid #d1d5db;
18+
border-radius: 8px;
19+
padding: 12px;
20+
}
21+
.error {
22+
white-space: pre-wrap;
23+
color: #fecaca;
24+
background: #450a0a;
25+
border: 1px solid #7f1d1d;
26+
padding: 8px;
27+
border-radius: 6px;
28+
}
29+
.note {
30+
margin-bottom: 8px;
31+
color: #475569;
32+
font-size: 12px;
33+
}
34+
@media (prefers-color-scheme: dark) {
35+
body {
36+
background: #0f172a;
37+
color: #e5e7eb;
38+
}
39+
.frame {
40+
background: #111827;
41+
border-color: #374151;
42+
}
43+
.note {
44+
color: #cbd5e1;
45+
}
46+
}
47+
</style>
48+
<script src="https://cdn.jsdelivr.net/npm/handlebars@4.7.8/dist/handlebars.min.js"></script>
49+
</head>
50+
<body>
51+
<div class="note">Preview uses a lightweight Postman visualizer shim (`pm.response.json`, `pm.getData`, `pm.visualizer.set`).</div>
52+
<div id="app" class="frame"></div>
53+
<script>
54+
(function() {
55+
const sampleData = {"mac_address": "aa:bb:cc:dd:ee:ff", "status": 0, "message": "Successfully retrieved codeword error rate", "results": [{"index": 3, "channel_id": 3, "codeword_totals": {"total_codewords": 801466, "total_errors": 0, "time_elapsed": 5.0, "error_rate": 0.0, "codewords_per_second": 160293.2, "errors_per_second": 0.0}}, {"index": 50, "channel_id": 32, "codeword_totals": {"total_codewords": 801254, "total_errors": 0, "time_elapsed": 5.0, "error_rate": 0.0, "codewords_per_second": 160250.8, "errors_per_second": 0.0}}, {"index": 51, "channel_id": 31, "codeword_totals": {"total_codewords": 803484, "total_errors": 0, "time_elapsed": 5.0, "error_rate": 0.0, "codewords_per_second": 160696.8, "errors_per_second": 0.0}}, {"index": 52, "channel_id": 30, "codeword_totals": {"total_codewords": 805598, "total_errors": 0, "time_elapsed": 5.0, "error_rate": 0.0, "codewords_per_second": 161119.6, "errors_per_second": 0.0}}, {"index": 53, "channel_id": 29, "codeword_totals": {"total_codewords": 799451, "total_errors": 0, "time_elapsed": 5.0, "error_rate": 0.0, "codewords_per_second": 159890.2, "errors_per_second": 0.0}}, {"index": 54, "channel_id": 28, "codeword_totals": {"total_codewords": 799208, "total_errors": 0, "time_elapsed": 5.0, "error_rate": 0.0, "codewords_per_second": 159841.6, "errors_per_second": 0.0}}, {"index": 55, "channel_id": 27, "codeword_totals": {"total_codewords": 798799, "total_errors": 0, "time_elapsed": 5.0, "error_rate": 0.0, "codewords_per_second": 159759.8, "errors_per_second": 0.0}}, {"index": 56, "channel_id": 26, "codeword_totals": {"total_codewords": 798983, "total_errors": 0, "time_elapsed": 5.0, "error_rate": 0.0, "codewords_per_second": 159796.6, "errors_per_second": 0.0}}, {"index": 57, "channel_id": 25, "codeword_totals": {"total_codewords": 798555, "total_errors": 0, "time_elapsed": 5.0, "error_rate": 0.0, "codewords_per_second": 159711.0, "errors_per_second": 0.0}}, {"index": 58, "channel_id": 24, "codeword_totals": {"total_codewords": 765770, "total_errors": 0, "time_elapsed": 5.0, "error_rate": 0.0, "codewords_per_second": 153154.0, "errors_per_second": 0.0}}, {"index": 59, "channel_id": 23, "codeword_totals": {"total_codewords": 674493, "total_errors": 0, "time_elapsed": 5.0, "error_rate": 0.0, "codewords_per_second": 134898.6, "errors_per_second": 0.0}}, {"index": 60, "channel_id": 22, "codeword_totals": {"total_codewords": 664659, "total_errors": 0, "time_elapsed": 5.0, "error_rate": 0.0, "codewords_per_second": 132931.8, "errors_per_second": 0.0}}, {"index": 61, "channel_id": 21, "codeword_totals": {"total_codewords": 658032, "total_errors": 0, "time_elapsed": 5.0, "error_rate": 0.0, "codewords_per_second": 131606.4, "errors_per_second": 0.0}}, {"index": 62, "channel_id": 20, "codeword_totals": {"total_codewords": 663866, "total_errors": 0, "time_elapsed": 5.0, "error_rate": 0.0, "codewords_per_second": 132773.2, "errors_per_second": 0.0}}, {"index": 63, "channel_id": 19, "codeword_totals": {"total_codewords": 666260, "total_errors": 0, "time_elapsed": 5.0, "error_rate": 0.0, "codewords_per_second": 133252.0, "errors_per_second": 0.0}}, {"index": 64, "channel_id": 18, "codeword_totals": {"total_codewords": 667387, "total_errors": 0, "time_elapsed": 5.0, "error_rate": 0.0, "codewords_per_second": 133477.4, "errors_per_second": 0.0}}, {"index": 65, "channel_id": 17, "codeword_totals": {"total_codewords": 659818, "total_errors": 0, "time_elapsed": 5.0, "error_rate": 0.0, "codewords_per_second": 131963.6, "errors_per_second": 0.0}}, {"index": 66, "channel_id": 16, "codeword_totals": {"total_codewords": 663416, "total_errors": 0, "time_elapsed": 5.0, "error_rate": 0.0, "codewords_per_second": 132683.2, "errors_per_second": 0.0}}, {"index": 67, "channel_id": 15, "codeword_totals": {"total_codewords": 775145, "total_errors": 0, "time_elapsed": 5.0, "error_rate": 0.0, "codewords_per_second": 155029.0, "errors_per_second": 0.0}}, {"index": 68, "channel_id": 14, "codeword_totals": {"total_codewords": 793486, "total_errors": 0, "time_elapsed": 5.0, "error_rate": 0.0, "codewords_per_second": 158697.2, "errors_per_second": 0.0}}, {"index": 69, "channel_id": 13, "codeword_totals": {"total_codewords": 794895, "total_errors": 0, "time_elapsed": 5.0, "error_rate": 0.0, "codewords_per_second": 158979.0, "errors_per_second": 0.0}}, {"index": 70, "channel_id": 12, "codeword_totals": {"total_codewords": 792756, "total_errors": 0, "time_elapsed": 5.0, "error_rate": 0.0, "codewords_per_second": 158551.2, "errors_per_second": 0.0}}, {"index": 71, "channel_id": 11, "codeword_totals": {"total_codewords": 796557, "total_errors": 0, "time_elapsed": 5.0, "error_rate": 0.0, "codewords_per_second": 159311.4, "errors_per_second": 0.0}}, {"index": 72, "channel_id": 10, "codeword_totals": {"total_codewords": 793542, "total_errors": 0, "time_elapsed": 5.0, "error_rate": 0.0, "codewords_per_second": 158708.4, "errors_per_second": 0.0}}, {"index": 73, "channel_id": 9, "codeword_totals": {"total_codewords": 799482, "total_errors": 0, "time_elapsed": 5.0, "error_rate": 0.0, "codewords_per_second": 159896.4, "errors_per_second": 0.0}}, {"index": 74, "channel_id": 8, "codeword_totals": {"total_codewords": 799303, "total_errors": 0, "time_elapsed": 5.0, "error_rate": 0.0, "codewords_per_second": 159860.6, "errors_per_second": 0.0}}, {"index": 75, "channel_id": 7, "codeword_totals": {"total_codewords": 801459, "total_errors": 0, "time_elapsed": 5.0, "error_rate": 0.0, "codewords_per_second": 160291.8, "errors_per_second": 0.0}}, {"index": 76, "channel_id": 6, "codeword_totals": {"total_codewords": 801542, "total_errors": 0, "time_elapsed": 5.0, "error_rate": 0.0, "codewords_per_second": 160308.4, "errors_per_second": 0.0}}, {"index": 77, "channel_id": 5, "codeword_totals": {"total_codewords": 796760, "total_errors": 0, "time_elapsed": 5.0, "error_rate": 0.0, "codewords_per_second": 159352.0, "errors_per_second": 0.0}}, {"index": 78, "channel_id": 4, "codeword_totals": {"total_codewords": 795338, "total_errors": 0, "time_elapsed": 5.0, "error_rate": 0.0, "codewords_per_second": 159067.6, "errors_per_second": 0.0}}, {"index": 79, "channel_id": 2, "codeword_totals": {"total_codewords": 795617, "total_errors": 0, "time_elapsed": 5.0, "error_rate": 0.0, "codewords_per_second": 159123.4, "errors_per_second": 0.0}}, {"index": 112, "channel_id": 1, "codeword_totals": {"total_codewords": 799526, "total_errors": 0, "time_elapsed": 5.0, "error_rate": 0.0, "codewords_per_second": 159905.2, "errors_per_second": 0.0}}]};
56+
const visualSource = "// Visualization Script\n// Visualization Script\nvar template = `\n<canvas id=\"myChart\" height=\"100\"></canvas>\n<script src=\"https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.5.0/Chart.min.js\"><\/script> \n<script>\n var ctx = document.getElementById(\"myChart\");\n var myChart = new Chart(ctx, {\n type: \"bar\",\n data: {\n labels: [],\n datasets: [\n {\n label: 'Total Codewords',\n data: [],\n backgroundColor: 'rgba(54, 162, 235, 0.6)',\n borderColor: 'rgba(54, 162, 235, 1)',\n borderWidth: 1\n },\n {\n label: 'Total Errors',\n data: [],\n backgroundColor: 'rgba(255, 99, 132, 0.6)',\n borderColor: 'rgba(255, 99, 132, 1)',\n borderWidth: 1\n }\n ]\n },\n options: {\n legend: { display: true },\n title: {\n display: true,\n text: 'Codeword Error Rate by Channel ID'\n },\n scales: {\n xAxes: [{\n display: true,\n scaleLabel: {\n display: true,\n labelString: 'Channel ID'\n }\n }],\n yAxes: [{\n display: true,\n scaleLabel: {\n display: true,\n labelString: 'Count'\n },\n ticks: {\n beginAtZero: true\n }\n }]\n }\n }\n });\n\n pm.getData(function (err, value) {\n myChart.data.labels = value.channelIds;\n myChart.data.datasets[0].data = value.totalCodewords;\n myChart.data.datasets[1].data = value.totalErrors;\n myChart.update();\n });\n\n<\/script>`;\n\nfunction createPayload() {\n var responseData = pm.response.json();\n var results = responseData.results || [];\n \n var channelIds = [];\n var totalCodewords = [];\n var totalErrors = [];\n \n for (var i = 0; i < results.length; i++) {\n channelIds.push(results[i].channel_id.toString());\n totalCodewords.push(results[i].codeword_totals.total_codewords);\n totalErrors.push(results[i].codeword_totals.total_errors);\n }\n \n return {\n channelIds: channelIds,\n totalCodewords: totalCodewords,\n totalErrors: totalErrors\n };\n}\n\npm.visualizer.set(template, createPayload());";
57+
const app = document.getElementById("app");
58+
let visualizerData = sampleData;
59+
let lastTemplate = null;
60+
61+
function executeRenderedScripts(root) {
62+
const scripts = Array.from(root.querySelectorAll("script"));
63+
for (const oldScript of scripts) {
64+
const newScript = document.createElement("script");
65+
for (const attr of oldScript.attributes) {
66+
newScript.setAttribute(attr.name, attr.value);
67+
}
68+
newScript.textContent = oldScript.textContent;
69+
oldScript.parentNode.replaceChild(newScript, oldScript);
70+
}
71+
}
72+
73+
function renderTemplate(template, data) {
74+
lastTemplate = template;
75+
visualizerData = data == null ? sampleData : data;
76+
try {
77+
if (window.Handlebars && typeof template === "string") {
78+
const compiled = window.Handlebars.compile(template);
79+
app.innerHTML = compiled(visualizerData);
80+
executeRenderedScripts(app);
81+
} else if (typeof template === "string") {
82+
app.innerHTML = template;
83+
executeRenderedScripts(app);
84+
} else {
85+
app.textContent = String(template);
86+
}
87+
} catch (err) {
88+
app.innerHTML = '<div class="error">Template render error\n' + String(err && err.stack || err) + '</div>';
89+
}
90+
}
91+
92+
function makeKVStore() {
93+
const store = Object.create(null);
94+
return {
95+
get: function(key) { return Object.prototype.hasOwnProperty.call(store, key) ? store[key] : undefined; },
96+
set: function(key, value) { store[key] = value; return value; },
97+
unset: function(key) { delete store[key]; }
98+
};
99+
}
100+
101+
window.pm = {
102+
response: {
103+
json: function() { return sampleData; },
104+
text: function() { return JSON.stringify(sampleData); }
105+
},
106+
request: {
107+
body: {
108+
raw: "{}"
109+
}
110+
},
111+
environment: makeKVStore(),
112+
globals: makeKVStore(),
113+
variables: makeKVStore(),
114+
getData: function(cb) {
115+
if (typeof cb === "function") cb(null, visualizerData);
116+
},
117+
visualizer: {
118+
set: function(template, data) {
119+
renderTemplate(template, data);
120+
}
121+
}
122+
};
123+
124+
window.console = window.console || { log(){}, warn(){}, error(){} };
125+
126+
try {
127+
const fn = new Function(visualSource);
128+
fn();
129+
if (!lastTemplate && !app.innerHTML.trim()) {
130+
app.innerHTML = '<div class="error">No visualizer output produced. The script may require unsupported Postman APIs.</div>';
131+
}
132+
} catch (err) {
133+
app.innerHTML = '<div class="error">Script execution error\n' + String(err && err.stack || err) + '</div>';
134+
}
135+
})();
136+
</script>
137+
</body>
138+
</html>

docs/visual-previews/DOCSIS-3.0/DS-SCQAM-Stats.html

Lines changed: 138 additions & 0 deletions
Large diffs are not rendered by default.

docs/visual-previews/DOCSIS-3.0/US-ATDMA-PreEqualization.html

Lines changed: 138 additions & 0 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)