# Contributing to ShopDB Flask ## Coding Standards ### Naming principles 1. Spell it out. If a non-technical user looking at a report would not understand a column at a glance, rename it. 2. No project-specific shorthand. A name either uses a word in full, or uses an acronym from the allowed list below. 3. Consistency beats cleverness. Once a name is committed and used in code, do not rename it for marginal improvement. 4. When unsure, ask: "would a new shop-floor IT person understand this?" ### Database - **Table names**: lowercase, concatenated, plural. No dashes, no underscores. - Good: `machines`, `pctypes`, `networkdevices`, `businessunits`, `auditlogs` - Bad: `machine_types`, `network-devices`, `BusinessUnits` - **Column names**: lowercase, concatenated, singular. No dashes, no underscores. - Good: `machineid`, `machinenumber`, `lastzabbixsync`, `isactive` - Bad: `machine_id`, `last_zabbix_sync`, `IsActive` - **Foreign keys**: referenced table name (singular) + `id`. - Good: `locationid` references `locations`, `vendorid` references `vendors` - **Booleans**: prefix with `is` or `has`. - Good: `isactive`, `isshopfloor`, `hasprinter` - **Index names**: `idx__`. Underscores allowed here only because indexes are infrastructure, not data. - Good: `idx_machines_locationid` ### Allowed acronyms (closed list, expand by PR) - **Universal**: `id`, `url`, `api`, `http`, `https`, `json`, `jwt`, `sql`, `os`, `ip`, `dns`, `csv`, `pdf`, `cors`, `ttl`, `uuid`, `html`, `css`, `orm` - **Domain (shop floor / IT infra)**: `cmm`, `cnc`, `pc`, `usb`, `vnc`, `winrm`, `ssh`, `ssl`, `tls`, `tcp`, `udp`, `smtp`, `ldap`, `vlan`, `sso`, `dnc`, `focas`, `clm`, `mtconnect` Any acronym not on these lists must be spelled out. Proposing a new acronym is a separate PR that updates this list. **Third-party imports are exempt.** Library and stdlib module paths cannot be renamed. `from sqlalchemy.orm import ...`, `import importlib.util`, `from werkzeug.utils import ...` are all fine even if they contain words that would otherwise be banned. ### Banned shorthand `cfg`, `ctx`, `mgr`, `req`, `res`, `env`, `util`, `helper`, and any other domain-specific shortening. Spell it out: `config`, `context`, `manager`, `request`, `response`, `environment`, `utilities`. Note: `db` as a standalone variable name is banned, but `db` as a prefix in `database` or as part of project name `shopdb` is fine. **Suffix exception.** Banned shorthand applies to standalone identifiers. As a suffix where the prefix establishes meaning, it is acceptable: - `printers_bp`, `auth_bp`, `network_bp` are fine. The prefix names what blueprint, the `_bp` suffix is conventional for Flask blueprints. - `request_obj` is fine when disambiguating from imported `request`. - `bp` alone as a variable name is not fine. Always pair with a meaningful prefix. ### Code #### Python - **Variables and functions holding a database value**: match the column name exactly. No conversion to snake_case. - DB column `machineid` -> Python variable `machineid`, attribute `Machine.machineid`, dict key `{"machineid": 1}` - DB column `lastzabbixsync` -> Python variable `lastzabbixsync` - **Pure code variables and functions** (loop counters, helpers, anything that does not mirror a DB field): snake_case per PEP 8. - Good: `loop_count`, `current_user`, `validate_input()` - **Classes**: PascalCase. Spell out fully. - Good: `PrinterData`, `AssetType`, `NetworkDevice` - **Modules / file names**: lowercase. Underscores allowed where it improves readability (PEP 8 permits this for modules). - Good: `employee_service.py`, `asset_routes.py` #### JavaScript / Vue - **Variables**: camelCase per JS convention. - Good: `currentUser`, `isLoading` - **Variables holding API field values**: match the API field name exactly. Do NOT convert to camelCase. - API returns `{"machineid": 1}` -> JS `response.machineid`, NOT `response.machineId` - API returns `{"lastzabbixsync": "..."}` -> JS `device.lastzabbixsync` - **Components**: PascalCase. Spell out fully. - Good: `AssetDetail.vue`, `MachineForm.vue`, `PrinterList.vue` - **CSS classes**: lowercase with dashes. - Good: `asset-detail`, `machine-form` #### API - **Endpoints**: lowercase, plural nouns. No underscores or dashes. - Good: `/api/machines`, `/api/networkdevices`, `/api/businessunits` - **Query parameters**: lowercase, concatenated. Match column names where applicable. - Good: `?locationid=5`, `?isactive=true` - **Response keys**: match database column names exactly. - Good: `{"machineid": 1, "lastzabbixsync": "2026-01-12T10:00:00Z"}` ### Examples ```python # Good - DB-mirrored fields keep column names; pure code uses snake_case class PrinterData(db.Model): __tablename__ = 'printerdata' machineid = db.Column(db.Integer, db.ForeignKey('machines.machineid')) lastzabbixsync = db.Column(db.DateTime) isnetworkprinter = db.Column(db.Boolean) def list_printers_by_location(locationid: int) -> list[dict]: printers = PrinterData.query.filter_by(locationid=locationid).all() result_count = len(printers) return [{"machineid": p.machineid, "isnetworkprinter": p.isnetworkprinter} for p in printers] ``` ```python # Bad - PEP 8-style underscores on DB fields, banned shorthand, project acronym not on allowed list class PrinterData(db.Model): __tablename__ = 'printer_data' machine_id = db.Column(db.Integer) last_zabbix_sync = db.Column(db.DateTime) is_net_prn = db.Column(db.Boolean) def list_prns_by_loc(loc_id): bp_ctx = get_context() cfg = load_cfg() return PrinterData.query.filter_by(loc_id=loc_id).all() ``` ### Style policy (non-naming) - No emojis in code, comments, documentation, string literals, or UI. - No em-dashes (U+2014), en-dashes (U+2013), Unicode arrows, or smart quotes anywhere. Plain ASCII only. - Comments default to none. Add one only when the WHY is non-obvious. Keep comments terse (lite caveman style is fine for inline `#` and `//` comments). Docstrings stay normal English. - Dark theme is default. Keep UI functional and professional. ### File structure - Core models: `shopdb/core/models/` - Core API routes: `shopdb/core/api/` - Core services: `shopdb/core/services/` - Plugin code: `plugins//{models,api,services,schemas}/` ### Plugin development When creating a new plugin: 1. Create directory `plugins//`. Use the scaffold (`flask plugin new `) when available. 2. Include `manifest.json` with metadata. This is the single source of truth for plugin name, version, dependencies, api_prefix. 3. Extend `BasePlugin` class. 4. Follow naming conventions above. 5. Run `flask plugin install ` to install. See `docs/PLUGIN-QUICKSTART.md` (when published) for the 30-minute walkthrough. ## Testing Run tests with: ```bash pytest tests/ ``` Tests are required for: - New routes (smoke + happy path + 1-2 edge cases) - New plugin hooks (contract test) - Schema migrations (before/after data integrity) ## Code review checklist - [ ] No underscores in table or column names - [ ] No banned shorthand or unapproved acronyms in any new identifier - [ ] DB-mirrored Python and JS variables match column names exactly - [ ] API response keys match column names exactly - [ ] No em-dashes, en-dashes, smart quotes, Unicode arrows, or emojis (run `grep -rP '[^\x00-\x7F]' --include='*.py' --include='*.vue' --include='*.js' .`) - [ ] Plugin follows `BasePlugin` interface and contract version - [ ] Tests included for new functionality