- Introduced reusable test utilities in `backend/tests/utils` for FSM transitions, HTMX interactions, and common scenarios. - Added factory functions for creating test submissions, parks, rides, and photo submissions. - Implemented assertion helpers for verifying state changes, toast notifications, and transition logs. - Created comprehensive state machine diagrams for all FSM-enabled models in `docs/STATE_DIAGRAMS.md`, detailing states, transitions, and guard conditions.
10 KiB
System Patterns
High-Level Architecture
Backend Architecture
- Django-based MVT (Model-View-Template) architecture
- Modular app structure for domain separation
- HTMX for dynamic server-side rendering
- AlpineJS for client-side interactivity
Core Apps
-
Parks & Rides
- parks/ - Park management
- rides/ - Ride information
- designers/ - Ride designer profiles
- companies/ - Park operator profiles
-
User Content
- reviews/ - User reviews
- media/ - Media management
- moderation/ - Content moderation
-
Supporting Systems
- accounts/ - User management
- analytics/ - Usage tracking
- location/ - Geographic services
- email_service/ - Communication
- history_tracking/ - Change tracking
Core Technical Patterns
Data Flow
-
Request Handling
- Django URL routing
- View processing
- HTMX partial updates
- Template rendering
-
Content Management
- Moderated submission flow
- Media processing pipeline
- Review validation system
- History tracking
-
User Interactions
- HTMX for dynamic updates
- AlpineJS for UI state
- Partial template loading
- Progressive enhancement
Database Patterns
- Django ORM for data access
- Related models for complex relationships
- Signals for cross-model updates
- History tracking for changes
Key Technical Decisions
Frontend Strategy
-
Server-Side Rendering
- Django templates as base
- HTMX for dynamic updates
- Partial templates by model
- AlpineJS for client state
-
Styling
- Tailwind CSS
- Component-based design
- Responsive layouts
Backend Organization
-
App Separation
- Domain-driven design
- Clear responsibility boundaries
- Modular functionality
- Reusable components
-
Code Structure
- Models for data definition
- Views for business logic
- Templates for presentation
- URLs for routing
- Signals for cross-cutting concerns
Integration Patterns
-
External Services
- Email service integration
- Media storage handling
- Analytics tracking
- Location services
-
Internal Communication
- Django signals
- Context processors
- Middleware
- Template tags
Data Flow Patterns
Content Creation
-
User Input
- Form submission
- Media upload
- Review creation
- Park/ride updates
-
Processing
- Validation
- Moderation queue
- Media processing
- History tracking
-
Publication
- Approval workflow
- Public visibility
- Notification system
- Cache updates
Query Patterns
-
Efficient Loading
- Select related
- Prefetch related
- Cached queries
- Optimized indexes
-
Search Operations
- Location-based queries
- Full-text search
- Filtered results
- Sorted listings
FSM State Machine Integration with HTMX
Overview
The codebase implements a reusable FSM (Finite State Machine) infrastructure using django-fsm, integrated with HTMX for seamless state transitions without full page reloads.
Core Components
-
FSMTransitionView (
apps/core/views/views.py)- Generic view for handling FSM state transitions via HTMX POST requests
- Validates permissions using
can_proceedfrom django-fsm - Executes transitions and returns updated HTML partials
- Sends HX-Trigger headers for toast notifications
-
FSM Template Tags (
apps/core/templatetags/fsm_tags.py)get_available_transitions: Returns available transitions for an object/userget_state_value: Gets current state valueget_state_display: Gets human-readable state displaydefault_target_id: Generates HTMX target IDsapp_label,model_name: Model metadata filters
-
Reusable Partials (
templates/htmx/)status_with_actions.html: Combined status badge with action buttonsstate_actions.html: Standalone action buttonsupdated_row.html: Generic fallback for row updates
Integration Pattern
{% load fsm_tags %}
<!-- Include FSM status and actions -->
{% include 'htmx/status_with_actions.html' with object=submission user=user show_badge=True %}
FSM Transition Flow with HTMX
sequenceDiagram
participant User
participant Browser
participant HTMX
participant FSMTransitionView
participant Model
participant Template
User->>Browser: Click "Approve" button
Browser->>HTMX: hx-post to FSM transition URL
HTMX->>FSMTransitionView: POST /moderation/submissions/<pk>/transition/transition_to_approved/
FSMTransitionView->>FSMTransitionView: Validate permissions (can_proceed)
FSMTransitionView->>Model: Execute transition
Model->>Model: Update status field + save
Model-->>FSMTransitionView: Return updated object
FSMTransitionView->>Template: Render model-specific partial
FSMTransitionView-->>HTMX: HTTP 200 + HX-Trigger: showToast
HTMX->>Browser: Swap target element with new HTML
HTMX->>Browser: Trigger showToast event
Browser->>User: Display updated row + success toast
URL Pattern Configuration
FSM transitions use a consistent URL pattern:
/api/moderation/<model_plural>/<pk>/transition/<transition_name>/
Example URL patterns in apps/moderation/urls.py:
path(
"submissions/<int:pk>/transition/<str:transition_name>/",
FSMTransitionView.as_view(),
{"app_label": "moderation", "model_name": "editsubmission"},
name="submission_transition",
)
Template Resolution
FSMTransitionView automatically resolves model-specific templates:
{app_label}/partials/{model_name}_row.html{app_label}/partials/{model_name}_item.html{app_label}/partials/{model_name}.htmlhtmx/updated_row.html(fallback)
Toast Notifications
The FSMTransitionView includes HX-Trigger headers for toast notifications:
response["HX-Trigger"] = json.dumps({
"showToast": {
"message": "Submission approved successfully",
"type": "success"
}
})
Dashboard templates listen for these events with Alpine.js:
@show-toast.window="showToast($event.detail)"
Status Badge Styling
The status_with_actions.html partial includes automatic styling based on state:
PENDING- Yellow (warning)APPROVED- Green (success)REJECTED- Red (danger)ESCALATED- Orange (caution)IN_PROGRESS- Blue (info)
Flash Animations
Successful transitions include a flash animation class:
.animate-flash-success {
animation: flash-success 1s ease-in-out;
}
Error Handling Flow
sequenceDiagram
participant User
participant Browser
participant HTMX
participant FSMTransitionView
participant PermissionGuard
User->>Browser: Click "Approve" button
Browser->>HTMX: hx-post with HX-Request header
HTMX->>FSMTransitionView: POST /transition/approve/
FSMTransitionView->>PermissionGuard: Check can_proceed(user)
alt Permission Denied
PermissionGuard-->>FSMTransitionView: False
FSMTransitionView-->>HTMX: 403 + HX-Trigger: showToast (error)
HTMX->>Browser: Trigger error toast event
Browser->>User: Display red error toast
else Permission Granted
PermissionGuard-->>FSMTransitionView: True
FSMTransitionView->>FSMTransitionView: Execute transition
alt Transition Success
FSMTransitionView-->>HTMX: 200 + HTML + HX-Trigger: showToast (success)
HTMX->>Browser: Swap HTML + trigger success toast
Browser->>User: Display updated row + green toast
else Transition Failed
FSMTransitionView-->>HTMX: 400 + HX-Trigger: showToast (error)
HTMX->>Browser: Trigger error toast event
Browser->>User: Display red error toast
end
end
Error Response Types
| Status | Scenario | Toast Type | Message |
|---|---|---|---|
| 200 | Success | success | "{Model} has been {action}d successfully" |
| 400 | Invalid transition | error | "Transition not allowed from current state" |
| 403 | Permission denied | error | "You don't have permission for this action" |
| 404 | Object not found | error | "Object not found" |
| 500 | Server error | error | "An unexpected error occurred" |
Testing FSM HTMX Transitions
Unit Tests
Backend unit tests in apps/moderation/tests.py, apps/parks/tests.py, and apps/rides/tests.py cover:
- FSM transition logic and state changes
- Permission guards and role-based access
- Transition history logging with django-fsm-log
- Callback execution and side effects
Integration Tests
Integration tests in backend/tests/integration/test_fsm_transition_view.py verify:
- FSMTransitionView handles HTMX requests correctly
- HX-Trigger headers contain proper toast data
- Correct partial templates rendered for each model
- Permission validation before transition execution
- StateLog entries created for each transition
E2E Tests
End-to-end tests using Playwright in backend/tests/e2e/ validate:
- Complete user interaction flow from button click to UI update
- Toast notifications appear and auto-dismiss
- Loading indicators show during transitions
- Error handling displays user-friendly messages
- Permission guards prevent unauthorized transitions
- Cross-browser compatibility (Chrome, Firefox, Safari)
Test files:
test_moderation_fsm.py- EditSubmission, PhotoSubmission transitionstest_park_ride_fsm.py- Park and Ride status changestest_fsm_permissions.py- Permission guard verificationtest_fsm_error_handling.py- Error scenarios and loading states
Running Tests
# Run all FSM tests
pytest -k fsm
# Run e2e FSM tests
pytest backend/tests/e2e/test_moderation_fsm.py
pytest backend/tests/e2e/test_park_ride_fsm.py
pytest backend/tests/e2e/test_fsm_permissions.py
# Run with specific browser
pytest --browser firefox backend/tests/e2e/test_moderation_fsm.py
# Run with headed mode (see browser)
pytest --headed backend/tests/e2e/test_moderation_fsm.py
# Run integration tests (faster, no browser)
pytest backend/tests/integration/test_fsm_transition_view.py
Browser Testing Checklist
For comprehensive manual testing, see backend/tests/e2e/BROWSER_TESTING_CHECKLIST.md.
Error Handling
- Django middleware
- Custom error pages
- Logging system
- User notifications
Security Patterns
- Django authentication
- Permission mixins
- CSRF protection
- XSS prevention
- Input validation