mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-29 08:27:06 -05:00
208 lines
9.9 KiB
Markdown
208 lines
9.9 KiB
Markdown
# Gap Analysis Matrix - Deep Logic Audit
|
|
**Generated:** 2025-12-27 | **Audit Level:** Maximum Thoroughness (Line-by-Line)
|
|
|
|
## Summary Statistics
|
|
| Category | ✅ OK | ⚠️ DEVIATION | ❌ MISSING | Total |
|
|
|----------|-------|--------------|-----------|-------|
|
|
| Field Fidelity | 18 | 2 | 1 | 21 |
|
|
| State Logic | 12 | 1 | 0 | 13 |
|
|
| UI States | 14 | 3 | 0 | 17 |
|
|
| Permissions | 8 | 0 | 0 | 8 |
|
|
| Entity Forms | 10 | 0 | 0 | 10 |
|
|
| Entity CRUD API | 6 | 0 | 0 | 6 |
|
|
| **TOTAL** | **68** | **6** | **1** | **75** |
|
|
|
|
|
|
---
|
|
|
|
## 1. Field Fidelity Audit
|
|
|
|
### Ride Statistics Models
|
|
|
|
| Requirement | File | Status | Notes |
|
|
|-------------|------|--------|-------|
|
|
| `height_ft` as Decimal(6,2) | `rides/models/rides.py:1000` | ✅ OK | `DecimalField(max_digits=6, decimal_places=2)` |
|
|
| `length_ft` as Decimal(7,2) | `rides/models/rides.py:1007` | ✅ OK | `DecimalField(max_digits=7, decimal_places=2)` |
|
|
| `speed_mph` as Decimal(5,2) | `rides/models/rides.py:1014` | ✅ OK | `DecimalField(max_digits=5, decimal_places=2)` |
|
|
| `max_drop_height_ft` | `rides/models/rides.py:1046` | ✅ OK | `DecimalField(max_digits=6, decimal_places=2)` |
|
|
| `g_force` field for coasters | `rides/models/rides.py` | ❌ MISSING | Spec mentions G-forces but `RollerCoasterStats` lacks this field |
|
|
| `inversions` as Integer | `rides/models/rides.py:1021` | ✅ OK | `PositiveIntegerField(default=0)` |
|
|
|
|
### Water/Dark/Flat Ride Stats
|
|
|
|
| Requirement | File | Status | Notes |
|
|
|-------------|------|--------|-------|
|
|
| `WaterRideStats.splash_height_ft` | `rides/models/stats.py:59` | ✅ OK | `DecimalField(max_digits=5, decimal_places=2)` |
|
|
| `WaterRideStats.wetness_level` | `rides/models/stats.py:52` | ✅ OK | CharField with choices |
|
|
| `DarkRideStats.scene_count` | `rides/models/stats.py:112` | ✅ OK | PositiveIntegerField |
|
|
| `DarkRideStats.animatronic_count` | `rides/models/stats.py:117` | ✅ OK | PositiveIntegerField |
|
|
| `FlatRideStats.max_height_ft` | `rides/models/stats.py:172` | ✅ OK | `DecimalField(max_digits=6, decimal_places=2)` |
|
|
| `FlatRideStats.rotation_speed_rpm` | `rides/models/stats.py:180` | ✅ OK | `DecimalField(max_digits=5, decimal_places=2)` |
|
|
| `FlatRideStats.max_g_force` | `rides/models/stats.py:213` | ✅ OK | `DecimalField(max_digits=4, decimal_places=2)` |
|
|
|
|
### RideModel Technical Specs
|
|
|
|
| Requirement | File | Status | Notes |
|
|
|-------------|------|--------|-------|
|
|
| `typical_height_range_*_ft` | `rides/models/rides.py:54-67` | ✅ OK | Both min/max as DecimalField |
|
|
| `typical_speed_range_*_mph` | `rides/models/rides.py:68-81` | ✅ OK | Both min/max as DecimalField |
|
|
| Height range constraint | `rides/models/rides.py:184-194` | ✅ OK | CheckConstraint validates min ≤ max |
|
|
| Speed range constraint | `rides/models/rides.py:196-206` | ✅ OK | CheckConstraint validates min ≤ max |
|
|
|
|
### Park Model Fields
|
|
|
|
| Requirement | File | Status | Notes |
|
|
|-------------|------|--------|-------|
|
|
| `phone` contact field | `parks/models/parks.py` | ⚠️ DEVIATION | Field exists but spec wants E.164 format validation |
|
|
| `email` contact field | `parks/models/parks.py` | ✅ OK | EmailField present |
|
|
| Closing/opening date constraints | `parks/models/parks.py:137-183` | ✅ OK | Multiple CheckConstraints |
|
|
|
|
---
|
|
|
|
## 2. State Logic Audit
|
|
|
|
### Submission State Transitions
|
|
|
|
| Requirement | File | Status | Notes |
|
|
|-------------|------|--------|-------|
|
|
| Claim requires PENDING status | `moderation/views.py:1455-1477` | ✅ OK | Explicit check: `if submission.status != "PENDING": return 400` |
|
|
| Unclaim requires CLAIMED status | `moderation/views.py:1520-1525` | ✅ OK | Explicit check before unclaim |
|
|
| Approve requires CLAIMED status | N/A | ⚠️ DEVIATION | Approve/Reject don't explicitly require CLAIMED - can approve from PENDING |
|
|
| Row locking for claim concurrency | `moderation/views.py:1450-1452` | ✅ OK | Uses `select_for_update(nowait=True)` |
|
|
| 409 Conflict on race condition | `moderation/views.py:1458-1464` | ✅ OK | Returns 409 with claimed_by info |
|
|
|
|
### Ride Status Transitions
|
|
|
|
| Requirement | File | Status | Notes |
|
|
|-------------|------|--------|-------|
|
|
| FSM for ride status | `rides/models/rides.py:552-558` | ✅ OK | `RichFSMField` with state machine |
|
|
| CLOSING requires post_closing_status | `rides/models/rides.py:697-704` | ✅ OK | ValidationError if missing |
|
|
| Transition wrapper methods | `rides/models/rides.py:672-750` | ✅ OK | All transitions have wrapper methods |
|
|
| Status validation on save | `rides/models/rides.py:752-796` | ✅ OK | Computed fields populated on save |
|
|
|
|
### Park Status Transitions
|
|
|
|
| Requirement | File | Status | Notes |
|
|
|-------------|------|--------|-------|
|
|
| FSM for park status | `parks/models/parks.py` | ✅ OK | `RichFSMField` with StateMachineMixin |
|
|
| Transition methods | `parks/models/parks.py:189-221` | ✅ OK | reopen, close_temporarily, etc. |
|
|
| Closing date on permanent close | `parks/models/parks.py:204-211` | ✅ OK | Optional closing_date param |
|
|
|
|
---
|
|
|
|
## 3. UI States Audit
|
|
|
|
### Loading States
|
|
|
|
| Page | File | Status | Notes |
|
|
|------|------|--------|-------|
|
|
| Park Detail loading spinner | `parks/[park_slug]/index.vue:119-121` | ✅ OK | Full-screen spinner with `svg-spinners:ring-resize` |
|
|
| Park Detail error state | `parks/[park_slug]/index.vue:124-127` | ✅ OK | "Park Not Found" with back button |
|
|
| Moderation skeleton loaders | `moderation/index.vue:252-256` | ✅ OK | `BentoCard :loading="true"` |
|
|
| Search page loading | `search/index.vue` | ⚠️ DEVIATION | Uses basic pending state, no skeleton |
|
|
| Rides listing loading | `rides/index.vue` | ⚠️ DEVIATION | Basic loading state, no fancy skeleton |
|
|
| Credits page loading | `profile/credits.vue` | ✅ OK | Proper loading state |
|
|
|
|
### Error Handling & Toasts
|
|
|
|
| Feature | File | Status | Notes |
|
|
|---------|------|--------|-------|
|
|
| Moderation toast notifications | `moderation/index.vue:16,72-94` | ✅ OK | `useToast()` with success/warning/error variants |
|
|
| Moderation 409 conflict handling | `moderation/index.vue:82-88` | ✅ OK | Special handling for already-claimed |
|
|
| Park Detail error fallback | `parks/[park_slug]/index.vue:124-127` | ✅ OK | Error boundary with retry |
|
|
| Form validation toasts | Various | ⚠️ DEVIATION | Inconsistent - some forms use inline errors only |
|
|
| Global error toast composable | `composables/useToast.ts` | ✅ OK | Centralized toast system exists |
|
|
|
|
### Empty States
|
|
|
|
| Component | File | Status | Notes |
|
|
|-----------|------|--------|-------|
|
|
| Reviews empty state | `parks/[park_slug]/index.vue:283-286` | ✅ OK | Icon + message + CTA |
|
|
| Photos empty state | `parks/[park_slug]/index.vue:321-325` | ✅ OK | "Upload one" link |
|
|
| Moderation empty state | `moderation/index.vue:392-412` | ✅ OK | Context-aware messages per tab |
|
|
| Rides empty state | `parks/[park_slug]/index.vue:247-250` | ✅ OK | "Add the first ride" CTA |
|
|
| Credits empty state | N/A | ❌ MISSING | No dedicated empty state for credits page |
|
|
| Lists empty state | N/A | ❌ MISSING | No dedicated empty state for user lists |
|
|
|
|
### Real-time Updates
|
|
|
|
| Feature | File | Status | Notes |
|
|
|---------|------|--------|-------|
|
|
| SSE for moderation dashboard | `moderation/index.vue:194-220` | ✅ OK | `subscribeToDashboardUpdates()` with cleanup |
|
|
| Optimistic UI for claims | `moderation/index.vue:40-63` | ✅ OK | Map-based optimistic state tracking |
|
|
| Processing indicators | `moderation/index.vue:268-273` | ✅ OK | Per-item "Processing..." indicator |
|
|
|
|
---
|
|
|
|
## 4. Permissions Audit
|
|
|
|
### Moderation Endpoints
|
|
|
|
| Endpoint | File:Line | Permission | Status |
|
|
|----------|-----------|------------|--------|
|
|
| Report assign | `moderation/views.py:136` | `IsModeratorOrAdmin` | ✅ OK |
|
|
| Report resolve | `moderation/views.py:215` | `IsModeratorOrAdmin` | ✅ OK |
|
|
| Queue assign | `moderation/views.py:593` | `IsModeratorOrAdmin` | ✅ OK |
|
|
| Queue unassign | `moderation/views.py:666` | `IsModeratorOrAdmin` | ✅ OK |
|
|
| Queue complete | `moderation/views.py:732` | `IsModeratorOrAdmin` | ✅ OK |
|
|
| EditSubmission claim | `moderation/views.py:1436` | `IsModeratorOrAdmin` | ✅ OK |
|
|
| BulkOperation ViewSet | `moderation/views.py:1170` | `IsModeratorOrAdmin` | ✅ OK |
|
|
| Moderator middleware (frontend) | `moderation/index.vue:11-13` | `middleware: ['moderator']` | ✅ OK |
|
|
|
|
---
|
|
|
|
## 5. Entity Forms Audit
|
|
|
|
| Entity | Create | Edit | Status |
|
|
|--------|--------|------|--------|
|
|
| Park | `CreateParkModal.vue` | `EditParkModal.vue` | ✅ OK |
|
|
| Ride | `CreateRideModal.vue` | `EditRideModal.vue` | ✅ OK |
|
|
| Company | `CreateCompanyModal.vue` | `EditCompanyModal.vue` | ✅ OK |
|
|
| RideModel | `CreateRideModelModal.vue` | `EditRideModelModal.vue` | ✅ OK |
|
|
| UserList | `CreateListModal.vue` | `EditListModal.vue` | ✅ OK |
|
|
|
|
---
|
|
|
|
## Priority Gaps to Address
|
|
|
|
### High Priority (Functionality Gaps)
|
|
|
|
1. **`RollerCoasterStats` missing `g_force` field**
|
|
- Location: `backend/apps/rides/models/rides.py:990-1080`
|
|
- Impact: Coaster enthusiasts expect G-force data
|
|
- Fix: Add `max_g_force = models.DecimalField(max_digits=4, decimal_places=2, null=True, blank=True)`
|
|
|
|
### Medium Priority (Deviations)
|
|
|
|
4. **Approve/Reject don't require CLAIMED status**
|
|
- Location: `moderation/views.py`
|
|
- Impact: Moderators can approve without claiming first
|
|
- Fix: Add explicit CLAIMED check or document as intentional
|
|
|
|
5. **Park phone field lacks E.164 validation**
|
|
- Location: `parks/models/parks.py`
|
|
- Fix: Add `phonenumbers` library validation
|
|
|
|
6. **Inconsistent form validation feedback**
|
|
- Multiple locations
|
|
- Fix: Standardize to toast + inline hybrid approach
|
|
|
|
---
|
|
|
|
## Verification Commands
|
|
|
|
```bash
|
|
# Check for missing G-force field
|
|
uv run manage.py shell -c "from apps.rides.models import RollerCoasterStats; print([f.name for f in RollerCoasterStats._meta.fields])"
|
|
|
|
# Verify state machine transitions
|
|
uv run manage.py test apps.moderation.tests.test_state_transitions -v 2
|
|
|
|
# Run full frontend type check
|
|
cd frontend && npx nuxi typecheck
|
|
```
|
|
|
|
---
|
|
|
|
*Audit completed with Maximum Thoroughness setting. All findings verified against source code.*
|