The repo already enforces that every served route appears in docs/api-inventory.json, and the guides endpoints did not - so the parity test was failing. They are now listed with auth, params and a working example each, beside knowledgebase because the two get read for the same reason. docs/GUIDES.md covers what the inventory cannot: the block schema and why it is blocks rather than HTML, the id rule that makes a tick rememberable, what the server refuses and why validation happens at the door, the working-copy / revision / published-pointer split that stops a live procedure changing under somebody following it, and a worked example of creating and publishing a guide from a script. It also states plainly what does not exist yet - no importer, orphaned images never pruned - and what converting existing documents will actually be like: structured HTML and Markdown map onto block types nearly one for one, while a plain-text runbook lands as paragraphs and needs marking up by hand afterwards. Generated ids have to stay stable across re-imports, or every re-import resets readers' progress. Guides moves to position 8 in the sidebar, beside Slides, rather than down among the reference lists in Information. A guide is something somebody goes and does something with, not a catalogue they look things up in.
238 lines
8.9 KiB
Markdown
238 lines
8.9 KiB
Markdown
# Guides: writing procedures in ShopDB, and generating them from elsewhere
|
|
|
|
A guide is a step-by-step procedure stored in ShopDB: images, working
|
|
checklists, version history, and printable output. This is the reference for the
|
|
data model and the API, aimed at somebody generating guides programmatically -
|
|
from a script, an importer, or a model.
|
|
|
|
Not to be confused with the **knowledgebase** plugin, which is a link registry:
|
|
a description, a URL and keywords pointing at a document held somewhere else.
|
|
That one references documents; this one holds them. They stay separate, and
|
|
share only a tag vocabulary and the global search box.
|
|
|
|
---
|
|
|
|
## 1. The shape of a guide
|
|
|
|
A guide is an ordered list of typed **blocks**, stored as JSON. Not HTML, and
|
|
that is load-bearing:
|
|
|
|
- a tickable step needs a **stable id** to be remembered between visits
|
|
- **dark mode** needs semantic markup, not pasted colours
|
|
- **print** needs to know where a step begins so it does not split one across a
|
|
page
|
|
|
|
Free-form HTML gives none of those, and pasted styling fights the theme.
|
|
|
|
### Block types
|
|
|
|
| type | Fields | Notes |
|
|
|---|---|---|
|
|
| `heading` | `text` | Section heading |
|
|
| `paragraph` | `text` | Body text |
|
|
| `step` | `title`, `text` | Numbers itself by position among steps |
|
|
| `checklist` | `items[]` | The tickable one. Items need their own ids |
|
|
| `bullets` | `items[]` | Unordered list |
|
|
| `numbers` | `items[]` | Ordered list |
|
|
| `callout` | `text`, `title`, `variant` | `variant`: `info`, `warning`, `success` |
|
|
| `code` | `text`, `language`, `caption` | Never autocorrected; wraps rather than clips |
|
|
| `image` | `url`, `alt`, `caption` | `url` comes from the image upload endpoint |
|
|
| `table` | `header[]`, `rows[][]` | Simple grid |
|
|
|
|
Every block needs:
|
|
|
|
```json
|
|
{ "id": "b1", "type": "paragraph", "text": "..." }
|
|
```
|
|
|
|
**Ids must be unique and stable.** They are what a reader's tick is stored
|
|
against, so a save without them is refused, and changing one loses that item's
|
|
progress. Checklist items carry their own ids for the same reason:
|
|
|
|
```json
|
|
{ "id": "b4", "type": "checklist",
|
|
"items": [ { "id": "i1", "text": "Confirm the Verified date" } ] }
|
|
```
|
|
|
|
An unknown block type renders as nothing rather than an error, so a guide
|
|
written against a newer version stays readable on an older one.
|
|
|
|
### Inline text
|
|
|
|
Block text supports a deliberately small subset: `**bold**`, `*italic*`,
|
|
`` `code` ``, `[links](https://example.com)`, and line breaks. Nothing else -
|
|
no fonts, sizes or colours. Emphasis is semantic and the theme decides how it
|
|
looks, which is what makes dark mode work and keeps a guide written next year
|
|
matching one written today.
|
|
|
|
Text is escaped **before** formatting is applied, so no markup in the source can
|
|
reach the DOM. `javascript:` and `data:` links are dropped.
|
|
|
|
---
|
|
|
|
## 2. Creating a guide from a script
|
|
|
|
Every endpoint is listed in `docs/api-inventory.json`; this is the workflow.
|
|
|
|
```bash
|
|
TOK=$(curl -s -X POST http://localhost:5001/api/auth/login \
|
|
-H 'Content-Type: application/json' \
|
|
-d '{"username":"...","password":"..."}' | jq -r .data.access_token)
|
|
|
|
# 1. Create - always starts as a draft
|
|
curl -X POST http://localhost:5001/api/guides \
|
|
-H "Authorization: Bearer $TOK" -H 'Content-Type: application/json' \
|
|
-d '{
|
|
"title": "Replace a part marker controller",
|
|
"summary": "Restoring patterns and settings after a swap",
|
|
"tags": "partmarker, backups",
|
|
"blocks": [
|
|
{"id":"b1","type":"paragraph","text":"The new unit arrives **empty**."},
|
|
{"id":"b2","type":"callout","variant":"warning","title":"Before you start",
|
|
"text":"Check the marking head."},
|
|
{"id":"b3","type":"checklist","items":[
|
|
{"id":"i1","text":"Copy the snapshot path"},
|
|
{"id":"i2","text":"Confirm the pattern count"}]},
|
|
{"id":"b4","type":"code","language":"powershell",
|
|
"text":"Backup-PartMarker.ps1 -Force"}
|
|
]
|
|
}'
|
|
|
|
# 2. Publish - readers see nothing until this happens
|
|
curl -X POST http://localhost:5001/api/guides/12/publish \
|
|
-H "Authorization: Bearer $TOK"
|
|
```
|
|
|
|
A scoped API token works the same way as a user JWT.
|
|
|
|
### What the server rejects
|
|
|
|
Validation happens at the door rather than at render time, because a malformed
|
|
guide stored is a page that fails every time somebody opens it, long after the
|
|
client bug that caused it:
|
|
|
|
- a block with no `id`, or a duplicate `id`
|
|
- an unknown block `type` (the error lists the known ones)
|
|
- a checklist item with no `id`
|
|
- more than 500 blocks, or more than 2 MB of blocks
|
|
- text longer than 20,000 characters in one block
|
|
|
|
Errors name the offending block by position: `block 3: needs a stable id`.
|
|
|
|
---
|
|
|
|
## 3. Drafts, versions and publishing
|
|
|
|
Three separate things, and the distinction is what stops a live procedure
|
|
changing under somebody following it:
|
|
|
|
| | What it is |
|
|
|---|---|
|
|
| **Working copy** | The `guides` row. What the editor loads and saves |
|
|
| **Revisions** | Immutable snapshots in `guiderevisions`, written when content changes |
|
|
| **Published pointer** | Which revision READERS get |
|
|
|
|
- `PUT /api/guides/<id>` saves the working copy and writes a revision **only if
|
|
the content hash changed**. An autosave that changed nothing records nothing,
|
|
so the history stays readable. The response carries `saved: false`.
|
|
- The hash covers title, summary, tags **and** blocks - renaming or retagging is
|
|
a change a reader notices.
|
|
- `POST /api/guides/<id>/publish` **promotes an existing revision**. It does not
|
|
snapshot, so a publish cannot ship unsaved edits nobody reviewed.
|
|
- `POST /api/guides/<id>/restore/<revisionid>` brings old content back as a
|
|
**new** revision. History is never rewritten.
|
|
- Drafts are invisible to readers, to `GET /api/guides/<slug>` (404, not 403),
|
|
and to global search.
|
|
|
|
---
|
|
|
|
## 4. Images
|
|
|
|
```bash
|
|
curl -X POST http://localhost:5001/api/guides/images \
|
|
-H "Authorization: Bearer $TOK" -F file=@screenshot.png -F guideid=12
|
|
```
|
|
|
|
Returns `{"url": "/api/guides/images/5", ...}` for use as an image block's
|
|
`url`. PNG, JPEG, GIF and WebP, up to 8 MB. `guideid` is optional because an
|
|
image is pasted before a new guide has an id.
|
|
|
|
Bytes live in the database, not on disk: a row travels with a database backup,
|
|
whereas a file under `instance/` has been lost to a container rebuild on this
|
|
deployment before.
|
|
|
|
**Known gap:** images uploaded to a guide that is then abandoned are never
|
|
pruned.
|
|
|
|
---
|
|
|
|
## 5. Proofing
|
|
|
|
The editor runs checks continuously and warns rather than blocking. They exist
|
|
because none of these are spelling mistakes:
|
|
|
|
- a **smart quote or long dash in a code block** - autocorrect does this, the
|
|
command then fails, and it still looks right to the author (one-click fix)
|
|
- an empty checklist item, which reads as a step somebody forgot to write
|
|
- a heading with nothing under it, left behind by a reorganise
|
|
- an image with no alt text
|
|
- a leftover `TODO`, `TBD` or `FIXME`
|
|
- no summary or tags, which makes a guide hard to find
|
|
|
|
Publishing with unresolved errors asks for confirmation; it does not refuse. A
|
|
procedure that exists imperfectly beats one nobody wrote.
|
|
|
|
---
|
|
|
|
## 6. Printing
|
|
|
|
Print always renders **light**, whatever the screen theme, because a dark page
|
|
lays a full sheet of toner down and knocks the text out in white.
|
|
|
|
Also on paper: no buttons or progress bar, backgrounds dropped, steps and code
|
|
blocks kept whole across page breaks, code wrapped rather than clipped, and
|
|
checkboxes printed **empty** even where ticked on screen - a printout is a
|
|
worksheet somebody ticks by hand. The footer carries the version and the print
|
|
date, so a stale copy on a noticeboard is spottable.
|
|
|
|
---
|
|
|
|
## 7. Permissions
|
|
|
|
| Permission | Allows |
|
|
|---|---|
|
|
| `guides.view` | Read guides |
|
|
| `guides.create` | Create, and edit your OWN guide while it is unpublished |
|
|
| `guides.edit` | Edit any guide, including published ones |
|
|
| `guides.publish` | Publish or withdraw |
|
|
| `guides.delete` | Delete |
|
|
|
|
The split between `edit` and `publish` is deliberate: a technician can write a
|
|
procedure up while somebody else decides it is fit for the floor.
|
|
|
|
---
|
|
|
|
## 8. Converting existing documents
|
|
|
|
There is no importer yet. When one is written, the practical notes are:
|
|
|
|
- **Structured HTML converts cleanly.** The GE Aerospace KB template's
|
|
`alert alert-warning` divs map to warning callouts, `code-block` to code
|
|
blocks, `step` divs to steps.
|
|
- **Markdown converts well**: headings, fenced code, lists and tables all have
|
|
matching block types.
|
|
- **Plain-text runbooks convert poorly.** ASCII underlines and hand-indented
|
|
lists carry no structure, so everything lands as paragraphs and a person has
|
|
to mark up the steps afterwards. Worth converting anyway - the text is then
|
|
searchable and versioned - but budget the cleanup.
|
|
- Generate ids yourself and keep them stable across re-imports, or every
|
|
re-import resets readers' progress.
|
|
|
|
---
|
|
|
|
## See also
|
|
|
|
- `docs/api-inventory.json` - every endpoint with auth, params and an example
|
|
- `plugins/guides/services/blocks.py` - the validation rules, and why each exists
|
|
- `plugins/guides/services/authoring.py` - the single write path
|