Docs audit fixes: kiosk code drift, PowerShell chains, broken links, leaks
From the Fable/Opus documentation audit (8 confirmed + verified lab-drift the run's session limit had cut short): - HIGH: the lab's kiosk _kiosk_find_item block showed the pre-stage-17 row-id resolver as current; replace with the shipped gagelabtag / numeric-tail resolver, fix the stale 'resolved by row id' prose and the 'stage-7 code is corrected' note. - MED: the badge _external_lookup block used dict-only row access that breaks on a tuple cursor; use the tuple-or-dict form shipped. Split '&&' command chains (fail in PowerShell 5.1) in the lab. - LOW/link: the Windows note's [DEVELOPMENT-SETUP] link dropped the .md and 404'd in four docs; fix. Correct the stage-6a->16a comment and the lab-stage tag range (..16 -> ..17). - Leaks: drop /home/camp path from ADR-006, the internal gitea host from PLUGINS.md. - Windows: add an mklink junction note for the external-plugin symlink dev loop. - CI: prime root to mysql_native_password so pymysql connects to the MySQL 8 service without the cryptography package (and its kit wheel).
This commit is contained in:
10
.github/workflows/ci.yml
vendored
10
.github/workflows/ci.yml
vendored
@@ -75,10 +75,16 @@ jobs:
|
|||||||
python-version: '3.12'
|
python-version: '3.12'
|
||||||
cache: pip
|
cache: pip
|
||||||
- run: pip install -r requirements.txt
|
- run: pip install -r requirements.txt
|
||||||
- name: Force utf8mb4 on the CI database
|
- name: Prime the CI database (utf8mb4 + native auth for pymysql)
|
||||||
|
# MySQL 8 defaults root to caching_sha2_password, which pymysql can
|
||||||
|
# only speak with the 'cryptography' package. Rather than add that
|
||||||
|
# dependency (and its offline wheel), switch root to native auth here
|
||||||
|
# via the mysql CLI, so the app's pymysql connections work as-is.
|
||||||
run: |
|
run: |
|
||||||
mysql -h 127.0.0.1 -uroot -proot -e \
|
mysql -h 127.0.0.1 -uroot -proot -e \
|
||||||
"ALTER DATABASE shopdb_ci CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
|
"ALTER DATABASE shopdb_ci CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; \
|
||||||
|
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'root'; \
|
||||||
|
FLUSH PRIVILEGES;"
|
||||||
- name: Fresh core upgrade from empty
|
- name: Fresh core upgrade from empty
|
||||||
run: flask db upgrade
|
run: flask db upgrade
|
||||||
- name: Install every bundled plugin (runs its chain)
|
- name: Install every bundled plugin (runs its chain)
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ is required for v1 (pip distribution is deferred to v2 per ADR-003).
|
|||||||
> **Windows / VS Code:** command examples use the Linux venv path
|
> **Windows / VS Code:** command examples use the Linux venv path
|
||||||
> `venv/bin/python`; on Windows use `venv\Scripts\python` and
|
> `venv/bin/python`; on Windows use `venv\Scripts\python` and
|
||||||
> `$env:FLASK_APP="shopdb"` (not `export`). Full Windows onboarding:
|
> `$env:FLASK_APP="shopdb"` (not `export`). Full Windows onboarding:
|
||||||
> [DEVELOPMENT-SETUP](DEVELOPMENT-SETUP).
|
> [DEVELOPMENT-SETUP](DEVELOPMENT-SETUP.md).
|
||||||
|
|
||||||
|
|
||||||
If you have not written a plugin before, start with
|
If you have not written a plugin before, start with
|
||||||
@@ -77,6 +77,11 @@ git clone https://github.com/ge-aero/wjsf-shipping.git
|
|||||||
cd shopdb-flask
|
cd shopdb-flask
|
||||||
ln -s ../../wjsf-shipping plugins/shipping
|
ln -s ../../wjsf-shipping plugins/shipping
|
||||||
# (use an absolute path if you prefer: ln -s "$(pwd)/../wjsf-shipping" plugins/shipping)
|
# (use an absolute path if you prefer: ln -s "$(pwd)/../wjsf-shipping" plugins/shipping)
|
||||||
|
#
|
||||||
|
# Windows: use a directory junction instead of ln -s. In an ADMIN prompt
|
||||||
|
# (or with Developer Mode on) from the shopdb-flask dir:
|
||||||
|
# mklink /D plugins\shipping ..\..\wjsf-shipping
|
||||||
|
# The plugin loader treats a junction the same as a real directory.
|
||||||
|
|
||||||
# 3. Set up the framework as usual.
|
# 3. Set up the framework as usual.
|
||||||
python3 -m venv venv
|
python3 -m venv venv
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ way it does by walking the shipped code of the exemplar plugin.
|
|||||||
> **Windows / VS Code:** command examples use the Linux venv path
|
> **Windows / VS Code:** command examples use the Linux venv path
|
||||||
> `venv/bin/python`; on Windows use `venv\Scripts\python` and
|
> `venv/bin/python`; on Windows use `venv\Scripts\python` and
|
||||||
> `$env:FLASK_APP="shopdb"` (not `export`). Full Windows onboarding:
|
> `$env:FLASK_APP="shopdb"` (not `export`). Full Windows onboarding:
|
||||||
> [DEVELOPMENT-SETUP](DEVELOPMENT-SETUP).
|
> [DEVELOPMENT-SETUP](DEVELOPMENT-SETUP.md).
|
||||||
|
|
||||||
`measuringtools` was chosen as the exemplar on purpose. It is the first plugin
|
`measuringtools` was chosen as the exemplar on purpose. It is the first plugin
|
||||||
built after the framework matured (ADR-005 scoped it; ADR-008 changed how plugin
|
built after the framework matured (ADR-005 scoped it; ADR-008 changed how plugin
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ decision records: `docs/proposals/printedparts-plugin.md`.
|
|||||||
> **Windows / VS Code:** command examples below use the Linux venv path
|
> **Windows / VS Code:** command examples below use the Linux venv path
|
||||||
> `venv/bin/python`; on Windows use `venv\Scripts\python` and
|
> `venv/bin/python`; on Windows use `venv\Scripts\python` and
|
||||||
> `$env:FLASK_APP="shopdb"` (not `export`). Full Windows onboarding:
|
> `$env:FLASK_APP="shopdb"` (not `export`). Full Windows onboarding:
|
||||||
> [DEVELOPMENT-SETUP](DEVELOPMENT-SETUP).
|
> [DEVELOPMENT-SETUP](DEVELOPMENT-SETUP.md).
|
||||||
|
|
||||||
|
|
||||||
Know before you start
|
Know before you start
|
||||||
@@ -262,7 +262,7 @@ def downgrade():
|
|||||||
See it work:
|
See it work:
|
||||||
|
|
||||||
```
|
```
|
||||||
flask plugin install printedparts && flask plugin enable printedparts
|
flask plugin install printedparts; flask plugin enable printedparts
|
||||||
mysql> SHOW TABLES LIKE 'printed%'; -- both tables
|
mysql> SHOW TABLES LIKE 'printed%'; -- both tables
|
||||||
mysql> SELECT * FROM alembic_version_printedparts; -- printedparts0001baseline
|
mysql> SELECT * FROM alembic_version_printedparts; -- printedparts0001baseline
|
||||||
flask plugin upgrade-all -- printedparts: ok
|
flask plugin upgrade-all -- printedparts: ok
|
||||||
@@ -299,7 +299,7 @@ printedparts_bp = Blueprint('printedparts', __name__)
|
|||||||
|
|
||||||
|
|
||||||
@printedparts_bp.route('/items', methods=['GET'])
|
@printedparts_bp.route('/items', methods=['GET'])
|
||||||
@jwt_required(optional=True) # stage 6a tightens this to view-gated
|
@jwt_required(optional=True) # stage 16a tightens this to view-gated
|
||||||
def list_items():
|
def list_items():
|
||||||
"""List printed items, paginated; search + low-stock filter."""
|
"""List printed items, paginated; search + low-stock filter."""
|
||||||
page, per_page = get_pagination_params(request)
|
page, per_page = get_pagination_params(request)
|
||||||
@@ -629,10 +629,11 @@ def _external_lookup(kind, digits):
|
|||||||
f'WHERE {column} = %s', (digits,))
|
f'WHERE {column} = %s', (digits,))
|
||||||
row = cursor.fetchone()
|
row = cursor.fetchone()
|
||||||
if row:
|
if row:
|
||||||
sso = str(row['SSO'])
|
# pymysql may return a tuple or a dict cursor - handle both.
|
||||||
name = f"{(row['First_Name'] or '').strip()} " \
|
sso = str(row[0] if not isinstance(row, dict) else row['SSO'])
|
||||||
f"{(row['Last_Name'] or '').strip()}".strip()
|
first = row[1] if not isinstance(row, dict) else row['First_Name']
|
||||||
return sso, name
|
last = row[2] if not isinstance(row, dict) else row['Last_Name']
|
||||||
|
return sso, f"{(first or '').strip()} {(last or '').strip()}".strip()
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception('HR directory lookup failed for %s %s', kind, digits)
|
logger.exception('HR directory lookup failed for %s %s', kind, digits)
|
||||||
finally:
|
finally:
|
||||||
@@ -798,17 +799,25 @@ catch you (below).
|
|||||||
def _kiosk_find_item(itemcode):
|
def _kiosk_find_item(itemcode):
|
||||||
"""Resolve a scanned or typed code to an active item.
|
"""Resolve a scanned or typed code to an active item.
|
||||||
|
|
||||||
Accepts the full code (WJRP0042) or bare digits from the touch keypad -
|
Matches the internal code OR the gage-lab tag exactly; bare keypad
|
||||||
the digits in a minted code ARE the row id, so id lookup keeps working
|
digits match the numeric tail of EITHER identifier, and only when
|
||||||
even for labels printed under an older prefix."""
|
exactly one active item matches (see stage 17)."""
|
||||||
itemcode = (itemcode or '').strip()
|
scanned = (itemcode or '').strip().upper()
|
||||||
item = PrintedItem.query.filter(
|
item = PrintedItem.query.filter(
|
||||||
PrintedItem.itemcode == itemcode,
|
or_(PrintedItem.itemcode == scanned,
|
||||||
|
PrintedItem.gagelabtag == scanned),
|
||||||
PrintedItem.isactive == True).first()
|
PrintedItem.isactive == True).first()
|
||||||
if not item and itemcode.isdigit():
|
if not item and scanned.isdigit():
|
||||||
candidate = db.session.get(PrintedItem, int(itemcode))
|
wanted = int(scanned)
|
||||||
if candidate and candidate.isactive:
|
matches = []
|
||||||
item = candidate
|
for candidate in PrintedItem.query.filter_by(isactive=True).all():
|
||||||
|
for value in (candidate.itemcode, candidate.gagelabtag):
|
||||||
|
tail = ''.join(ch for ch in (value or '') if ch.isdigit())
|
||||||
|
if tail and int(tail) == wanted:
|
||||||
|
matches.append(candidate)
|
||||||
|
break
|
||||||
|
if len(matches) == 1:
|
||||||
|
item = matches[0]
|
||||||
return item
|
return item
|
||||||
|
|
||||||
|
|
||||||
@@ -920,7 +929,8 @@ function onWedgeEnter() {
|
|||||||
```
|
```
|
||||||
|
|
||||||
Manual fallbacks use the TouchKeypad: badge entry is digits (an SSO), and
|
Manual fallbacks use the TouchKeypad: badge entry is digits (an SSO), and
|
||||||
item entry is bare digits resolved by row id server-side - no alphanumeric
|
item entry is bare digits matched server-side against the numeric tail of
|
||||||
|
either the internal code or the gage-lab tag (unique match only) - no alphanumeric
|
||||||
on-screen keyboard needed. Success screen auto-resets after a few seconds.
|
on-screen keyboard needed. Success screen auto-resets after a few seconds.
|
||||||
Full component (~250 lines) at the tag.
|
Full component (~250 lines) at the tag.
|
||||||
|
|
||||||
@@ -1093,7 +1103,7 @@ failure. Summaries here; complete diffs at the tags.
|
|||||||
hit the deny policy. Lesson: anything resolving PEOPLE must honor the
|
hit the deny policy. Lesson: anything resolving PEOPLE must honor the
|
||||||
site's directory mode (the stage-5 code above is the corrected version).
|
site's directory mode (the stage-5 code above is the corrected version).
|
||||||
- **16 - touchscreen findings** (`lab-stage-16`): the focus-steal guard and
|
- **16 - touchscreen findings** (`lab-stage-16`): the focus-steal guard and
|
||||||
keypad-driven manual entry (the stage-7 code above is the corrected
|
keypad-driven manual entry (the kiosk resolver above is the stage-17
|
||||||
version).
|
version).
|
||||||
|
|
||||||
- **17 - the gage-lab asset tag** (`lab-stage-17`): the field team assigns
|
- **17 - the gage-lab asset tag** (`lab-stage-17`): the field team assigns
|
||||||
@@ -1134,11 +1144,12 @@ for a contributor:
|
|||||||
2. **Build in stage-sized commits** exactly as this lab does - each commit a
|
2. **Build in stage-sized commits** exactly as this lab does - each commit a
|
||||||
working checkpoint with its tests. Subject line: short, plain English,
|
working checkpoint with its tests. Subject line: short, plain English,
|
||||||
present tense ("printedparts stage 5: the ledger"); body says WHY.
|
present tense ("printedparts stage 5: the ledger"); body says WHY.
|
||||||
3. **Before every push**, the same three gates CI runs:
|
3. **Before every push**, run the three gates CI runs (in VS Code: the
|
||||||
```bash
|
**Check: naming + tests + build** task). By hand in PowerShell:
|
||||||
bash scripts/check-naming-and-style.sh
|
```powershell
|
||||||
venv/bin/python -m pytest tests/ -q
|
venv\Scripts\python -m pytest tests/ -q
|
||||||
cd frontend && npx vitest run && npm run build
|
cd frontend; npx vitest run; npm run build; cd ..
|
||||||
|
bash scripts/check-naming-and-style.sh # naming - runs via Git Bash
|
||||||
```
|
```
|
||||||
4. **Push your branch and open a Pull Request** against `main`:
|
4. **Push your branch and open a Pull Request** against `main`:
|
||||||
```bash
|
```bash
|
||||||
@@ -1179,4 +1190,4 @@ for a contributor:
|
|||||||
| List/Detail master templates | `PrintersList.vue`, `PrinterDetail.vue` |
|
| List/Detail master templates | `PrintersList.vue`, `PrinterDetail.vue` |
|
||||||
| Reports hook + CSV | `plugins/warranty/` + `shopdb/core/api/reports.py` |
|
| Reports hook + CSV | `plugins/warranty/` + `shopdb/core/api/reports.py` |
|
||||||
| Permissions declaration | `plugins/usb/plugin.py::get_permissions` |
|
| Permissions declaration | `plugins/usb/plugin.py::get_permissions` |
|
||||||
| The finished plugin itself | branch `feat/printedparts-plugin`, tags `lab-stage-01..16` |
|
| The finished plugin itself | branch `feat/printedparts-plugin`, tags `lab-stage-01..17` |
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ For the full hook reference, see [PLUGIN-HOOKS.md](PLUGIN-HOOKS.md).
|
|||||||
> **Windows / VS Code:** command examples below use the Linux venv path
|
> **Windows / VS Code:** command examples below use the Linux venv path
|
||||||
> `venv/bin/python`; on Windows use `venv\Scripts\python` and
|
> `venv/bin/python`; on Windows use `venv\Scripts\python` and
|
||||||
> `$env:FLASK_APP="shopdb"` (not `export`). Full Windows onboarding:
|
> `$env:FLASK_APP="shopdb"` (not `export`). Full Windows onboarding:
|
||||||
> [DEVELOPMENT-SETUP](DEVELOPMENT-SETUP).
|
> [DEVELOPMENT-SETUP](DEVELOPMENT-SETUP.md).
|
||||||
|
|
||||||
For the architectural decisions behind the contract, see [docs/adr/](../docs/adr/).
|
For the architectural decisions behind the contract, see [docs/adr/](../docs/adr/).
|
||||||
|
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ which revisions each plugin has applied in `migrations_applied`.
|
|||||||
|
|
||||||
For sister-site plugins (per [ADR-003](adr/ADR-003-plugin-distribution.md)):
|
For sister-site plugins (per [ADR-003](adr/ADR-003-plugin-distribution.md)):
|
||||||
|
|
||||||
- Plugin lives in its own git repo: `gitea.proudtech.net/<your-site>/<pluginname>`
|
- Plugin lives in its own git repo: `<git-host>/<your-site>/<pluginname>`
|
||||||
- Adopting site clones or symlinks into their `<repo>/plugins/<name>/`
|
- Adopting site clones or symlinks into their `<repo>/plugins/<name>/`
|
||||||
- Plugin manifest declares `core_version` range matching the framework version they target
|
- Plugin manifest declares `core_version` range matching the framework version they target
|
||||||
- Plugin readme explains: what it tracks, who maintains it, where to file issues
|
- Plugin readme explains: what it tracks, who maintains it, where to file issues
|
||||||
|
|||||||
@@ -145,4 +145,4 @@ Migration path:
|
|||||||
- `shopdb/plugins/base.py` (`get_collector_schema` + `apply_collector_payload` hooks)
|
- `shopdb/plugins/base.py` (`get_collector_schema` + `apply_collector_payload` hooks)
|
||||||
- ADR-001 (asset model the collectors target)
|
- ADR-001 (asset model the collectors target)
|
||||||
- ADR-002 (collector schema is part of plugin contract; changes to the hook signature are major bumps)
|
- ADR-002 (collector schema is part of plugin contract; changes to the hook signature are major bumps)
|
||||||
- The PXE project (`/home/camp/projects/pxe/`) which feeds the computers collector
|
- The PXE project (the PXE imaging project) which feeds the computers collector
|
||||||
|
|||||||
Reference in New Issue
Block a user