06/18 Update CLAUDE.md & notification_matrix.py
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
> **Audience:** AI assistants and developers working on this codebase.
|
> **Audience:** AI assistants and developers working on this codebase.
|
||||||
> **Purpose:** Authoritative reference for architecture, conventions, gotchas, and decisions.
|
> **Purpose:** Authoritative reference for architecture, conventions, gotchas, and decisions.
|
||||||
> **Last reviewed:** June 2026 (Phase 19 complete + mobile API gap-fill Phases A–E + customer UI refinements + Phase 22 comment visibility + Phase 23 support chat/tickets + inspector performance Excel export + inspection list filters + customer issue logging + AI chatbot + dashboard grouped sections + issues/inspections PDF export + date/ID filters + Reports expansion Phases R1–R4)
|
> **Last reviewed:** June 2026 (Phase 19 complete + mobile API gap-fill Phases A–E + customer UI refinements + Phase 22 comment visibility + Phase 23 support chat/tickets + inspector performance Excel export + inspection list filters + customer issue logging + AI chatbot + dashboard grouped sections + issues/inspections PDF export + date/ID filters + Reports expansion Phases R1–R4 + Phase 24 issue_created notify defaults + Phase 25 inspection GPS + Phase 26 issue vendor fields + Phase 27 facility score alerts)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -217,6 +217,8 @@ inspections: id, template_id, facility_id, area_id, inspector_id, inspection_dat
|
|||||||
overall_score, status (in_progress/completed/flagged), notes, form_data (JSON),
|
overall_score, status (in_progress/completed/flagged), notes, form_data (JSON),
|
||||||
completed_at, parent_inspection_id (self-FK), follow_up_required, follow_up_note,
|
completed_at, parent_inspection_id (self-FK), follow_up_required, follow_up_note,
|
||||||
mobile_local_id VARCHAR(64) nullable indexed ← Phase B
|
mobile_local_id VARCHAR(64) nullable indexed ← Phase B
|
||||||
|
submit_latitude DECIMAL(10,7) nullable ← Phase 25
|
||||||
|
submit_longitude DECIMAL(10,7) nullable ← Phase 25
|
||||||
```
|
```
|
||||||
|
|
||||||
**`mobile_local_id`:** UUID string generated on the iPad. Used for idempotency — if a submission arrives twice (network retry), the server returns the existing record without creating a duplicate. Set `NULL` for all web-created inspections.
|
**`mobile_local_id`:** UUID string generated on the iPad. Used for idempotency — if a submission arrives twice (network retry), the server returns the existing record without creating a duplicate. Set `NULL` for all web-created inspections.
|
||||||
@@ -232,7 +234,10 @@ issues: id, inspection_id (nullable), area_id, facility_id (nullable), severity
|
|||||||
reported_at, resolved_at, result_notes, result_photos (JSON),
|
reported_at, resolved_at, result_notes, result_photos (JSON),
|
||||||
mobile_photo_paths (JSON), ← Phase 19
|
mobile_photo_paths (JSON), ← Phase 19
|
||||||
verified_by, verified_at, verification_note, sla_notified,
|
verified_by, verified_at, verification_note, sla_notified,
|
||||||
mobile_local_id VARCHAR(64) nullable indexed ← Phase B
|
mobile_local_id VARCHAR(64) nullable indexed, ← Phase B
|
||||||
|
vendor_name VARCHAR(100) nullable, ← Phase 26
|
||||||
|
vendor_contact VARCHAR(200) nullable, ← Phase 26
|
||||||
|
vendor_notes TEXT nullable ← Phase 26
|
||||||
```
|
```
|
||||||
|
|
||||||
**Photo columns — three distinct fields with different semantics:**
|
**Photo columns — three distinct fields with different semantics:**
|
||||||
@@ -264,6 +269,16 @@ issue_comments: id, issue_id (FK), user_id (FK), body, created_at,
|
|||||||
|
|
||||||
**`is_customer_visible`:** Staff comments are hidden from customers by default (`False`). Staff can tick "Share with customer" at post time to set `True`. Customer-authored comments are always stored as `True`. Customers see only `is_customer_visible=True` comments; staff see all.
|
**`is_customer_visible`:** Staff comments are hidden from customers by default (`False`). Staff can tick "Share with customer" at post time to set `True`. Customer-authored comments are always stored as `True`. Customers see only `is_customer_visible=True` comments; staff see all.
|
||||||
|
|
||||||
|
### FacilityScoreAlert
|
||||||
|
|
||||||
|
```
|
||||||
|
facility_score_alerts: id, facility_id (FK→facilities CASCADE), sent_at DATETIME,
|
||||||
|
current_avg DECIMAL(5,2), prior_avg DECIMAL(5,2), delta DECIMAL(5,2)
|
||||||
|
INDEX ix_fsa_facility_sent (facility_id, sent_at)
|
||||||
|
```
|
||||||
|
|
||||||
|
Records each score-trend alert dispatched for a facility. `send_score_alerts()` queries this table to skip re-alerting a facility within the last 24 hours, preventing notification storms on persistent score drops.
|
||||||
|
|
||||||
### SupportTicket / SupportTicketReply
|
### SupportTicket / SupportTicketReply
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -552,6 +567,7 @@ EVENT_SLA_ALERT = 'sla_alert'
|
|||||||
EVENT_ISSUE_FLAGGED = 'issue_flagged'
|
EVENT_ISSUE_FLAGGED = 'issue_flagged'
|
||||||
EVENT_CUSTOMER_INSPECTION_DONE = 'customer_inspection_completed'
|
EVENT_CUSTOMER_INSPECTION_DONE = 'customer_inspection_completed'
|
||||||
EVENT_CUSTOMER_ISSUE_UPDATED = 'customer_issue_updated'
|
EVENT_CUSTOMER_ISSUE_UPDATED = 'customer_issue_updated'
|
||||||
|
EVENT_SCORE_ALERT = 'score_alert' ← Phase 27
|
||||||
```
|
```
|
||||||
|
|
||||||
### Cron Endpoints (all require `token=DIGEST_SECRET`)
|
### Cron Endpoints (all require `token=DIGEST_SECRET`)
|
||||||
@@ -561,6 +577,7 @@ EVENT_CUSTOMER_ISSUE_UPDATED = 'customer_issue_updated'
|
|||||||
| `POST /notifications/send-digest` | Digest email delivery | `0 7 * * *` |
|
| `POST /notifications/send-digest` | Digest email delivery | `0 7 * * *` |
|
||||||
| `POST /notifications/check-sla` | SLA breach/at-risk alerts | `*/30 * * * *` |
|
| `POST /notifications/check-sla` | SLA breach/at-risk alerts | `*/30 * * * *` |
|
||||||
| `POST /notifications/cleanup-tokens` | Purge expired API tokens | `0 3 * * *` |
|
| `POST /notifications/cleanup-tokens` | Purge expired API tokens | `0 3 * * *` |
|
||||||
|
| `POST /notifications/check-score-trends` | Facility score drop alerts (Phase 27) | `0 8 * * *` |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -628,7 +645,11 @@ phase1_projects_roles → phase6_features → phase7_mobile_api → phase8_notif
|
|||||||
→ phase21_template_active
|
→ phase21_template_active
|
||||||
→ phase21_performance_indexes
|
→ phase21_performance_indexes
|
||||||
→ phase22_comment_visibility
|
→ phase22_comment_visibility
|
||||||
→ phase23_support_tickets ← HEAD
|
→ phase23_support_tickets
|
||||||
|
→ phase24_notify_defaults
|
||||||
|
→ phase25_inspection_gps
|
||||||
|
→ phase26_issue_vendor
|
||||||
|
→ phase27_score_alerts ← HEAD
|
||||||
```
|
```
|
||||||
|
|
||||||
### phase21_performance_indexes
|
### phase21_performance_indexes
|
||||||
@@ -657,6 +678,43 @@ pip install groq # if not already installed
|
|||||||
sudo systemctl restart gunicorn
|
sudo systemctl restart gunicorn
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### phase24_notify_defaults
|
||||||
|
|
||||||
|
Data-only migration. Sets `enabled=True` for `('issue_created', 'admin')` and `('issue_created', 'director')` rows in `notification_matrix` if they already exist. Rows that do not yet exist are seeded at runtime by `MATRIX_DEFAULTS`. No schema change — no existence check needed, `UPDATE` on missing rows is a no-op.
|
||||||
|
|
||||||
|
### phase25_inspection_gps
|
||||||
|
|
||||||
|
Adds `submit_latitude DECIMAL(10,7) NULL` and `submit_longitude DECIMAL(10,7) NULL` to `inspections`. Populated at submit time — by browser Geolocation API (web) or CoreLocation (iPad). Null for all existing rows. Uses `INFORMATION_SCHEMA` column existence check — safe to re-run.
|
||||||
|
|
||||||
|
`inspections/view.html` shows a Google Maps embed (admin/director only) when both columns are non-null.
|
||||||
|
|
||||||
|
**iPad behaviour:** `InspectionLocationManager` begins acquiring a fix when the submit confirm dialog appears. GPS is captured into `LocalInspection.submitLatitude`/`submitLongitude` and sent in the `POST /api/v1/inspections` body. The `PATCH` endpoint does not accept GPS — creation-time capture only.
|
||||||
|
|
||||||
|
### phase26_issue_vendor
|
||||||
|
|
||||||
|
Adds three nullable columns to `issues`:
|
||||||
|
|
||||||
|
| Column | Type | Purpose |
|
||||||
|
|---|---|---|
|
||||||
|
| `vendor_name` | `VARCHAR(100)` | External contractor or vendor name |
|
||||||
|
| `vendor_contact` | `VARCHAR(200)` | Phone or email for the vendor |
|
||||||
|
| `vendor_notes` | `TEXT` | Notes about what the vendor is handling |
|
||||||
|
|
||||||
|
Displayed in `issues/view.html` and editable via `IssueForm` (`form.html`). Staff-only — not exposed in mobile API. Uses `INFORMATION_SCHEMA` existence check — safe to re-run.
|
||||||
|
|
||||||
|
### phase27_score_alerts
|
||||||
|
|
||||||
|
Creates `facility_score_alerts` table. Used by `send_score_alerts()` in `sla.py` for 24-hour deduplication of score-drop notifications. Uses table existence check — safe to re-run.
|
||||||
|
|
||||||
|
**Deploy order for phases 24–27:**
|
||||||
|
```bash
|
||||||
|
flask db upgrade
|
||||||
|
sudo systemctl restart gunicorn
|
||||||
|
# Add to cron:
|
||||||
|
# 0 8 * * * curl -s -X POST https://yourdomain.com/notifications/check-score-trends \
|
||||||
|
# -d "token=YOUR_DIGEST_SECRET"
|
||||||
|
```
|
||||||
|
|
||||||
### phase22_comment_visibility
|
### phase22_comment_visibility
|
||||||
|
|
||||||
Adds `is_customer_visible BOOLEAN NOT NULL DEFAULT FALSE` to `issue_comments`. Existing comments default to staff-only visibility. Uses `INFORMATION_SCHEMA` column existence check — safe to re-run.
|
Adds `is_customer_visible BOOLEAN NOT NULL DEFAULT FALSE` to `issue_comments`. Existing comments default to staff-only visibility. Uses `INFORMATION_SCHEMA` column existence check — safe to re-run.
|
||||||
@@ -898,6 +956,8 @@ timeout = 30
|
|||||||
-d "token=SECRET"
|
-d "token=SECRET"
|
||||||
0 8 * * * curl -s -X POST https://your-domain.com/scheduled-reports/run \
|
0 8 * * * curl -s -X POST https://your-domain.com/scheduled-reports/run \
|
||||||
-d "secret=SECRET"
|
-d "secret=SECRET"
|
||||||
|
0 8 * * * curl -s -X POST https://your-domain.com/notifications/check-score-trends \
|
||||||
|
-d "token=SECRET"
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ issue_created : admin ✗ director ✗ inspector ✗ pm ✗ cust
|
|||||||
issue_updated_customer : admin ✗ director ✗ inspector ✗ pm ✗ customer ✓
|
issue_updated_customer : admin ✗ director ✗ inspector ✗ pm ✗ customer ✓
|
||||||
verification_requested : admin ✓ director ✓ inspector ✗ pm ✗ customer ✗
|
verification_requested : admin ✓ director ✓ inspector ✗ pm ✗ customer ✗
|
||||||
sla_alert : admin ✓ director ✗ inspector ✗ pm ✗ customer ✗ (assignee + followers implicit)
|
sla_alert : admin ✓ director ✗ inspector ✗ pm ✗ customer ✗ (assignee + followers implicit)
|
||||||
|
score_alert : admin ✓ director ✓ inspector ✗ pm ✗ customer ✗ (facility score drop cron)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
@@ -58,6 +59,7 @@ MATRIX_EVENTS = {
|
|||||||
'issue_updated_customer': 'Issue updated (customer)',
|
'issue_updated_customer': 'Issue updated (customer)',
|
||||||
'verification_requested': 'Verification requested',
|
'verification_requested': 'Verification requested',
|
||||||
'sla_alert': 'SLA at-risk / breached',
|
'sla_alert': 'SLA at-risk / breached',
|
||||||
|
'score_alert': 'Facility score trend alert (significant drop)',
|
||||||
}
|
}
|
||||||
|
|
||||||
# Default enabled state: (event_key, role_key) → True/False
|
# Default enabled state: (event_key, role_key) → True/False
|
||||||
@@ -147,6 +149,13 @@ MATRIX_DEFAULTS = {
|
|||||||
('sla_alert', 'project_manager'): False,
|
('sla_alert', 'project_manager'): False,
|
||||||
('sla_alert', 'customer'): False,
|
('sla_alert', 'customer'): False,
|
||||||
('sla_alert', 'custom'): False,
|
('sla_alert', 'custom'): False,
|
||||||
|
# score_alert — facility rolling-avg score drop detected by cron
|
||||||
|
('score_alert', 'admin'): True,
|
||||||
|
('score_alert', 'director'): True,
|
||||||
|
('score_alert', 'inspector'): False,
|
||||||
|
('score_alert', 'project_manager'): False,
|
||||||
|
('score_alert', 'customer'): False,
|
||||||
|
('score_alert', 'custom'): False,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user