# Moderation Models FSM Migration Documentation

## Overview

This document describes the migration of moderation models from manual `RichChoiceField` status management to automated FSM-based state transitions using `django-fsm`.

## Migration Summary

### Models Migrated

1. **EditSubmission** - Content edit submission workflow
2. **ModerationReport** - User content/behavior reports
3. **ModerationQueue** - Moderation task queue
4. **BulkOperation** - Bulk administrative operations
5. **PhotoSubmission** - Photo upload moderation workflow

### Key Changes

#### 1. Field Type Changes
- **Before**: `status = RichChoiceField(...)`
- **After**: `status = RichFSMField(...)`

#### 2. Model Inheritance
- Added `StateMachineMixin` to all models
- Set `state_field_name = "status"` on each model

#### 3. Transition Methods
Models now have auto-generated FSM transition methods based on RichChoice metadata:
- `transition_to_<state>(user=None)` - FSM transition methods
- Original business logic preserved in existing methods (approve, reject, escalate)

#### 4. Service Layer Updates
- Updated to use FSM transition methods where appropriate
- Added `TransitionNotAllowed` exception handling
- Fallback to direct status assignment for compatibility

#### 5. View Layer Updates
- Added `TransitionNotAllowed` exception handling
- Graceful fallback for missing FSM transitions

## FSM Transition Methods

### EditSubmission
```python
# Auto-generated based on edit_submission_statuses metadata
submission.transition_to_approved(user=moderator)
submission.transition_to_rejected(user=moderator)
submission.transition_to_escalated(user=moderator)

# Business logic preserved in wrapper methods
submission.approve(moderator)  # Creates/updates Park or Ride objects
submission.reject(moderator, reason="...")
submission.escalate(moderator, reason="...")
```

### ModerationReport
```python
# Auto-generated based on moderation_report_statuses metadata
report.transition_to_under_review(user=moderator)
report.transition_to_resolved(user=moderator)
report.transition_to_closed(user=moderator)
```

### ModerationQueue
```python
# Auto-generated based on moderation_queue_statuses metadata
queue_item.transition_to_in_progress(user=moderator)
queue_item.transition_to_completed(user=moderator)
queue_item.transition_to_pending(user=moderator)
```

### BulkOperation
```python
# Auto-generated based on bulk_operation_statuses metadata
operation.transition_to_running(user=admin)
operation.transition_to_completed(user=admin)
operation.transition_to_failed(user=admin)
operation.transition_to_cancelled(user=admin)
operation.transition_to_pending(user=admin)
```

### PhotoSubmission
```python
# Auto-generated based on photo_submission_statuses metadata
submission.transition_to_approved(user=moderator)
submission.transition_to_rejected(user=moderator)
submission.transition_to_escalated(user=moderator)

# Business logic preserved in wrapper methods
submission.approve(moderator, notes="...")  # Creates ParkPhoto or RidePhoto
submission.reject(moderator, notes="...")
submission.escalate(moderator, notes="...")
```

## StateMachineMixin Helper Methods

All models now have access to these helper methods:

```python
# Check if transition is possible
submission.can_transition_to('APPROVED')  # Returns bool

# Get available transitions from current state
submission.get_available_transitions()  # Returns list of state values

# Get available transition method names
submission.get_available_transition_methods()  # Returns list of method names

# Check if state is final (no transitions out)
submission.is_final_state()  # Returns bool

# Get state display with metadata
submission.get_state_display_rich()  # Returns RichChoice with metadata
```

## Configuration (apps.py)

State machines are auto-configured during Django initialization:

```python
# apps/moderation/apps.py
class ModerationConfig(AppConfig):
    def ready(self):
        from apps.core.state_machine import apply_state_machine
        from .models import (
            EditSubmission, ModerationReport, ModerationQueue,
            BulkOperation, PhotoSubmission
        )

        apply_state_machine(
            EditSubmission,
            field_name="status",
            choice_group="edit_submission_statuses",
            domain="moderation"
        )
        # ... similar for other models
```

## Validation Command

Validate all state machine configurations:

```bash
# Validate all models
python manage.py validate_state_machines

# Validate specific model
python manage.py validate_state_machines --model editsubmission

# Verbose output with transition graphs
python manage.py validate_state_machines --verbose
```

## Migration Steps Applied

