feat: LibreOffice Calc extension, ODS library, AI description, audit design
Calc extension (pkg/calc/):
- Python UNO ProtocolHandler with 8 toolbar commands
- SiloClient HTTP client adapted from FreeCAD workbench
- Pull BOM/Project: populates sheets with 28-col format, hidden property
columns, row hash tracking, auto project tagging
- Push: row classification, create/update items, conflict detection
- Completion wizard: 3-step category/description/fields with PN conflict
resolution dialog
- OpenRouter AI integration: generate standardized descriptions from seller
text, configurable model/instructions, review dialog
- Settings: JSON persistence, env var fallbacks, OpenRouter fields
- 31 unit tests (no UNO/network required)
Go ODS library (internal/ods/):
- Pure Go ODS read/write (ZIP of XML, no headless LibreOffice)
- Writer, reader, 10 round-trip tests
Server ODS endpoints (internal/api/ods.go):
- GET /api/items/export.ods, template.ods, POST import.ods
- GET /api/items/{pn}/bom/export.ods
- GET /api/projects/{code}/sheet.ods
- POST /api/sheets/diff
Documentation:
- docs/CALC_EXTENSION.md: extension progress report
- docs/COMPONENT_AUDIT.md: web audit tool design with weighted scoring,
assembly computed fields, batch AI assistance plan
This commit is contained in:
255
docs/CALC_EXTENSION.md
Normal file
255
docs/CALC_EXTENSION.md
Normal file
@@ -0,0 +1,255 @@
|
||||
# LibreOffice Calc Extension
|
||||
|
||||
**Last Updated:** 2026-02-01
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
The Silo Calc extension (`silo-calc.oxt`) is a LibreOffice Calc add-on that
|
||||
connects project BOM spreadsheets directly to the Silo parts database.
|
||||
Engineers work in their familiar spreadsheet environment while Silo handles
|
||||
part number generation, revision tracking, and data synchronization.
|
||||
|
||||
The extension is a Python UNO component packaged as an `.oxt` file. It uses
|
||||
only stdlib (`urllib`, `json`, `ssl`) -- no pip dependencies. The same
|
||||
`SiloClient` pattern and auth flow from the FreeCAD workbench is reused.
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
Engineer's workstation Silo server (silod)
|
||||
+--------------------------+ +------------------------+
|
||||
| LibreOffice Calc | | Go API server |
|
||||
| +----------------------+ | REST | +--------------------+ |
|
||||
| | Silo Extension (.oxt)| <--------> | | ODS endpoints | |
|
||||
| | - Pull/Push BOM | | API | | (internal/ods) | |
|
||||
| | - Completion Wizard | | | +--------------------+ |
|
||||
| | - AI Describe | | | | |
|
||||
| +----------------------+ | | +--------------------+ |
|
||||
| UNO API | cells | | | PostgreSQL | |
|
||||
| +----------------------+ | | +--------------------+ |
|
||||
| | Project Workbook | | +------------------------+
|
||||
| | ~/projects/sheets/ | |
|
||||
| | 3DX10/3DX10.ods | |
|
||||
+--------------------------+
|
||||
|
||||
Extension also calls OpenRouter AI API directly for
|
||||
description generation (does not go through silod).
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Extension Structure
|
||||
|
||||
```
|
||||
pkg/calc/
|
||||
META-INF/manifest.xml Extension manifest
|
||||
description.xml Extension metadata (id, version, publisher)
|
||||
description/description_en.txt English description
|
||||
Addons.xcu Toolbar + menu registration
|
||||
ProtocolHandler.xcu Dispatch protocol registration
|
||||
silo_calc_component.py UNO DispatchProvider entry point
|
||||
pythonpath/silo_calc/
|
||||
__init__.py
|
||||
ai_client.py OpenRouter API client
|
||||
client.py SiloClient (HTTP, auth, SSL)
|
||||
completion_wizard.py 3-step new item wizard
|
||||
dialogs.py UNO dialog toolkit wrappers
|
||||
project_files.py Local project file management
|
||||
pull.py Sheet population from server
|
||||
push.py Sheet changes back to server
|
||||
settings.py JSON settings (~/.config/silo/calc-settings.json)
|
||||
sheet_format.py Column layout constants
|
||||
sync_engine.py Row hashing, classification, diff
|
||||
tests/
|
||||
test_basics.py 31 unit tests (no UNO/network required)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Toolbar Commands
|
||||
|
||||
| Button | Command | Description |
|
||||
|--------|---------|-------------|
|
||||
| Login | `SiloLogin` | Username/password dialog, creates API token |
|
||||
| Pull BOM | `SiloPullBOM` | Assembly picker -> expanded BOM -> populates sheet |
|
||||
| Pull Project | `SiloPullProject` | Project picker -> all project items -> multi-sheet workbook |
|
||||
| Push | `SiloPush` | Classifies rows -> creates/updates items -> auto-tags project |
|
||||
| Add Item | `SiloAddItem` | Completion wizard (category -> description -> fields) |
|
||||
| Refresh | `SiloRefresh` | Re-pull (placeholder) |
|
||||
| Settings | `SiloSettings` | API URL, token, SSL, OpenRouter config |
|
||||
| AI Describe | `SiloAIDescription` | AI description from seller description |
|
||||
|
||||
---
|
||||
|
||||
## BOM Sheet Format
|
||||
|
||||
28 columns total: 11 visible core, 13 hidden properties, 4 hidden sync tracking.
|
||||
|
||||
### Visible Columns
|
||||
|
||||
| Col | Header | Notes |
|
||||
|-----|--------|-------|
|
||||
| A | Item | Assembly/section header |
|
||||
| B | Level | BOM depth (0=top) |
|
||||
| C | Source | M=manufactured, P=purchased |
|
||||
| D | PN | Part number (read-only for existing) |
|
||||
| E | Description | Required for new items |
|
||||
| F | Seller Description | Vendor catalog text |
|
||||
| G | Unit Cost | Currency |
|
||||
| H | QTY | Decimal quantity |
|
||||
| I | Ext Cost | Formula =G*H (not stored) |
|
||||
| J | Sourcing Link | URL |
|
||||
| K | Schema | Schema name |
|
||||
|
||||
### Hidden Property Columns (L-X)
|
||||
|
||||
Manufacturer, Manufacturer PN, Supplier, Supplier PN, Lead Time, Min Order
|
||||
Qty, Lifecycle Status, RoHS, Country of Origin, Material, Finish, Notes,
|
||||
Long Description. Populated from revision properties, collapsed by default.
|
||||
|
||||
### Hidden Sync Columns (Y-AB)
|
||||
|
||||
`_silo_row_hash` (SHA-256), `_silo_row_status`, `_silo_updated_at`,
|
||||
`_silo_parent_pn`. Used for change detection and conflict resolution.
|
||||
|
||||
### Row Status Colors
|
||||
|
||||
| Status | Color | Hex |
|
||||
|--------|-------|-----|
|
||||
| synced | light green | #C6EFCE |
|
||||
| modified | light yellow | #FFEB9C |
|
||||
| new | light blue | #BDD7EE |
|
||||
| error | light red | #FFC7CE |
|
||||
| conflict | orange | #F4B084 |
|
||||
|
||||
---
|
||||
|
||||
## Completion Wizard
|
||||
|
||||
Three-step guided workflow for adding new BOM rows:
|
||||
|
||||
1. **Category** -- select from schema categories (F01-X08)
|
||||
2. **Description** -- required text, with AI generation offer when blank
|
||||
3. **Common fields** -- sourcing type, unit cost, quantity, sourcing link
|
||||
|
||||
If a manually entered PN already exists in the database, the PN Conflict
|
||||
Resolution dialog offers: use existing item, auto-generate new PN, or cancel.
|
||||
|
||||
New items are automatically tagged with the workbook's project code.
|
||||
|
||||
---
|
||||
|
||||
## OpenRouter AI Integration
|
||||
|
||||
The extension calls the OpenRouter API (OpenAI-compatible) to generate
|
||||
standardized part descriptions from verbose seller descriptions. This is
|
||||
useful because seller descriptions are typically detailed catalog text while
|
||||
BOM descriptions need to be concise (max 60 chars, title case, component
|
||||
type first, standard abbreviations).
|
||||
|
||||
### Configuration
|
||||
|
||||
Settings dialog fields (or `OPENROUTER_API_KEY` env var):
|
||||
|
||||
- **API Key** -- OpenRouter bearer token (masked in UI)
|
||||
- **AI Model** -- default `openai/gpt-4.1-nano`
|
||||
- **AI Instructions** -- customizable system prompt
|
||||
|
||||
### Workflow
|
||||
|
||||
1. Paste seller description into column F
|
||||
2. Click "AI Describe" on toolbar
|
||||
3. Review side-by-side dialog (seller text left, AI result right)
|
||||
4. Edit if needed, click Accept
|
||||
5. Description written to column E
|
||||
|
||||
The AI client (`ai_client.py`) is designed for reuse. The generic
|
||||
`chat_completion()` function can be called by future features (price
|
||||
analysis, sourcing assistance) without modification.
|
||||
|
||||
---
|
||||
|
||||
## Server-Side ODS Support
|
||||
|
||||
Pure Go ODS library at `internal/ods/` for server-side spreadsheet generation.
|
||||
No headless LibreOffice dependency -- ODS is a ZIP of XML files.
|
||||
|
||||
### Library (`internal/ods/`)
|
||||
|
||||
- `ods.go` -- types: Workbook, Sheet, Column, Row, Cell, CellType
|
||||
- `writer.go` -- generates valid ODS ZIP archives
|
||||
- `reader.go` -- parses ODS back to Go structs
|
||||
- `ods_test.go` -- 10 round-trip tests
|
||||
|
||||
### ODS Endpoints
|
||||
|
||||
| Method | Path | Description |
|
||||
|--------|------|-------------|
|
||||
| GET | `/api/items/export.ods` | Items as ODS |
|
||||
| GET | `/api/items/template.ods` | Blank import template |
|
||||
| POST | `/api/items/import.ods` | Import from ODS |
|
||||
| GET | `/api/items/{pn}/bom/export.ods` | BOM as formatted ODS |
|
||||
| GET | `/api/projects/{code}/sheet.ods` | Multi-sheet project workbook |
|
||||
| POST | `/api/sheets/diff` | Upload ODS, return JSON diff |
|
||||
|
||||
---
|
||||
|
||||
## Build and Install
|
||||
|
||||
```makefile
|
||||
make build-calc-oxt # zip pkg/calc/ into silo-calc.oxt
|
||||
make install-calc # unopkg add silo-calc.oxt
|
||||
make uninstall-calc # unopkg remove io.kindredsystems.silo.calc
|
||||
make test-calc # python3 -m unittest (31 tests)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Implementation Status
|
||||
|
||||
| Component | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| Extension skeleton | Done | manifest, description, Addons.xcu, ProtocolHandler.xcu |
|
||||
| SiloClient | Done | HTTP client adapted from FreeCAD workbench |
|
||||
| Settings | Done | JSON persistence, env var fallbacks |
|
||||
| Login dialog | Done | Two-step username/password |
|
||||
| Settings dialog | Done | API URL, token, SSL, OpenRouter fields |
|
||||
| Pull BOM | Done | Full column set, hidden groups, hash tracking |
|
||||
| Pull Project | Done | Items sheet + BOM sheet |
|
||||
| Push | Done | Create/update, auto project tagging, conflict detection |
|
||||
| Completion wizard | Done | 3-step with PN conflict resolution |
|
||||
| AI description | Done | OpenRouter client, review dialog, toolbar button |
|
||||
| Refresh | Stub | Placeholder only |
|
||||
| Go ODS library | Done | Writer, reader, 10 round-trip tests |
|
||||
| ODS endpoints | Done | 6 handlers registered |
|
||||
| Makefile targets | Done | build, install, uninstall, test, clean |
|
||||
|
||||
### Known Issues
|
||||
|
||||
- Refresh command is a placeholder (shows "coming soon")
|
||||
- No integration tests with a running Silo instance yet
|
||||
- `completion_wizard.py` uses simple input boxes instead of proper list dialogs
|
||||
- Push does not yet handle BOM relationship creation (item fields only)
|
||||
|
||||
---
|
||||
|
||||
## Testing
|
||||
|
||||
31 unit tests in `pkg/calc/tests/test_basics.py`, runnable without UNO or
|
||||
network access:
|
||||
|
||||
- TestSheetFormat (7) -- column indices, headers, sheet type detection
|
||||
- TestSyncEngine (9) -- hashing, classification, diff, conflict detection
|
||||
- TestSettings (3) -- load/save/auth
|
||||
- TestProjectFiles (3) -- path resolution, read/write
|
||||
- TestAIClient (9) -- constants, configuration, error handling
|
||||
|
||||
```
|
||||
$ python3 -m unittest pkg/calc/tests/test_basics.py -v
|
||||
Ran 31 tests in 0.031s
|
||||
OK
|
||||
```
|
||||
Reference in New Issue
Block a user