July 4 - Update with recurring/scheduled inspections

This commit is contained in:
2026-07-04 13:11:16 -04:00
parent 215a6cd920
commit 07226b4878
12 changed files with 824 additions and 5 deletions
+34 -3
View File
@@ -389,6 +389,19 @@ notification_matrix: id, event_type, role_key, enabled, custom_emails (JSON)
UniqueConstraint(event_type, role_key)
```
### InspectionSchedule (phase34)
```
inspection_schedules: id, name VARCHAR(255), template_id (FK→inspection_templates CASCADE),
facility_id (FK→facilities CASCADE), area_id (FK→areas SET NULL, nullable),
inspector_id (FK→users CASCADE), frequency ENUM(daily/weekly/monthly/quarterly),
active BOOL, created_by (FK→users SET NULL), created_at DATETIME,
last_run_at DATETIME NULL, next_run_at DATETIME NULL
INDEX ix_ischd_active_next (active, next_run_at)
```
Recurring inspection generator. `POST /inspection-schedules/run` (token-protected cron) walks active schedules where `next_run_at <= now`, creates one `in_progress` Inspection per due schedule (assigned to `inspector_id`, dated now), notifies the inspector (`event_type='inspection_scheduled'`), then advances `next_run_at`. Managed at `/inspection-schedules` by admin/director/project_manager. Purely additive — a schedule is an automated `inspections.start()`.
### AuditLog
```
@@ -473,6 +486,7 @@ The `DeviceRegistration` model and the duplicate `api_devices` blueprint were **
| `audit` | `/audit` | list (admin only), view, purge |
| `reports` | `/reports` | index, facility report, scorecard, CSV/PDF/Excel export, issues-aging, sla-compliance, followup-closure, facility summary PDF |
| `scheduled_reports` | `/scheduled-reports` | CRUD + manual trigger (accessible via Reports sub-nav) |
| `inspection_schedules` | `/inspection-schedules` | phase34 — recurring inspection CRUD (`@project_manager_required`) + `POST /run-now` (manual) + `POST /run` (token-protected cron materialiser) |
| `support` | `/support` | `GET /chat`, `POST /chat/message` (AJAX→Groq), `POST /tickets`, `GET /my-tickets`, `GET/POST /my-tickets/<id>`, `GET /admin/tickets`, `GET/POST /admin/tickets/<id>` |
| `broadcast` | `/admin/broadcast` | `GET /` (compose + history), `POST /send` — admin-only push to iOS via Notification rows (phase29) |
| `devices` | `/admin/devices` | `GET /` (registered device list), `POST /notify` — notify users on outdated app versions (reads `api_device_tokens`) |
@@ -675,6 +689,7 @@ EVENT_ISSUE_FLAGGED = 'issue_flagged'
EVENT_CUSTOMER_INSPECTION_DONE = 'customer_inspection_completed'
EVENT_CUSTOMER_ISSUE_UPDATED = 'customer_issue_updated'
EVENT_SCORE_ALERT = 'score_alert' ← Phase 27
EVENT_INSPECTION_SCHEDULED = 'inspection_scheduled' ← phase34
```
### Cron Endpoints (all require `token=DIGEST_SECRET`)
@@ -687,6 +702,7 @@ EVENT_SCORE_ALERT = 'score_alert' ← Phase 27
| `POST /notifications/check-score-trends` | Facility score drop alerts (Phase 27) | `0 8 * * *` |
| `POST /notifications/trial-reminders` | Trial-ending warning emails (≤3 days left) | `0 9 * * *` |
| `POST /notifications/dunning-reminders` | Payment-failure escalation emails (day 3/7/14) | `0 10 * * *` |
| `POST /inspection-schedules/run` | Materialise due recurring inspections (phase34) | `0 6 * * *` |
---
@@ -741,7 +757,7 @@ limiter = Limiter(
## 17. Alembic Migration Chain
**Current HEAD:** `phase33_tenant_settings` (31 migrations total).
**Current HEAD:** `phase34_inspection_schedules` (32 migrations total).
**Chain root:** `0003_add_user_active` — a guarded squashed baseline (MT-2) that recreates the full 25-table schema with INFORMATION_SCHEMA guards. The original baseline migrations (0001/0002/0003) were lost; this file restores the chain root so Alembic can build the revision map. `down_revision = None`.
@@ -769,7 +785,20 @@ limiter = Limiter(
→ phase30_device_registry
→ phase31_device_registry
→ phase32_device_token_columns
→ phase33_tenant_settings ← HEAD
→ phase33_tenant_settings
→ phase34_inspection_schedules ← HEAD
```
### phase34_inspection_schedules
Creates the `inspection_schedules` table backing recurring/automated inspections (see §5 model + the `inspection_schedules` blueprint). A schedule pairs a template + facility (+ optional area) + inspector + cadence; the cron endpoint `POST /inspection-schedules/run` materialises a real `in_progress` Inspection per due schedule and notifies the inspector (`event_type='inspection_scheduled'`). Uses an `INFORMATION_SCHEMA` table-existence check — safe to re-run.
**Deploy order:**
```bash
flask db upgrade
sudo systemctl restart gunicorn
# Add to cron (materialise due schedules daily at 06:00):
# 0 6 * * * curl -s -X POST https://yourdomain.com/inspection-schedules/run -d "token=YOUR_DIGEST_SECRET"
```
### phase28_fix_inspection_notify
@@ -1162,6 +1191,8 @@ set -a; . /etc/jqc/control.env; set +a
-d "token=SECRET"
0 10 * * * curl -s -X POST https://your-domain.com/notifications/dunning-reminders \
-d "token=SECRET"
0 6 * * * curl -s -X POST https://your-domain.com/inspection-schedules/run \
-d "token=SECRET"
```
---
@@ -1596,7 +1627,7 @@ Ask: Does this change break any other code path that uses the modified function,
**Rule 13 — List every file changed** with the exact location of each change (function name and what was modified).
**Rule 14 — Migrations are required for any schema change.**
Follow the `phase{N}_description.py` naming convention. The new migration's `down_revision` must point to the current HEAD (`phase33_tenant_settings`). Use `INFORMATION_SCHEMA` existence checks so migrations are safe to re-run. Never use `batch_alter_table` for MySQL.
Follow the `phase{N}_description.py` naming convention. The new migration's `down_revision` must point to the current HEAD (`phase34_inspection_schedules`). Use `INFORMATION_SCHEMA` existence checks so migrations are safe to re-run. Never use `batch_alter_table` for MySQL.
Self-contained package, own `ControlBase` + engine/session, own Alembic chain. No imports from `app/`.