1. ✅ Updated model field definitions (RichChoiceField → RichFSMField)
2. ✅ Added StateMachineMixin to all models
3. ✅ Refactored transition methods to work with FSM
4. ✅ Configured state machine application in apps.py
5. ✅ Updated service layer to use FSM transitions
6. ✅ Updated view layer with TransitionNotAllowed handling
7. ✅ Created Django migration (0007_convert_status_to_richfsmfield.py)
8. ✅ Created validation management command
9. ✅ Fixed FSM method naming to use transition_to_<state> pattern
10. ✅ Updated business logic methods to call FSM transitions

## Next Steps

### 1. Review Generated Migration ✅ COMPLETED
Migration file created: `apps/moderation/migrations/0007_convert_status_to_richfsmfield.py`
- Converts status fields from RichChoiceField to RichFSMField
- All 5 models included: EditSubmission, ModerationReport, ModerationQueue, BulkOperation, PhotoSubmission
- No data loss - field type change is compatible
- Default values preserved

### 2. Apply Migration
```bash
python manage.py migrate moderation
```

### 3. Validate State Machines
```bash
python manage.py validate_state_machines --verbose
```

### 4. Test Transitions
- Test approve/reject/escalate workflows for EditSubmission
- Test photo approval workflows for PhotoSubmission
- Test queue item lifecycle for ModerationQueue
- Test report resolution for ModerationReport
- Test bulk operation status changes for BulkOperation

## RichChoice Metadata Requirements

All choice groups must have this metadata structure:

```python
{
    'PENDING': {
        'can_transition_to': ['APPROVED', 'REJECTED', 'ESCALATED'],
        'requires_moderator': False,
        'is_final': False
    },
    'APPROVED': {
        'can_transition_to': [],
        'requires_moderator': True,
        'is_final': True
    },
    # ...
}
```

Required metadata keys:
- `can_transition_to`: List of states this state can transition to
- `requires_moderator`: Whether transition requires moderator permissions
- `is_final`: Whether this is a terminal state

## Permission Guards

FSM transitions automatically enforce permissions based on metadata:

- `requires_moderator=True`: Requires MODERATOR, ADMIN, or SUPERUSER role
- Permission checks happen before transition execution
- `TransitionNotAllowed` raised if permissions insufficient

## Error Handling

### TransitionNotAllowed Exception

Raised when:
- Invalid state transition attempted
- Permission requirements not met
- Current state doesn't allow transition

```python
from django_fsm import TransitionNotAllowed

try:
    submission.transition_to_approved(user=user)
except TransitionNotAllowed:
    # Handle invalid transition
    pass
```

### Service Layer Fallbacks

Services include fallback logic for compatibility:

```python
try:
    queue_item.transition_to_completed(user=moderator)
except (TransitionNotAllowed, AttributeError):
    # Fallback to direct assignment if FSM unavailable
    queue_item.status = 'COMPLETED'
```

## Testing Recommendations

### Unit Tests
- Test each transition method individually
- Verify permission requirements
- Test invalid transitions raise TransitionNotAllowed
- Test business logic in wrapper methods

### Integration Tests
- Test complete approval workflows
- Test queue item lifecycle
- Test bulk operation status progression
- Test service layer integration

### Manual Testing
- Use Django admin to trigger transitions
- Test API endpoints for status changes
- Verify fsm_log records created correctly

## FSM Logging

All transitions are automatically logged via `django-fsm-log`:

```python
from django_fsm_log.models import StateLog

# Get transition history for a model
logs = StateLog.objects.for_(submission)

# Each log contains:
# - timestamp
# - state (new state)
# - by (user who triggered transition)
# - transition (method name)
# - source_state (previous state)
```

## Rollback Plan

If issues arise, rollback steps:

1. Revert migration: `python manage.py migrate moderation <previous_migration>`
2. Revert code changes in Git
3. Remove FSM configuration from apps.py
4. Restore original RichChoiceField definitions

## Performance Considerations

- FSM transitions add minimal overhead
- State validation happens in-memory
- Permission guards use cached user data
- No additional database queries for transitions
- FSM logging adds one INSERT per transition (async option available)

## Compatibility Notes

- Maintains backward compatibility with existing status queries
- RichFSMField is drop-in replacement for RichChoiceField
- All existing filters and lookups continue to work
- No changes needed to serializers or templates
- API responses unchanged (status values remain the same)

## Support Resources

- FSM Infrastructure: `backend/apps/core/state_machine/`
- State Machine README: `backend/apps/core/state_machine/README.md`
- Metadata Specification: `backend/apps/core/state_machine/METADATA_SPEC.md`
- django-fsm docs: https://github.com/viewflow/django-fsm
- django-fsm-log docs: https://github.com/jazzband/django-fsm-log
