mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 16:51:07 -05:00
Compare commits
1 Commits
main
...
feature/dj
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2faf0368cf |
@@ -1,74 +1,133 @@
|
|||||||
# Active Development Context
|
# Active Context - Wiki Migration & Integration
|
||||||
|
|
||||||
## Recently Completed
|
## Current Status
|
||||||
|
Corrected implementation strategy to use wiki-only approach instead of dual-system.
|
||||||
|
|
||||||
### Park Search Implementation (2024-02-22)
|
### Completed Components
|
||||||
|
1. Wiki Plugin Structure
|
||||||
|
- Models for park metadata
|
||||||
|
- Forms for data input
|
||||||
|
- Templates for display
|
||||||
|
- URL configurations
|
||||||
|
|
||||||
1. Autocomplete Base:
|
2. Documentation
|
||||||
- Created BaseAutocomplete in core/forms.py
|
- Technical specifications
|
||||||
- Configured project-wide auth requirement
|
- Migration guide
|
||||||
- Added test coverage for base functionality
|
- Implementation decisions
|
||||||
|
- User guide
|
||||||
|
|
||||||
2. Park Search:
|
### Current Focus
|
||||||
- Implemented ParkAutocomplete class
|
Migration to wiki-only system
|
||||||
- Created ParkSearchForm with autocomplete widget
|
|
||||||
- Updated views and templates for integration
|
|
||||||
- Added comprehensive test suite
|
|
||||||
|
|
||||||
3. Documentation:
|
## Immediate Tasks
|
||||||
- Updated memory-bank/features/parks/search.md
|
|
||||||
- Added test documentation
|
|
||||||
- Created user interface guidelines
|
|
||||||
|
|
||||||
## Active Tasks
|
### 1. Data Migration
|
||||||
|
- [x] Create migration script
|
||||||
|
- [ ] Test migration in development
|
||||||
|
- [ ] Backup production data
|
||||||
|
- [ ] Execute migration
|
||||||
|
- [ ] Verify data integrity
|
||||||
|
|
||||||
1. Testing:
|
### 2. URL Structure
|
||||||
- [ ] Run the test suite with `uv run pytest parks/tests/`
|
- [x] Update URL configuration
|
||||||
- [ ] Monitor test coverage with pytest-cov
|
- [x] Add redirects from old URLs
|
||||||
- [ ] Verify HTMX interactions work as expected
|
- [ ] Test all redirects
|
||||||
|
- [ ] Monitor 404 errors
|
||||||
|
|
||||||
2. Performance Monitoring:
|
### 3. Template Cleanup
|
||||||
- [ ] Add database indexes if needed
|
- [x] Remove dual-system templates
|
||||||
- [ ] Monitor query performance
|
- [x] Update wiki templates
|
||||||
- [ ] Consider caching strategies
|
- [ ] Remove legacy templates
|
||||||
|
- [ ] Clean up static files
|
||||||
3. User Experience:
|
|
||||||
- [ ] Get feedback on search responsiveness
|
|
||||||
- [ ] Monitor error rates
|
|
||||||
- [ ] Check accessibility compliance
|
|
||||||
|
|
||||||
## Next Steps
|
## Next Steps
|
||||||
|
|
||||||
1. Enhancements:
|
### 1. Migration Testing (Priority High)
|
||||||
- Add geographic search capabilities
|
```bash
|
||||||
- Implement result caching
|
# Test migration command
|
||||||
- Add full-text search support
|
uv run manage.py migrate_to_wiki --dry-run
|
||||||
|
```
|
||||||
|
|
||||||
2. Integration:
|
### 2. Plugin Refinement
|
||||||
- Extend to other models (Rides, Areas)
|
- Add missing metadata fields
|
||||||
- Add combined search functionality
|
- Optimize queries
|
||||||
- Improve filter integration
|
- Implement caching
|
||||||
|
- Add validation
|
||||||
|
|
||||||
3. Testing:
|
### 3. User Experience
|
||||||
- Add Playwright e2e tests
|
- Update navigation
|
||||||
- Implement performance benchmarks
|
- Add search integration
|
||||||
- Add accessibility tests
|
- Improve metadata forms
|
||||||
|
- Add quick actions
|
||||||
|
|
||||||
## Technical Debt
|
## Technical Requirements
|
||||||
|
|
||||||
None currently identified for the search implementation.
|
### Migration
|
||||||
|
1. Database Backup
|
||||||
|
```sql
|
||||||
|
pg_dump thrillwiki > backup.sql
|
||||||
|
```
|
||||||
|
|
||||||
## Dependencies
|
2. Data Verification
|
||||||
|
```python
|
||||||
|
# Verify counts match
|
||||||
|
parks_count = Park.objects.count()
|
||||||
|
wiki_count = Article.objects.filter(
|
||||||
|
plugin_parks_parkmetadata__isnull=False
|
||||||
|
).count()
|
||||||
|
```
|
||||||
|
|
||||||
- django-htmx-autocomplete
|
3. Performance Monitoring
|
||||||
- pytest-django
|
- Monitor database load
|
||||||
- pytest-cov
|
- Watch memory usage
|
||||||
|
- Track response times
|
||||||
|
|
||||||
|
### Integration Points
|
||||||
|
1. User Authentication
|
||||||
|
- Wiki permissions
|
||||||
|
- Role mapping
|
||||||
|
- Access control
|
||||||
|
|
||||||
|
2. Media Handling
|
||||||
|
- Image storage
|
||||||
|
- File management
|
||||||
|
- Gallery support
|
||||||
|
|
||||||
|
3. Search Integration
|
||||||
|
- Index wiki content
|
||||||
|
- Include metadata
|
||||||
|
- Update search views
|
||||||
|
|
||||||
|
## Risks and Mitigations
|
||||||
|
|
||||||
|
### Data Loss Prevention
|
||||||
|
- Complete backup before migration
|
||||||
|
- Dry run verification
|
||||||
|
- Rollback plan prepared
|
||||||
|
- Data integrity checks
|
||||||
|
|
||||||
|
### Performance Impact
|
||||||
|
- Monitor database load
|
||||||
|
- Cache aggressively
|
||||||
|
- Optimize queries
|
||||||
|
- Staged migration
|
||||||
|
|
||||||
|
### User Disruption
|
||||||
|
- Clear communication
|
||||||
|
- Maintenance window
|
||||||
|
- Quick rollback option
|
||||||
|
- Support documentation
|
||||||
|
|
||||||
|
## Success Criteria
|
||||||
|
1. All park data migrated
|
||||||
|
2. No data loss
|
||||||
|
3. All features functional
|
||||||
|
4. Performance maintained
|
||||||
|
5. Users can access content
|
||||||
|
6. Search working correctly
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
- Keep old models temporarily
|
||||||
The implementation follows these principles:
|
- Monitor error logs
|
||||||
- Authentication-first approach
|
- Document all issues
|
||||||
- Performance optimization
|
- Track performance metrics
|
||||||
- Accessibility compliance
|
|
||||||
- Test coverage
|
|
||||||
- Clean documentation
|
|
||||||
147
memory-bank/decisions/django_wiki_evaluation.md
Normal file
147
memory-bank/decisions/django_wiki_evaluation.md
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
# Django-Wiki Transformation Evaluation
|
||||||
|
|
||||||
|
## Current System State
|
||||||
|
- Early stage project with minimal existing data
|
||||||
|
- Complex custom implementation for content management
|
||||||
|
- Multiple specialized apps that may be overkill for current needs
|
||||||
|
- HTMX + AlpineJS + Tailwind CSS frontend
|
||||||
|
|
||||||
|
## Django-Wiki Analysis
|
||||||
|
|
||||||
|
### Core Features Provided
|
||||||
|
1. Content Management
|
||||||
|
- Wiki pages and hierarchies
|
||||||
|
- Version control
|
||||||
|
- Markdown support
|
||||||
|
- Built-in editor
|
||||||
|
- Permission system
|
||||||
|
|
||||||
|
2. Extension System
|
||||||
|
- Plugins available
|
||||||
|
- Customizable templates
|
||||||
|
- API hooks
|
||||||
|
- Custom storage backends
|
||||||
|
|
||||||
|
### Transformation Benefits
|
||||||
|
|
||||||
|
1. **Simplified Architecture**
|
||||||
|
- Replace custom content management
|
||||||
|
- Built-in versioning and history
|
||||||
|
- Standard wiki conventions
|
||||||
|
- Reduced code maintenance
|
||||||
|
|
||||||
|
2. **Feature Alignment**
|
||||||
|
- Core park/ride pages as wiki articles
|
||||||
|
- Categories for organization
|
||||||
|
- Rich text editing
|
||||||
|
- User contributions
|
||||||
|
- Content moderation
|
||||||
|
|
||||||
|
3. **Development Efficiency**
|
||||||
|
- Proven, maintained codebase
|
||||||
|
- Active community
|
||||||
|
- Documentation available
|
||||||
|
- Security updates
|
||||||
|
|
||||||
|
## Transformation Strategy
|
||||||
|
|
||||||
|
### Phase 1: Core Setup
|
||||||
|
1. Remove unnecessary apps:
|
||||||
|
- history/history_tracking (use wiki history)
|
||||||
|
- core (migrate needed parts)
|
||||||
|
- designers (convert to wiki pages)
|
||||||
|
- media (use wiki attachments)
|
||||||
|
|
||||||
|
2. Keep Essential Apps:
|
||||||
|
- accounts (user management)
|
||||||
|
- location (geographic features)
|
||||||
|
- moderation (adapt for wiki)
|
||||||
|
|
||||||
|
3. Install Django-Wiki:
|
||||||
|
- Core installation
|
||||||
|
- Configure settings
|
||||||
|
- Setup templates
|
||||||
|
- Migrate database
|
||||||
|
|
||||||
|
### Phase 2: UI Integration
|
||||||
|
1. Wiki Template Customization
|
||||||
|
- Apply Tailwind CSS
|
||||||
|
- Integrate AlpineJS
|
||||||
|
- Add HTMX enhancements
|
||||||
|
- Match site design
|
||||||
|
|
||||||
|
2. Feature Implementation
|
||||||
|
- Park pages as articles
|
||||||
|
- Ride information sections
|
||||||
|
- Location integration
|
||||||
|
- Review system
|
||||||
|
- Media handling
|
||||||
|
|
||||||
|
### Phase 3: Enhanced Features
|
||||||
|
1. Custom Extensions
|
||||||
|
- Park metadata plugin
|
||||||
|
- Location visualization
|
||||||
|
- Review integration
|
||||||
|
- Media gallery
|
||||||
|
|
||||||
|
2. User Experience
|
||||||
|
- Navigation structure
|
||||||
|
- Search optimization
|
||||||
|
- Mobile responsiveness
|
||||||
|
- Performance tuning
|
||||||
|
|
||||||
|
## Technical Requirements
|
||||||
|
|
||||||
|
### Core Dependencies
|
||||||
|
- django-wiki
|
||||||
|
- django-mptt (tree structure)
|
||||||
|
- django-nyt (notifications)
|
||||||
|
- Markdown processing
|
||||||
|
- Pillow (images)
|
||||||
|
- Sorl-thumbnail (thumbnails)
|
||||||
|
|
||||||
|
### Frontend Integration
|
||||||
|
- Custom templates
|
||||||
|
- Tailwind CSS setup
|
||||||
|
- AlpineJS components
|
||||||
|
- HTMX interactions
|
||||||
|
|
||||||
|
### Authentication
|
||||||
|
- Retain current auth system
|
||||||
|
- Integrate with wiki permissions
|
||||||
|
- Role-based access
|
||||||
|
- Moderation workflow
|
||||||
|
|
||||||
|
## Risks and Mitigations
|
||||||
|
|
||||||
|
1. **Data Migration**
|
||||||
|
- Risk: Minimal (little existing data)
|
||||||
|
- Action: Simple manual migration
|
||||||
|
|
||||||
|
2. **Feature Parity**
|
||||||
|
- Risk: Some custom features needed
|
||||||
|
- Action: Implement as wiki plugins
|
||||||
|
|
||||||
|
3. **Performance**
|
||||||
|
- Risk: Standard wiki performance
|
||||||
|
- Action: Implement caching
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
1. Initial Setup
|
||||||
|
- Remove unnecessary apps
|
||||||
|
- Install django-wiki
|
||||||
|
- Configure basic settings
|
||||||
|
- Setup authentication
|
||||||
|
|
||||||
|
2. UI Development
|
||||||
|
- Create base templates
|
||||||
|
- Apply styling
|
||||||
|
- Add interactivity
|
||||||
|
- Test responsive design
|
||||||
|
|
||||||
|
3. Custom Features
|
||||||
|
- Develop needed plugins
|
||||||
|
- Integrate location services
|
||||||
|
- Setup moderation
|
||||||
|
- Configure search
|
||||||
96
memory-bank/decisions/wiki_implementation_correction.md
Normal file
96
memory-bank/decisions/wiki_implementation_correction.md
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
# Wiki Implementation Correction
|
||||||
|
|
||||||
|
## Original Misunderstanding
|
||||||
|
We incorrectly attempted to maintain both systems:
|
||||||
|
- Traditional park/ride system
|
||||||
|
- Wiki-based system
|
||||||
|
|
||||||
|
This was WRONG. The correct approach is to fully migrate to wiki-based system.
|
||||||
|
|
||||||
|
## Corrected Approach
|
||||||
|
|
||||||
|
### 1. Implementation Strategy
|
||||||
|
- Use wiki as the primary and ONLY content system
|
||||||
|
- All park/ride content lives in wiki articles
|
||||||
|
- Metadata handled through wiki plugins
|
||||||
|
- Reviews/ratings as wiki extensions
|
||||||
|
|
||||||
|
### 2. URL Structure
|
||||||
|
```
|
||||||
|
/wiki/parks/[park-name] # Park articles
|
||||||
|
/wiki/rides/[ride-name] # Ride articles
|
||||||
|
/wiki/companies/[company-name] # Company articles
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Data Migration Plan
|
||||||
|
1. Convert existing parks to wiki articles
|
||||||
|
2. Transfer metadata to wiki plugin system
|
||||||
|
3. Move existing reviews to wiki comment system
|
||||||
|
4. Redirect old URLs to wiki system
|
||||||
|
|
||||||
|
### 4. Feature Implementation
|
||||||
|
All features should be implemented as wiki plugins:
|
||||||
|
- Park metadata plugin
|
||||||
|
- Ride metadata plugin
|
||||||
|
- Review/rating plugin
|
||||||
|
- Media handling plugin
|
||||||
|
- Statistics tracking plugin
|
||||||
|
|
||||||
|
### 5. Authorization/Permissions
|
||||||
|
Use wiki's built-in permission system:
|
||||||
|
- Article creation permissions
|
||||||
|
- Edit permissions
|
||||||
|
- Moderation system
|
||||||
|
- User roles
|
||||||
|
|
||||||
|
## Benefits of Wiki-Only Approach
|
||||||
|
1. Consistent Content Management
|
||||||
|
- Single source of truth
|
||||||
|
- Unified editing interface
|
||||||
|
- Version control for all content
|
||||||
|
|
||||||
|
2. Better Collaboration
|
||||||
|
- Community editing
|
||||||
|
- Change tracking
|
||||||
|
- Discussion pages
|
||||||
|
|
||||||
|
3. Simplified Architecture
|
||||||
|
- One content system
|
||||||
|
- Unified permissions
|
||||||
|
- Consistent user experience
|
||||||
|
|
||||||
|
4. Enhanced Features
|
||||||
|
- Built-in versioning
|
||||||
|
- Discussion pages
|
||||||
|
- Change tracking
|
||||||
|
- Link management
|
||||||
|
|
||||||
|
## Implementation Tasks
|
||||||
|
|
||||||
|
### Immediate
|
||||||
|
1. Remove dual-system templates
|
||||||
|
2. Create wiki-only templates
|
||||||
|
3. Set up plugin architecture
|
||||||
|
|
||||||
|
### Short Term
|
||||||
|
1. Create data migration scripts
|
||||||
|
2. Update URL routing
|
||||||
|
3. Implement wiki plugins
|
||||||
|
|
||||||
|
### Long Term
|
||||||
|
1. Phase out old models
|
||||||
|
2. Remove legacy code
|
||||||
|
3. Update documentation
|
||||||
|
|
||||||
|
## Migration Strategy
|
||||||
|
1. Create wiki articles for all parks
|
||||||
|
2. Migrate metadata to plugins
|
||||||
|
3. Move media to wiki system
|
||||||
|
4. Update all references
|
||||||
|
5. Remove old system
|
||||||
|
|
||||||
|
## Documentation Updates Needed
|
||||||
|
1. Update user guides
|
||||||
|
2. Create wiki contribution guides
|
||||||
|
3. Document plugin usage
|
||||||
|
4. Update API documentation
|
||||||
187
memory-bank/decisions/wiki_plugin_decisions.md
Normal file
187
memory-bank/decisions/wiki_plugin_decisions.md
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
# Wiki Plugin Implementation Decisions
|
||||||
|
|
||||||
|
## Parks Plugin Design Decisions
|
||||||
|
|
||||||
|
### 1. Plugin Architecture
|
||||||
|
**Decision:** Implement as full Django-Wiki plugin rather than standalone app
|
||||||
|
**Rationale:**
|
||||||
|
- Better integration with wiki features
|
||||||
|
- Consistent user experience
|
||||||
|
- Built-in revision tracking
|
||||||
|
- Permission system reuse
|
||||||
|
|
||||||
|
### 2. Data Model Structure
|
||||||
|
**Decision:** Split into ParkMetadata and ParkStatistic models
|
||||||
|
**Rationale:**
|
||||||
|
- Separates core metadata from time-series data
|
||||||
|
- Allows efficient querying of historical data
|
||||||
|
- Enables future analytics features
|
||||||
|
- Maintains data normalization
|
||||||
|
|
||||||
|
### 3. GeoDjango Integration
|
||||||
|
**Decision:** Use GeoDjango for location data
|
||||||
|
**Rationale:**
|
||||||
|
- Proper spatial data handling
|
||||||
|
- Future mapping capabilities
|
||||||
|
- Industry standard for geographic features
|
||||||
|
- Enables location-based queries
|
||||||
|
|
||||||
|
### 4. JSON Fields for Flexible Data
|
||||||
|
**Decision:** Use JSONField for amenities and ticket info
|
||||||
|
**Rationale:**
|
||||||
|
- Allows schema evolution
|
||||||
|
- Supports varying data structures
|
||||||
|
- Easy to extend without migrations
|
||||||
|
- Good for unstructured data
|
||||||
|
|
||||||
|
### 5. Template Organization
|
||||||
|
**Decision:** Three-template structure (metadata, statistics, sidebar)
|
||||||
|
**Rationale:**
|
||||||
|
- Separates concerns
|
||||||
|
- Reusable components
|
||||||
|
- Easier maintenance
|
||||||
|
- Better performance (partial updates)
|
||||||
|
|
||||||
|
### 6. Form Handling
|
||||||
|
**Decision:** Custom form classes with specialized processing
|
||||||
|
**Rationale:**
|
||||||
|
- Complex data transformation
|
||||||
|
- Better validation
|
||||||
|
- Improved user experience
|
||||||
|
- Reusable logic
|
||||||
|
|
||||||
|
## Lessons Learned
|
||||||
|
|
||||||
|
### Successful Approaches
|
||||||
|
1. Separation of Metadata and Statistics
|
||||||
|
- Simplified queries
|
||||||
|
- Better performance
|
||||||
|
- Easier maintenance
|
||||||
|
|
||||||
|
2. Use of Tailwind CSS
|
||||||
|
- Consistent styling
|
||||||
|
- Rapid development
|
||||||
|
- Responsive design
|
||||||
|
|
||||||
|
3. Template Structure
|
||||||
|
- Modular design
|
||||||
|
- Clear separation
|
||||||
|
- Easy to extend
|
||||||
|
|
||||||
|
### Areas for Improvement
|
||||||
|
1. Cache Strategy
|
||||||
|
- Need more granular caching
|
||||||
|
- Consider cache invalidation
|
||||||
|
- Performance optimization
|
||||||
|
|
||||||
|
2. Form Validation
|
||||||
|
- Add more client-side validation
|
||||||
|
- Improve error messages
|
||||||
|
- Consider async validation
|
||||||
|
|
||||||
|
3. Data Migration
|
||||||
|
- Need better migration tools
|
||||||
|
- Consider automated mapping
|
||||||
|
- Improve data verification
|
||||||
|
|
||||||
|
## Impact on Rides Plugin
|
||||||
|
|
||||||
|
### Design Patterns to Reuse
|
||||||
|
1. Model Structure
|
||||||
|
- Metadata/Statistics split
|
||||||
|
- JSON fields for flexibility
|
||||||
|
- Clear relationships
|
||||||
|
|
||||||
|
2. Template Organization
|
||||||
|
- Three-template approach
|
||||||
|
- Component reuse
|
||||||
|
- Consistent layout
|
||||||
|
|
||||||
|
3. Form Handling
|
||||||
|
- Custom validation
|
||||||
|
- Field transformation
|
||||||
|
- Error handling
|
||||||
|
|
||||||
|
### Improvements to Implement
|
||||||
|
1. Cache Strategy
|
||||||
|
- Implement from start
|
||||||
|
- More granular control
|
||||||
|
- Better invalidation
|
||||||
|
|
||||||
|
2. Data Validation
|
||||||
|
- More comprehensive
|
||||||
|
- Better error handling
|
||||||
|
- Client-side checks
|
||||||
|
|
||||||
|
3. Integration Points
|
||||||
|
- Cleaner API
|
||||||
|
- Better event handling
|
||||||
|
- Improved relationships
|
||||||
|
|
||||||
|
## Future Considerations
|
||||||
|
|
||||||
|
### Scalability
|
||||||
|
1. Database Optimization
|
||||||
|
- Index strategy
|
||||||
|
- Query optimization
|
||||||
|
- Cache usage
|
||||||
|
|
||||||
|
2. Content Management
|
||||||
|
- Media handling
|
||||||
|
- Version control
|
||||||
|
- Content validation
|
||||||
|
|
||||||
|
3. User Experience
|
||||||
|
- Progressive enhancement
|
||||||
|
- Loading states
|
||||||
|
- Error recovery
|
||||||
|
|
||||||
|
### Maintenance
|
||||||
|
1. Documentation
|
||||||
|
- Keep inline docs
|
||||||
|
- Update technical docs
|
||||||
|
- Maintain user guides
|
||||||
|
|
||||||
|
2. Testing
|
||||||
|
- Comprehensive coverage
|
||||||
|
- Integration tests
|
||||||
|
- Performance tests
|
||||||
|
|
||||||
|
3. Monitoring
|
||||||
|
- Error tracking
|
||||||
|
- Performance metrics
|
||||||
|
- Usage analytics
|
||||||
|
|
||||||
|
## Technical Debt Management
|
||||||
|
|
||||||
|
### Current Technical Debt
|
||||||
|
1. Cache Implementation
|
||||||
|
- Basic caching only
|
||||||
|
- No invalidation strategy
|
||||||
|
- Limited scope
|
||||||
|
|
||||||
|
2. Form Validation
|
||||||
|
- Mostly server-side
|
||||||
|
- Basic client validation
|
||||||
|
- Limited feedback
|
||||||
|
|
||||||
|
3. Error Handling
|
||||||
|
- Basic error messages
|
||||||
|
- Limited recovery options
|
||||||
|
- Minimal logging
|
||||||
|
|
||||||
|
### Debt Resolution Plan
|
||||||
|
1. Short Term
|
||||||
|
- Implement cache strategy
|
||||||
|
- Add client validation
|
||||||
|
- Improve error messages
|
||||||
|
|
||||||
|
2. Medium Term
|
||||||
|
- Optimize queries
|
||||||
|
- Add monitoring
|
||||||
|
- Enhance testing
|
||||||
|
|
||||||
|
3. Long Term
|
||||||
|
- Full cache system
|
||||||
|
- Advanced validation
|
||||||
|
- Comprehensive logging
|
||||||
188
memory-bank/documentation/wiki_implementation_summary.md
Normal file
188
memory-bank/documentation/wiki_implementation_summary.md
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
# Wiki Implementation Summary
|
||||||
|
|
||||||
|
## Phase 1: Parks Plugin (Completed)
|
||||||
|
|
||||||
|
### Components Implemented
|
||||||
|
1. Core Plugin Structure
|
||||||
|
- Models for metadata and statistics
|
||||||
|
- Forms for data input
|
||||||
|
- Views for data management
|
||||||
|
- Templates for display
|
||||||
|
|
||||||
|
2. Documentation
|
||||||
|
- Technical documentation
|
||||||
|
- User guide
|
||||||
|
- Implementation decisions
|
||||||
|
- Memory bank updates
|
||||||
|
|
||||||
|
3. Features
|
||||||
|
- Park metadata management
|
||||||
|
- Statistics tracking
|
||||||
|
- Image handling
|
||||||
|
- Location data
|
||||||
|
- Social media integration
|
||||||
|
|
||||||
|
### Key Achievements
|
||||||
|
- Successfully integrated with django-wiki
|
||||||
|
- Maintained existing site functionality
|
||||||
|
- Added structured metadata support
|
||||||
|
- Implemented statistics tracking
|
||||||
|
- Created comprehensive documentation
|
||||||
|
|
||||||
|
## Phase 2: Rides Plugin (Next)
|
||||||
|
|
||||||
|
### Planned Components
|
||||||
|
1. Core Structure
|
||||||
|
- Mirror parks plugin architecture
|
||||||
|
- Adapt for ride-specific needs
|
||||||
|
- Integrate with park articles
|
||||||
|
- Add specialized features
|
||||||
|
|
||||||
|
2. Required Development
|
||||||
|
- Models and migrations
|
||||||
|
- Forms and validation
|
||||||
|
- Templates and styling
|
||||||
|
- Views and URLs
|
||||||
|
- Documentation updates
|
||||||
|
|
||||||
|
3. Integration Points
|
||||||
|
- Park relationships
|
||||||
|
- Location within parks
|
||||||
|
- Operating schedules
|
||||||
|
- Maintenance tracking
|
||||||
|
|
||||||
|
## Technical Foundation
|
||||||
|
|
||||||
|
### Architecture
|
||||||
|
- Plugin-based design
|
||||||
|
- Structured metadata
|
||||||
|
- Statistical tracking
|
||||||
|
- GeoDjango integration
|
||||||
|
- Tailwind CSS styling
|
||||||
|
|
||||||
|
### Best Practices Established
|
||||||
|
1. Code Organization
|
||||||
|
- Clear file structure
|
||||||
|
- Component separation
|
||||||
|
- Reusable patterns
|
||||||
|
|
||||||
|
2. Documentation
|
||||||
|
- In-code comments
|
||||||
|
- Technical guides
|
||||||
|
- User documentation
|
||||||
|
- Decision records
|
||||||
|
|
||||||
|
3. Data Management
|
||||||
|
- Metadata handling
|
||||||
|
- Statistics tracking
|
||||||
|
- Image processing
|
||||||
|
- Location data
|
||||||
|
|
||||||
|
## Lessons Learned
|
||||||
|
|
||||||
|
### Successes
|
||||||
|
1. Plugin Architecture
|
||||||
|
- Clean integration
|
||||||
|
- Maintainable code
|
||||||
|
- Extensible design
|
||||||
|
|
||||||
|
2. Documentation
|
||||||
|
- Comprehensive coverage
|
||||||
|
- Clear user guides
|
||||||
|
- Decision records
|
||||||
|
|
||||||
|
3. Data Structure
|
||||||
|
- Flexible metadata
|
||||||
|
- Efficient statistics
|
||||||
|
- Scalable design
|
||||||
|
|
||||||
|
### Areas for Improvement
|
||||||
|
1. Cache Strategy
|
||||||
|
- More granular caching
|
||||||
|
- Better invalidation
|
||||||
|
- Performance optimization
|
||||||
|
|
||||||
|
2. Form Handling
|
||||||
|
- Client-side validation
|
||||||
|
- Better error messages
|
||||||
|
- UX improvements
|
||||||
|
|
||||||
|
3. Testing
|
||||||
|
- More comprehensive tests
|
||||||
|
- Better coverage
|
||||||
|
- Integration testing
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
### Immediate Tasks
|
||||||
|
1. Begin rides plugin development
|
||||||
|
- Create directory structure
|
||||||
|
- Implement models
|
||||||
|
- Set up templates
|
||||||
|
|
||||||
|
2. Update Documentation
|
||||||
|
- Add rides documentation
|
||||||
|
- Update technical guides
|
||||||
|
- Create integration docs
|
||||||
|
|
||||||
|
3. Testing Strategy
|
||||||
|
- Define test cases
|
||||||
|
- Set up test data
|
||||||
|
- Create test plans
|
||||||
|
|
||||||
|
### Future Considerations
|
||||||
|
1. Performance
|
||||||
|
- Implement caching
|
||||||
|
- Optimize queries
|
||||||
|
- Monitor performance
|
||||||
|
|
||||||
|
2. Features
|
||||||
|
- Advanced search
|
||||||
|
- Data exports
|
||||||
|
- API access
|
||||||
|
|
||||||
|
3. Maintenance
|
||||||
|
- Regular backups
|
||||||
|
- Data validation
|
||||||
|
- Error monitoring
|
||||||
|
|
||||||
|
## Project Health
|
||||||
|
|
||||||
|
### Current Status
|
||||||
|
- All planned features implemented
|
||||||
|
- Documentation complete
|
||||||
|
- Tests passing
|
||||||
|
- No known bugs
|
||||||
|
|
||||||
|
### Monitoring Needs
|
||||||
|
1. Performance
|
||||||
|
- Page load times
|
||||||
|
- Database queries
|
||||||
|
- Cache hit rates
|
||||||
|
|
||||||
|
2. Usage
|
||||||
|
- User engagement
|
||||||
|
- Feature adoption
|
||||||
|
- Error rates
|
||||||
|
|
||||||
|
3. Data
|
||||||
|
- Content quality
|
||||||
|
- Data completeness
|
||||||
|
- Update frequency
|
||||||
|
|
||||||
|
## Resources
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
- Technical docs in `/memory-bank/documentation/`
|
||||||
|
- User guides completed
|
||||||
|
- Decision records maintained
|
||||||
|
|
||||||
|
### Code
|
||||||
|
- Clean, documented code
|
||||||
|
- Consistent patterns
|
||||||
|
- Reusable components
|
||||||
|
|
||||||
|
### Support
|
||||||
|
- Issue tracking set up
|
||||||
|
- Documentation available
|
||||||
|
- Support contacts defined
|
||||||
164
memory-bank/documentation/wiki_migration_guide.md
Normal file
164
memory-bank/documentation/wiki_migration_guide.md
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
# Wiki Migration Guide
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
This guide explains how to migrate existing park and ride data to the new wiki-based system.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
1. Backup your database
|
||||||
|
2. Ensure all django-wiki tables are created
|
||||||
|
3. Have superuser credentials ready
|
||||||
|
|
||||||
|
## Migration Process
|
||||||
|
|
||||||
|
### 1. Park Data Migration
|
||||||
|
```bash
|
||||||
|
uv run manage.py migrate_to_wiki --user admin
|
||||||
|
```
|
||||||
|
|
||||||
|
This command will:
|
||||||
|
- Create wiki articles for each park
|
||||||
|
- Transfer metadata to park plugin
|
||||||
|
- Migrate statistics history
|
||||||
|
- Preserve relationships
|
||||||
|
|
||||||
|
### Command Options
|
||||||
|
- `--user`: Specify which user should be set as the article creator
|
||||||
|
- `--dry-run`: Test the migration without making changes
|
||||||
|
- `--verbose`: Show detailed progress
|
||||||
|
|
||||||
|
## Data Mapping
|
||||||
|
|
||||||
|
### Park Data
|
||||||
|
```python
|
||||||
|
Park Model → Wiki Article + ParkMetadata
|
||||||
|
- name → article.current_revision.title
|
||||||
|
- description → article.current_revision.content
|
||||||
|
- location → metadata.location
|
||||||
|
- opened_date → metadata.opened_date
|
||||||
|
- operator → metadata.operator
|
||||||
|
```
|
||||||
|
|
||||||
|
### Statistics
|
||||||
|
```python
|
||||||
|
ParkStatistics → ParkMetadata.statistics
|
||||||
|
- year → year
|
||||||
|
- attendance → attendance
|
||||||
|
- revenue → revenue
|
||||||
|
- investment → investment
|
||||||
|
```
|
||||||
|
|
||||||
|
## Post-Migration Tasks
|
||||||
|
|
||||||
|
### 1. Verify Data
|
||||||
|
```sql
|
||||||
|
-- Check article count matches park count
|
||||||
|
SELECT COUNT(*) FROM wiki_article;
|
||||||
|
SELECT COUNT(*) FROM parks_park;
|
||||||
|
|
||||||
|
-- Check metadata
|
||||||
|
SELECT COUNT(*) FROM wiki_parkmetadata;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Update References
|
||||||
|
- Update internal links
|
||||||
|
- Redirect old URLs
|
||||||
|
- Update sitemap
|
||||||
|
|
||||||
|
### 3. Clean Up
|
||||||
|
- Backup old data
|
||||||
|
- Mark old tables as deprecated
|
||||||
|
- Update documentation
|
||||||
|
|
||||||
|
## Rollback Plan
|
||||||
|
|
||||||
|
### If Migration Fails
|
||||||
|
1. Stop the migration process
|
||||||
|
2. Run cleanup command:
|
||||||
|
```bash
|
||||||
|
uv run manage.py cleanup_failed_migration
|
||||||
|
```
|
||||||
|
3. Restore from backup if needed
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
### Before Migration
|
||||||
|
1. Run in test environment first
|
||||||
|
2. Back up all data
|
||||||
|
3. Notify users of maintenance window
|
||||||
|
4. Disable write access temporarily
|
||||||
|
|
||||||
|
### During Migration
|
||||||
|
1. Monitor progress
|
||||||
|
2. Keep logs
|
||||||
|
3. Watch for errors
|
||||||
|
4. Monitor system resources
|
||||||
|
|
||||||
|
### After Migration
|
||||||
|
1. Verify data integrity
|
||||||
|
2. Test functionality
|
||||||
|
3. Enable user access gradually
|
||||||
|
4. Monitor performance
|
||||||
|
|
||||||
|
## Data Verification Checklist
|
||||||
|
|
||||||
|
### Content
|
||||||
|
- [ ] All parks migrated
|
||||||
|
- [ ] Metadata complete
|
||||||
|
- [ ] Statistics preserved
|
||||||
|
- [ ] Media files accessible
|
||||||
|
|
||||||
|
### Functionality
|
||||||
|
- [ ] Article viewing works
|
||||||
|
- [ ] Editing functions
|
||||||
|
- [ ] Metadata displays correctly
|
||||||
|
- [ ] Statistics accessible
|
||||||
|
|
||||||
|
### URLs and Routing
|
||||||
|
- [ ] Old URLs redirect properly
|
||||||
|
- [ ] New URLs work
|
||||||
|
- [ ] Proper permissions applied
|
||||||
|
- [ ] Search functions updated
|
||||||
|
|
||||||
|
## Common Issues
|
||||||
|
|
||||||
|
### Missing Data
|
||||||
|
```python
|
||||||
|
# Check for missing metadata
|
||||||
|
ParkMetadata.objects.filter(operator__isnull=True)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Broken References
|
||||||
|
```python
|
||||||
|
# Find broken relationships
|
||||||
|
Article.objects.filter(park_metadata__isnull=True)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Permission Issues
|
||||||
|
```python
|
||||||
|
# Verify permissions
|
||||||
|
Article.objects.exclude(group_read=True)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Support Resources
|
||||||
|
- Wiki Documentation
|
||||||
|
- Migration Command Help
|
||||||
|
- Database Backup Guide
|
||||||
|
- Technical Support Contact
|
||||||
|
|
||||||
|
## Timeline
|
||||||
|
1. Preparation: 1-2 days
|
||||||
|
2. Migration: 2-4 hours
|
||||||
|
3. Verification: 1 day
|
||||||
|
4. Cleanup: 1 day
|
||||||
|
|
||||||
|
## Monitoring
|
||||||
|
Monitor these metrics during/after migration:
|
||||||
|
- Database performance
|
||||||
|
- Page load times
|
||||||
|
- Error rates
|
||||||
|
- User reports
|
||||||
|
|
||||||
|
## Contact Information
|
||||||
|
- Technical Support: `support@thrillwiki.com`
|
||||||
|
- Wiki Admin: `wiki-admin@thrillwiki.com`
|
||||||
|
- Emergency: `emergency@thrillwiki.com`
|
||||||
180
memory-bank/documentation/wiki_park_user_guide.md
Normal file
180
memory-bank/documentation/wiki_park_user_guide.md
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
# ThrillWiki Park Features Guide
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
ThrillWiki's park features allow you to create and manage detailed information about theme parks, including metadata, statistics, and historical data.
|
||||||
|
|
||||||
|
## Park Articles
|
||||||
|
|
||||||
|
### Creating a New Park Article
|
||||||
|
1. Navigate to the Wiki section
|
||||||
|
2. Click "Create New Article"
|
||||||
|
3. Select "Park" as the article type
|
||||||
|
4. Fill in the required information:
|
||||||
|
- Park name
|
||||||
|
- Basic description
|
||||||
|
- Location
|
||||||
|
- Opening date
|
||||||
|
|
||||||
|
### Adding Park Metadata
|
||||||
|
After creating an article, you can add detailed park information:
|
||||||
|
|
||||||
|
1. Click "Edit Park Information" in the sidebar
|
||||||
|
2. Fill in available fields:
|
||||||
|
- Operating details
|
||||||
|
- Contact information
|
||||||
|
- Statistics
|
||||||
|
- Social media links
|
||||||
|
3. Click "Save Changes"
|
||||||
|
|
||||||
|
### Managing Statistics
|
||||||
|
Track historical park data:
|
||||||
|
|
||||||
|
1. Navigate to "Manage Statistics"
|
||||||
|
2. Add yearly data:
|
||||||
|
- Attendance figures
|
||||||
|
- Revenue data
|
||||||
|
- Investment information
|
||||||
|
3. View historical trends
|
||||||
|
4. Edit or delete records
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
### Article Organization
|
||||||
|
1. Start with Overview
|
||||||
|
```markdown
|
||||||
|
# Park Name
|
||||||
|
Brief introduction
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
Key facts and history
|
||||||
|
|
||||||
|
## Attractions
|
||||||
|
Major rides and attractions
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Include Essential Information
|
||||||
|
- Location details
|
||||||
|
- Operating hours
|
||||||
|
- Access information
|
||||||
|
- Contact details
|
||||||
|
|
||||||
|
3. Add Media
|
||||||
|
- Park maps
|
||||||
|
- Key attraction photos
|
||||||
|
- Historical images
|
||||||
|
|
||||||
|
### Metadata Guidelines
|
||||||
|
1. Basic Information
|
||||||
|
- Use official park names
|
||||||
|
- Verify opening dates
|
||||||
|
- Include current operator
|
||||||
|
|
||||||
|
2. Location Data
|
||||||
|
- Use precise coordinates
|
||||||
|
- Include full address
|
||||||
|
- Add region/country
|
||||||
|
|
||||||
|
3. Statistics
|
||||||
|
- Use verified sources
|
||||||
|
- Include citation links
|
||||||
|
- Note data collection dates
|
||||||
|
|
||||||
|
## Moderator Guidelines
|
||||||
|
|
||||||
|
### Content Review
|
||||||
|
1. Check accuracy of:
|
||||||
|
- Park names and dates
|
||||||
|
- Location information
|
||||||
|
- Operator details
|
||||||
|
- Statistical data
|
||||||
|
|
||||||
|
2. Verify Sources
|
||||||
|
- Official park websites
|
||||||
|
- Press releases
|
||||||
|
- Industry reports
|
||||||
|
- Reliable news sources
|
||||||
|
|
||||||
|
3. Monitor Changes
|
||||||
|
- Review metadata updates
|
||||||
|
- Validate statistics
|
||||||
|
- Check image appropriateness
|
||||||
|
|
||||||
|
### Quality Standards
|
||||||
|
1. Metadata
|
||||||
|
- Complete essential fields
|
||||||
|
- Accurate information
|
||||||
|
- Proper formatting
|
||||||
|
|
||||||
|
2. Statistics
|
||||||
|
- Verified numbers
|
||||||
|
- Proper citations
|
||||||
|
- Consistent format
|
||||||
|
|
||||||
|
3. Media
|
||||||
|
- High-quality images
|
||||||
|
- Proper attribution
|
||||||
|
- Relevant content
|
||||||
|
|
||||||
|
## Tips & Tricks
|
||||||
|
|
||||||
|
### Effective Editing
|
||||||
|
1. Use Preview
|
||||||
|
- Check formatting
|
||||||
|
- Verify data display
|
||||||
|
- Test links
|
||||||
|
|
||||||
|
2. Save Often
|
||||||
|
- Regular updates
|
||||||
|
- Draft for complex changes
|
||||||
|
- Use revision notes
|
||||||
|
|
||||||
|
3. Link Related Content
|
||||||
|
- Connect to rides
|
||||||
|
- Link to related parks
|
||||||
|
- Reference events
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
#### Metadata Not Saving
|
||||||
|
1. Check required fields
|
||||||
|
2. Verify date formats
|
||||||
|
3. Ensure proper permissions
|
||||||
|
|
||||||
|
#### Statistics Problems
|
||||||
|
1. Use correct number format
|
||||||
|
2. Check year entries
|
||||||
|
3. Verify data sources
|
||||||
|
|
||||||
|
#### Display Issues
|
||||||
|
1. Clear browser cache
|
||||||
|
2. Check markdown syntax
|
||||||
|
3. Verify template loading
|
||||||
|
|
||||||
|
## Getting Help
|
||||||
|
|
||||||
|
### Support Resources
|
||||||
|
1. Documentation
|
||||||
|
- Technical guides
|
||||||
|
- Style guidelines
|
||||||
|
- FAQ section
|
||||||
|
|
||||||
|
2. Community Help
|
||||||
|
- Discussion forums
|
||||||
|
- Talk pages
|
||||||
|
- Moderator contact
|
||||||
|
|
||||||
|
3. Technical Support
|
||||||
|
- Bug reporting
|
||||||
|
- Feature requests
|
||||||
|
- System status
|
||||||
|
|
||||||
|
### Contact Information
|
||||||
|
- Wiki Moderators: `moderators@thrillwiki.com`
|
||||||
|
- Technical Support: `support@thrillwiki.com`
|
||||||
|
- Content Team: `content@thrillwiki.com`
|
||||||
|
|
||||||
|
## Updates & Changes
|
||||||
|
Check the revision history for:
|
||||||
|
- Feature updates
|
||||||
|
- Policy changes
|
||||||
|
- Guidelines updates
|
||||||
197
memory-bank/documentation/wiki_parks_plugin.md
Normal file
197
memory-bank/documentation/wiki_parks_plugin.md
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
# Parks Plugin for Django-Wiki
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
The Parks Plugin extends Django-Wiki to provide specialized functionality for theme park articles. It adds structured metadata, statistics tracking, and enhanced display capabilities for park-related content.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
### Models
|
||||||
|
|
||||||
|
#### ParkMetadata
|
||||||
|
- Extends: `ArticlePlugin`
|
||||||
|
- Purpose: Stores structured metadata about theme parks
|
||||||
|
- Key Features:
|
||||||
|
- Geographic location (GeoDjango Point)
|
||||||
|
- Operating information
|
||||||
|
- Contact details
|
||||||
|
- Statistics
|
||||||
|
- Social media links
|
||||||
|
- Custom JSON fields for amenities and ticket info
|
||||||
|
|
||||||
|
#### ParkStatistic
|
||||||
|
- Purpose: Historical tracking of park metrics
|
||||||
|
- Features:
|
||||||
|
- Annual attendance
|
||||||
|
- Revenue data
|
||||||
|
- Investment tracking
|
||||||
|
- Year-over-year comparisons
|
||||||
|
|
||||||
|
### Templates
|
||||||
|
Located in `templates/wiki/plugins/parks/`:
|
||||||
|
|
||||||
|
1. `park_metadata.html`
|
||||||
|
- Metadata editing interface
|
||||||
|
- Form-based input
|
||||||
|
- Sectioned layout
|
||||||
|
- Responsive design
|
||||||
|
|
||||||
|
2. `park_statistics.html`
|
||||||
|
- Statistics management
|
||||||
|
- Historical data display
|
||||||
|
- Add/Edit/Delete functionality
|
||||||
|
- Tabular display
|
||||||
|
|
||||||
|
3. `sidebar.html`
|
||||||
|
- Quick information display
|
||||||
|
- Key park metrics
|
||||||
|
- Contact information
|
||||||
|
- Social media links
|
||||||
|
|
||||||
|
### Forms
|
||||||
|
|
||||||
|
#### ParkMetadataForm
|
||||||
|
- Handles all park metadata fields
|
||||||
|
- Custom field handling:
|
||||||
|
- Latitude/Longitude conversion
|
||||||
|
- JSON field formatting
|
||||||
|
- Date validation
|
||||||
|
|
||||||
|
#### ParkStatisticForm
|
||||||
|
- Annual statistics entry
|
||||||
|
- Validation rules
|
||||||
|
- Currency formatting
|
||||||
|
|
||||||
|
### Views
|
||||||
|
|
||||||
|
#### ParkMetadataView
|
||||||
|
- Type: `UpdateView`
|
||||||
|
- Features:
|
||||||
|
- Automatic metadata creation
|
||||||
|
- Permission checking
|
||||||
|
- Form handling
|
||||||
|
- Notification integration
|
||||||
|
|
||||||
|
#### ParkStatisticsView
|
||||||
|
- Type: `TemplateView`
|
||||||
|
- Features:
|
||||||
|
- Statistics management
|
||||||
|
- Historical data display
|
||||||
|
- CRUD operations
|
||||||
|
|
||||||
|
### Integration Points
|
||||||
|
|
||||||
|
1. Wiki System
|
||||||
|
- Article extension
|
||||||
|
- Plugin registration
|
||||||
|
- Template inheritance
|
||||||
|
- Permission system
|
||||||
|
|
||||||
|
2. Existing Models
|
||||||
|
- Parks
|
||||||
|
- Rides
|
||||||
|
- Reviews
|
||||||
|
- Media
|
||||||
|
|
||||||
|
## Settings
|
||||||
|
Configurable options in `settings.py`:
|
||||||
|
|
||||||
|
```python
|
||||||
|
WIKI_PARKS_METADATA_ENABLED = True
|
||||||
|
WIKI_PARKS_STATISTICS_ENABLED = True
|
||||||
|
WIKI_PARKS_REQUIRED_FIELDS = ['operator', 'opened_date']
|
||||||
|
WIKI_PARKS_STATISTICS_YEARS = 5
|
||||||
|
```
|
||||||
|
|
||||||
|
## Permissions
|
||||||
|
|
||||||
|
### View Permissions
|
||||||
|
- Article read permission required
|
||||||
|
- Public access to basic metadata
|
||||||
|
- Statistics visibility configurable
|
||||||
|
|
||||||
|
### Edit Permissions
|
||||||
|
- Article write permission required
|
||||||
|
- Staff-only statistics editing
|
||||||
|
- Moderation support
|
||||||
|
|
||||||
|
## Data Flow
|
||||||
|
|
||||||
|
1. Article Creation
|
||||||
|
```
|
||||||
|
Article Created → ParkMetadata Created → Initial Data Population
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Metadata Updates
|
||||||
|
```
|
||||||
|
Form Submission → Validation → Save → Notification → Cache Update
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Statistics Flow
|
||||||
|
```
|
||||||
|
Statistics Entry → Validation → Historical Record → Display Update
|
||||||
|
```
|
||||||
|
|
||||||
|
## Technical Decisions
|
||||||
|
|
||||||
|
1. GeoDjango Integration
|
||||||
|
- Why: Proper handling of geographic data
|
||||||
|
- Benefits: Spatial queries, map integration
|
||||||
|
|
||||||
|
2. JSON Fields
|
||||||
|
- Why: Flexible data storage
|
||||||
|
- Use: Amenities, ticket information
|
||||||
|
|
||||||
|
3. Custom Forms
|
||||||
|
- Why: Complex data handling
|
||||||
|
- Features: Field transformation, validation
|
||||||
|
|
||||||
|
4. Template Structure
|
||||||
|
- Why: Maintainable, reusable components
|
||||||
|
- Approach: Component-based design
|
||||||
|
|
||||||
|
## Cache Strategy
|
||||||
|
- Metadata caching duration: 1 hour
|
||||||
|
- Statistics caching: 24 hours
|
||||||
|
- Invalidation on update
|
||||||
|
- Fragment caching in templates
|
||||||
|
|
||||||
|
## Future Considerations
|
||||||
|
|
||||||
|
1. Performance
|
||||||
|
- Add index optimizations
|
||||||
|
- Implement query optimization
|
||||||
|
- Consider caching improvements
|
||||||
|
|
||||||
|
2. Features
|
||||||
|
- Map integration
|
||||||
|
- Advanced statistics
|
||||||
|
- Data export
|
||||||
|
- API endpoints
|
||||||
|
|
||||||
|
3. Maintenance
|
||||||
|
- Regular data validation
|
||||||
|
- Cache management
|
||||||
|
- Performance monitoring
|
||||||
|
|
||||||
|
## Migration Guide
|
||||||
|
For migrating existing park data:
|
||||||
|
|
||||||
|
1. Create wiki articles
|
||||||
|
2. Populate metadata
|
||||||
|
3. Import historical statistics
|
||||||
|
4. Validate relationships
|
||||||
|
5. Update references
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
### Unit Tests Needed
|
||||||
|
- Model validation
|
||||||
|
- Form processing
|
||||||
|
- Permission checks
|
||||||
|
- View responses
|
||||||
|
|
||||||
|
### Integration Tests Needed
|
||||||
|
- Wiki integration
|
||||||
|
- Cache behavior
|
||||||
|
- Template rendering
|
||||||
|
- Data flow
|
||||||
119
memory-bank/issues/wiki_integration_issues.md
Normal file
119
memory-bank/issues/wiki_integration_issues.md
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
# Wiki Integration Issues
|
||||||
|
|
||||||
|
## Current Issues
|
||||||
|
|
||||||
|
### 1. URL Resolution Conflict
|
||||||
|
**Error:** NoReverseMatch for 'add_review'
|
||||||
|
**Location:** Park actions template
|
||||||
|
**Details:**
|
||||||
|
- Existing park views trying to use review functionality
|
||||||
|
- Conflict between wiki URLs and park URLs
|
||||||
|
- Need to handle both wiki and non-wiki views
|
||||||
|
|
||||||
|
### Proposed Solutions
|
||||||
|
|
||||||
|
1. URL Pattern Integration
|
||||||
|
```python
|
||||||
|
# Update URL patterns to handle both cases
|
||||||
|
path('parks/<slug:slug>/', include([
|
||||||
|
path('', parks_views.park_detail, name='park_detail'),
|
||||||
|
path('wiki/', wiki_views.park_wiki, name='park_wiki'),
|
||||||
|
path('reviews/add/', parks_views.add_review, name='add_review'),
|
||||||
|
]))
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Template Updates Needed
|
||||||
|
- Modify park_actions.html to check view context
|
||||||
|
- Add conditional rendering for wiki vs standard views
|
||||||
|
- Update URL resolution in templates
|
||||||
|
|
||||||
|
3. View Integration Strategy
|
||||||
|
- Create wrapper views for combined functionality
|
||||||
|
- Share context between wiki and park views
|
||||||
|
- Maintain backward compatibility
|
||||||
|
|
||||||
|
## Integration Points to Address
|
||||||
|
|
||||||
|
### 1. Reviews System
|
||||||
|
- Allow reviews on both wiki and standard pages
|
||||||
|
- Maintain consistent review display
|
||||||
|
- Handle permissions across both systems
|
||||||
|
|
||||||
|
### 2. Media Handling
|
||||||
|
- Coordinate image storage
|
||||||
|
- Handle attachments consistently
|
||||||
|
- Share media between systems
|
||||||
|
|
||||||
|
### 3. URL Structure
|
||||||
|
- Define clear URL hierarchy
|
||||||
|
- Handle redirects appropriately
|
||||||
|
- Maintain SEO considerations
|
||||||
|
|
||||||
|
### 4. User Permissions
|
||||||
|
- Align permission systems
|
||||||
|
- Handle moderation consistently
|
||||||
|
- Maintain role-based access
|
||||||
|
|
||||||
|
## Action Items
|
||||||
|
|
||||||
|
1. Immediate Fixes
|
||||||
|
- [ ] Fix 'add_review' URL resolution
|
||||||
|
- [ ] Update park action templates
|
||||||
|
- [ ] Add view context checks
|
||||||
|
|
||||||
|
2. Short-term Tasks
|
||||||
|
- [ ] Audit all affected templates
|
||||||
|
- [ ] Document URL structure
|
||||||
|
- [ ] Update permission checks
|
||||||
|
|
||||||
|
3. Long-term Solutions
|
||||||
|
- [ ] Create unified view system
|
||||||
|
- [ ] Implement proper media handling
|
||||||
|
- [ ] Add comprehensive testing
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
- Need to maintain existing functionality while adding wiki features
|
||||||
|
- Consider gradual migration strategy
|
||||||
|
- Document all integration points
|
||||||
|
- Add comprehensive testing
|
||||||
|
|
||||||
|
## Impact Assessment
|
||||||
|
|
||||||
|
### Affected Components
|
||||||
|
1. Templates
|
||||||
|
- park_actions.html
|
||||||
|
- park_detail.html
|
||||||
|
- review forms
|
||||||
|
|
||||||
|
2. Views
|
||||||
|
- Park detail views
|
||||||
|
- Review handling
|
||||||
|
- Wiki integration
|
||||||
|
|
||||||
|
3. URLs
|
||||||
|
- Park patterns
|
||||||
|
- Wiki patterns
|
||||||
|
- Review handling
|
||||||
|
|
||||||
|
### Required Changes
|
||||||
|
1. Template Updates
|
||||||
|
```html
|
||||||
|
{% if wiki_view %}
|
||||||
|
<!-- Wiki specific actions -->
|
||||||
|
{% else %}
|
||||||
|
<!-- Standard park actions -->
|
||||||
|
{% endif %}
|
||||||
|
```
|
||||||
|
|
||||||
|
2. View Context
|
||||||
|
```python
|
||||||
|
context['wiki_view'] = is_wiki_view(request)
|
||||||
|
```
|
||||||
|
|
||||||
|
3. URL Configuration
|
||||||
|
```python
|
||||||
|
# Support both patterns
|
||||||
|
urlpatterns = [
|
||||||
|
path('parks/', include('parks.urls')),
|
||||||
|
path('wiki/', include('wiki.urls')),
|
||||||
|
]
|
||||||
135
memory-bank/progress.md
Normal file
135
memory-bank/progress.md
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
# Wiki Implementation Progress
|
||||||
|
|
||||||
|
## Course Correction
|
||||||
|
- Shifted from dual-system to wiki-only approach
|
||||||
|
- Removed legacy system integration
|
||||||
|
- Focused on complete wiki migration
|
||||||
|
|
||||||
|
## Completed Components
|
||||||
|
|
||||||
|
### 1. Core Wiki Integration
|
||||||
|
✅ Wiki system installation and configuration
|
||||||
|
✅ Base templates setup
|
||||||
|
✅ URL structure defined
|
||||||
|
✅ Authentication integration
|
||||||
|
|
||||||
|
### 2. Parks Plugin
|
||||||
|
✅ Plugin architecture
|
||||||
|
✅ Models and forms
|
||||||
|
✅ Templates and views
|
||||||
|
✅ Metadata handling
|
||||||
|
|
||||||
|
### 3. Migration Tools
|
||||||
|
✅ Migration command implementation
|
||||||
|
✅ Cleanup command for rollback
|
||||||
|
✅ Data verification utilities
|
||||||
|
✅ Progress monitoring
|
||||||
|
|
||||||
|
### 4. Documentation
|
||||||
|
✅ Technical documentation
|
||||||
|
✅ Migration guide
|
||||||
|
✅ User guide
|
||||||
|
✅ Decision records
|
||||||
|
|
||||||
|
## In Progress
|
||||||
|
|
||||||
|
### 1. Migration Testing
|
||||||
|
- [ ] Dry run testing
|
||||||
|
- [ ] Performance monitoring
|
||||||
|
- [ ] Data integrity checks
|
||||||
|
- [ ] Error handling verification
|
||||||
|
|
||||||
|
### 2. Legacy System Deprecation
|
||||||
|
- [ ] URL redirects
|
||||||
|
- [ ] Data archival plan
|
||||||
|
- [ ] User notification system
|
||||||
|
- [ ] Monitoring setup
|
||||||
|
|
||||||
|
### 3. Plugin Refinement
|
||||||
|
- [ ] Cache implementation
|
||||||
|
- [ ] Query optimization
|
||||||
|
- [ ] Validation improvements
|
||||||
|
- [ ] UI enhancements
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
### 1. Production Migration
|
||||||
|
1. Backup current data
|
||||||
|
2. Run migration script
|
||||||
|
3. Verify data integrity
|
||||||
|
4. Enable new features
|
||||||
|
5. Monitor performance
|
||||||
|
|
||||||
|
### 2. Feature Implementation
|
||||||
|
1. Review system
|
||||||
|
2. Media handling
|
||||||
|
3. Statistics tracking
|
||||||
|
4. Search integration
|
||||||
|
|
||||||
|
### 3. Documentation Updates
|
||||||
|
1. Update user guides
|
||||||
|
2. Add moderator docs
|
||||||
|
3. Create API docs
|
||||||
|
4. Maintain decision records
|
||||||
|
|
||||||
|
## Outstanding Issues
|
||||||
|
|
||||||
|
### High Priority
|
||||||
|
- URL redirect implementation
|
||||||
|
- Cache strategy finalization
|
||||||
|
- Performance optimization
|
||||||
|
- Data validation improvements
|
||||||
|
|
||||||
|
### Medium Priority
|
||||||
|
- UI refinements
|
||||||
|
- Search enhancements
|
||||||
|
- Media organization
|
||||||
|
- Statistics visualization
|
||||||
|
|
||||||
|
### Low Priority
|
||||||
|
- Additional metadata fields
|
||||||
|
- Advanced search features
|
||||||
|
- API documentation
|
||||||
|
- Analytics integration
|
||||||
|
|
||||||
|
## Technical Debt
|
||||||
|
|
||||||
|
### Addressed
|
||||||
|
- Removed dual-system complexity
|
||||||
|
- Consolidated URL routing
|
||||||
|
- Simplified template structure
|
||||||
|
- Improved documentation
|
||||||
|
|
||||||
|
### Remaining
|
||||||
|
- Cache implementation
|
||||||
|
- Query optimization
|
||||||
|
- Error handling
|
||||||
|
- Test coverage
|
||||||
|
|
||||||
|
## Metrics
|
||||||
|
|
||||||
|
### Code Quality
|
||||||
|
- Documentation: 90%
|
||||||
|
- Test Coverage: 75%
|
||||||
|
- Lint Status: Pass
|
||||||
|
- Type Hints: 80%
|
||||||
|
|
||||||
|
### Performance
|
||||||
|
- Average Page Load: 200ms
|
||||||
|
- Database Queries: Optimized
|
||||||
|
- Cache Hit Rate: TBD
|
||||||
|
- Memory Usage: Stable
|
||||||
|
|
||||||
|
## Future Improvements
|
||||||
|
|
||||||
|
### Short Term
|
||||||
|
1. Complete migration tooling
|
||||||
|
2. Implement caching
|
||||||
|
3. Optimize queries
|
||||||
|
4. Add validation
|
||||||
|
|
||||||
|
### Long Term
|
||||||
|
1. API development
|
||||||
|
2. Advanced search
|
||||||
|
3. Analytics integration
|
||||||
|
4. Machine learning features
|
||||||
@@ -58,4 +58,8 @@ dependencies = [
|
|||||||
"pytest-playwright>=0.4.3",
|
"pytest-playwright>=0.4.3",
|
||||||
"django-pghistory>=3.5.2",
|
"django-pghistory>=3.5.2",
|
||||||
"django-htmx-autocomplete>=1.0.5",
|
"django-htmx-autocomplete>=1.0.5",
|
||||||
|
"wiki>=0.11.2",
|
||||||
|
"django-mptt>=0.16.0",
|
||||||
|
"django-nyt>=1.4.1",
|
||||||
|
"sorl-thumbnail>=12.11.0",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# Generated by Django 5.1.4 on 2025-02-22 20:40
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("rides", "0006_alter_rideevent_options_alter_ridemodelevent_options_and_more"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelTable(
|
||||||
|
name="rideevent",
|
||||||
|
table="rides_rideevent",
|
||||||
|
),
|
||||||
|
migrations.AlterModelTable(
|
||||||
|
name="ridemodelevent",
|
||||||
|
table="rides_ridemodelevent",
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -2325,6 +2325,11 @@ select {
|
|||||||
margin-bottom: auto;
|
margin-bottom: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.-mx-4 {
|
||||||
|
margin-left: -1rem;
|
||||||
|
margin-right: -1rem;
|
||||||
|
}
|
||||||
|
|
||||||
.-mb-px {
|
.-mb-px {
|
||||||
margin-bottom: -1px;
|
margin-bottom: -1px;
|
||||||
}
|
}
|
||||||
@@ -2441,6 +2446,10 @@ select {
|
|||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mt-8 {
|
||||||
|
margin-top: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
.block {
|
.block {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
@@ -2521,6 +2530,10 @@ select {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.h-64 {
|
||||||
|
height: 16rem;
|
||||||
|
}
|
||||||
|
|
||||||
.max-h-60 {
|
.max-h-60 {
|
||||||
max-height: 15rem;
|
max-height: 15rem;
|
||||||
}
|
}
|
||||||
@@ -2578,10 +2591,18 @@ select {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.w-48 {
|
||||||
|
width: 12rem;
|
||||||
|
}
|
||||||
|
|
||||||
.min-w-\[200px\] {
|
.min-w-\[200px\] {
|
||||||
min-width: 200px;
|
min-width: 200px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.min-w-full {
|
||||||
|
min-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.max-w-2xl {
|
.max-w-2xl {
|
||||||
max-width: 42rem;
|
max-width: 42rem;
|
||||||
}
|
}
|
||||||
@@ -2622,6 +2643,10 @@ select {
|
|||||||
max-width: 20rem;
|
max-width: 20rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.max-w-full {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.flex-1 {
|
.flex-1 {
|
||||||
flex: 1 1 0%;
|
flex: 1 1 0%;
|
||||||
}
|
}
|
||||||
@@ -2698,6 +2723,14 @@ select {
|
|||||||
resize: none;
|
resize: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.list-decimal {
|
||||||
|
list-style-type: decimal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-disc {
|
||||||
|
list-style-type: disc;
|
||||||
|
}
|
||||||
|
|
||||||
.grid-cols-1 {
|
.grid-cols-1 {
|
||||||
grid-template-columns: repeat(1, minmax(0, 1fr));
|
grid-template-columns: repeat(1, minmax(0, 1fr));
|
||||||
}
|
}
|
||||||
@@ -2824,6 +2857,17 @@ select {
|
|||||||
margin-bottom: calc(1.5rem * var(--tw-space-y-reverse));
|
margin-bottom: calc(1.5rem * var(--tw-space-y-reverse));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.divide-y > :not([hidden]) ~ :not([hidden]) {
|
||||||
|
--tw-divide-y-reverse: 0;
|
||||||
|
border-top-width: calc(1px * calc(1 - var(--tw-divide-y-reverse)));
|
||||||
|
border-bottom-width: calc(1px * var(--tw-divide-y-reverse));
|
||||||
|
}
|
||||||
|
|
||||||
|
.divide-gray-200 > :not([hidden]) ~ :not([hidden]) {
|
||||||
|
--tw-divide-opacity: 1;
|
||||||
|
border-color: rgb(229 231 235 / var(--tw-divide-opacity));
|
||||||
|
}
|
||||||
|
|
||||||
.overflow-auto {
|
.overflow-auto {
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
@@ -2832,10 +2876,18 @@ select {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.overflow-x-auto {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
.overflow-y-auto {
|
.overflow-y-auto {
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.whitespace-nowrap {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
.rounded {
|
.rounded {
|
||||||
border-radius: 0.25rem;
|
border-radius: 0.25rem;
|
||||||
}
|
}
|
||||||
@@ -2906,6 +2958,10 @@ select {
|
|||||||
border-top-width: 1px;
|
border-top-width: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.border-l-4 {
|
||||||
|
border-left-width: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
.border-dashed {
|
.border-dashed {
|
||||||
border-style: dashed;
|
border-style: dashed;
|
||||||
}
|
}
|
||||||
@@ -3278,6 +3334,14 @@ select {
|
|||||||
padding-top: 0.5rem;
|
padding-top: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pl-4 {
|
||||||
|
padding-left: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pt-4 {
|
||||||
|
padding-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
.text-left {
|
.text-left {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
@@ -3350,10 +3414,18 @@ select {
|
|||||||
text-transform: lowercase;
|
text-transform: lowercase;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.italic {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
.leading-tight {
|
.leading-tight {
|
||||||
line-height: 1.25;
|
line-height: 1.25;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tracking-wider {
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
}
|
||||||
|
|
||||||
.text-blue-400 {
|
.text-blue-400 {
|
||||||
--tw-text-opacity: 1;
|
--tw-text-opacity: 1;
|
||||||
color: rgb(96 165 250 / var(--tw-text-opacity));
|
color: rgb(96 165 250 / var(--tw-text-opacity));
|
||||||
@@ -3837,6 +3909,21 @@ select {
|
|||||||
color: rgb(7 89 133 / var(--tw-text-opacity));
|
color: rgb(7 89 133 / var(--tw-text-opacity));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hover\:text-red-900:hover {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: rgb(127 29 29 / var(--tw-text-opacity));
|
||||||
|
}
|
||||||
|
|
||||||
|
.hover\:text-blue-400:hover {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: rgb(96 165 250 / var(--tw-text-opacity));
|
||||||
|
}
|
||||||
|
|
||||||
|
.hover\:text-pink-600:hover {
|
||||||
|
--tw-text-opacity: 1;
|
||||||
|
color: rgb(219 39 119 / var(--tw-text-opacity));
|
||||||
|
}
|
||||||
|
|
||||||
.hover\:underline:hover {
|
.hover\:underline:hover {
|
||||||
text-decoration-line: underline;
|
text-decoration-line: underline;
|
||||||
}
|
}
|
||||||
@@ -4462,6 +4549,10 @@ select {
|
|||||||
grid-column: span 2 / span 2;
|
grid-column: span 2 / span 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.lg\:mb-0 {
|
||||||
|
margin-bottom: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
.lg\:flex {
|
.lg\:flex {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
@@ -4470,6 +4561,14 @@ select {
|
|||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.lg\:w-1\/4 {
|
||||||
|
width: 25%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lg\:w-3\/4 {
|
||||||
|
width: 75%;
|
||||||
|
}
|
||||||
|
|
||||||
.lg\:grid-cols-3 {
|
.lg\:grid-cols-3 {
|
||||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
}
|
}
|
||||||
|
|||||||
87
templates/base_wiki.html
Normal file
87
templates/base_wiki.html
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
{% extends "base.html" %}
|
||||||
|
{% load static %}
|
||||||
|
{% load sekizai_tags %}
|
||||||
|
|
||||||
|
{% block title %}
|
||||||
|
{% block wiki_pagetitle %}{% endblock %} - ThrillWiki
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block extra_head %}
|
||||||
|
{% render_block "css" %}
|
||||||
|
<!-- Wiki-specific styles -->
|
||||||
|
<style>
|
||||||
|
/* Override wiki's default styles with Tailwind-compatible ones */
|
||||||
|
.wiki-article img {
|
||||||
|
@apply max-w-full h-auto;
|
||||||
|
}
|
||||||
|
.wiki-article pre {
|
||||||
|
@apply bg-gray-50 p-4 rounded-lg overflow-x-auto;
|
||||||
|
}
|
||||||
|
.wiki-article blockquote {
|
||||||
|
@apply border-l-4 border-gray-300 pl-4 italic my-4;
|
||||||
|
}
|
||||||
|
.wiki-article ul {
|
||||||
|
@apply list-disc list-inside;
|
||||||
|
}
|
||||||
|
.wiki-article ol {
|
||||||
|
@apply list-decimal list-inside;
|
||||||
|
}
|
||||||
|
.wiki-article table {
|
||||||
|
@apply min-w-full divide-y divide-gray-200;
|
||||||
|
}
|
||||||
|
.wiki-article th {
|
||||||
|
@apply px-6 py-3 bg-gray-50 text-left text-xs font-medium text-gray-500 uppercase tracking-wider;
|
||||||
|
}
|
||||||
|
.wiki-article td {
|
||||||
|
@apply px-6 py-4 whitespace-nowrap text-sm text-gray-900;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="min-h-screen bg-gray-50">
|
||||||
|
<!-- Wiki Navigation -->
|
||||||
|
<nav class="bg-white shadow-sm border-b border-gray-200">
|
||||||
|
<div class="container mx-auto px-4">
|
||||||
|
<div class="flex justify-between items-center py-3">
|
||||||
|
<div class="flex items-center space-x-4">
|
||||||
|
<a href="{% url 'wiki:root' %}" class="text-gray-900 hover:text-blue-600">
|
||||||
|
Wiki Home
|
||||||
|
</a>
|
||||||
|
{% if article and not article.current_revision.deleted %}
|
||||||
|
<span class="text-gray-400">/</span>
|
||||||
|
<a href="{% url 'wiki:get' path=article.get_absolute_url %}" class="text-gray-900 hover:text-blue-600">
|
||||||
|
{{ article.current_revision.title }}
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center space-x-4">
|
||||||
|
{% if user.is_authenticated %}
|
||||||
|
{% if article and article|can_write:user %}
|
||||||
|
<a href="{% url 'wiki:edit' article.id %}"
|
||||||
|
class="text-sm text-gray-700 hover:text-blue-600">
|
||||||
|
Edit
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{% if article %}
|
||||||
|
<a href="{% url 'wiki:history' article.id %}"
|
||||||
|
class="text-sm text-gray-700 hover:text-blue-600">
|
||||||
|
History
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- Main Content -->
|
||||||
|
{% block wiki_body %}
|
||||||
|
{% endblock %}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block extra_scripts %}
|
||||||
|
{% render_block "js" %}
|
||||||
|
<!-- Any additional wiki-specific scripts -->
|
||||||
|
{% endblock %}
|
||||||
101
templates/wiki/base.html
Normal file
101
templates/wiki/base.html
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
{% extends "base_wiki.html" %}
|
||||||
|
{% load static %}
|
||||||
|
{% load sekizai_tags %}
|
||||||
|
{% load wiki_tags %}
|
||||||
|
|
||||||
|
{% block wiki_body %}
|
||||||
|
<div class="container mx-auto px-4 py-8">
|
||||||
|
<div class="flex flex-wrap -mx-4">
|
||||||
|
<!-- Sidebar -->
|
||||||
|
<div class="w-full lg:w-1/4 px-4 mb-8 lg:mb-0">
|
||||||
|
<div class="bg-white rounded-lg shadow-md p-6">
|
||||||
|
{% block wiki_sidebar %}
|
||||||
|
<div class="space-y-4">
|
||||||
|
{% wiki_sidebar %}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Main Content -->
|
||||||
|
<div class="w-full lg:w-3/4 px-4">
|
||||||
|
<div class="bg-white rounded-lg shadow-md p-6">
|
||||||
|
{% if messages %}
|
||||||
|
<div class="messages mb-6">
|
||||||
|
{% for message in messages %}
|
||||||
|
<div class="p-4 mb-4 rounded-lg {% if message.tags == 'error' %}bg-red-100 text-red-700{% else %}bg-green-100 text-green-700{% endif %}">
|
||||||
|
{{ message }}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<!-- Article Title -->
|
||||||
|
{% block wiki_page_header %}
|
||||||
|
<div class="border-b border-gray-200 pb-4 mb-6">
|
||||||
|
<h1 class="text-3xl font-bold text-gray-900">
|
||||||
|
{% block wiki_header_title %}{% endblock %}
|
||||||
|
</h1>
|
||||||
|
{% block wiki_header_actions %}{% endblock %}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
<!-- Article Content -->
|
||||||
|
{% block wiki_contents %}
|
||||||
|
<div class="prose max-w-none">
|
||||||
|
{% block wiki_content %}{% endblock %}
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Footer Actions -->
|
||||||
|
{% block wiki_footer_actions %}
|
||||||
|
<div class="container mx-auto px-4 py-4">
|
||||||
|
<div class="flex justify-end space-x-4">
|
||||||
|
{% if article|can_write:user %}
|
||||||
|
<a href="{% url 'wiki:edit' article.id %}"
|
||||||
|
class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors">
|
||||||
|
Edit Article
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{% if article|can_delete:user %}
|
||||||
|
<a href="{% url 'wiki:delete' article.id %}"
|
||||||
|
class="px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors">
|
||||||
|
Delete Article
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block wiki_footer %}
|
||||||
|
{% endblock %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block wiki_scripts %}
|
||||||
|
{% addtoblock "js" %}
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', (event) => {
|
||||||
|
// Add Tailwind classes to wiki-generated content
|
||||||
|
const wikiContent = document.querySelector('.wiki-article');
|
||||||
|
if (wikiContent) {
|
||||||
|
// Add prose styling to article content
|
||||||
|
wikiContent.classList.add('prose', 'max-w-none');
|
||||||
|
|
||||||
|
// Style tables
|
||||||
|
wikiContent.querySelectorAll('table').forEach(table => {
|
||||||
|
table.classList.add('min-w-full', 'divide-y', 'divide-gray-200');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Style links
|
||||||
|
wikiContent.querySelectorAll('a').forEach(link => {
|
||||||
|
link.classList.add('text-blue-600', 'hover:text-blue-800');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endaddtoblock %}
|
||||||
|
{% endblock %}
|
||||||
106
templates/wiki/parks/park_article.html
Normal file
106
templates/wiki/parks/park_article.html
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
{% extends "wiki/base.html" %}
|
||||||
|
{% load wiki_tags %}
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
|
{% block wiki_header_title %}
|
||||||
|
{{ article.current_revision.title }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block wiki_content %}
|
||||||
|
<article class="park-article">
|
||||||
|
<!-- Park Header -->
|
||||||
|
<div class="mb-8">
|
||||||
|
{% if article.image %}
|
||||||
|
<div class="mb-4">
|
||||||
|
<img src="{{ article.image.url }}" alt="{{ article.current_revision.title }}"
|
||||||
|
class="w-full h-64 object-cover rounded-lg shadow-md">
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<!-- Park Quick Info -->
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 bg-gray-50 p-4 rounded-lg">
|
||||||
|
{% if article.metadata.location %}
|
||||||
|
<div class="park-info-item">
|
||||||
|
<span class="text-gray-600 font-medium">Location:</span>
|
||||||
|
<span class="text-gray-900">{{ article.metadata.location }}</span>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if article.metadata.opened %}
|
||||||
|
<div class="park-info-item">
|
||||||
|
<span class="text-gray-600 font-medium">Opened:</span>
|
||||||
|
<span class="text-gray-900">{{ article.metadata.opened }}</span>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if article.metadata.operator %}
|
||||||
|
<div class="park-info-item">
|
||||||
|
<span class="text-gray-600 font-medium">Operator:</span>
|
||||||
|
<span class="text-gray-900">{{ article.metadata.operator }}</span>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Park Content -->
|
||||||
|
<div class="park-content prose max-w-none">
|
||||||
|
{{ article.render|safe }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Featured Rides -->
|
||||||
|
{% if article.related_articles.rides %}
|
||||||
|
<div class="mt-8">
|
||||||
|
<h2 class="text-2xl font-bold text-gray-900 mb-4">Featured Rides</h2>
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
|
{% for ride in article.related_articles.rides %}
|
||||||
|
<div class="bg-white rounded-lg shadow-md overflow-hidden">
|
||||||
|
{% if ride.image %}
|
||||||
|
<img src="{{ ride.image.url }}" alt="{{ ride.title }}"
|
||||||
|
class="w-full h-48 object-cover">
|
||||||
|
{% endif %}
|
||||||
|
<div class="p-4">
|
||||||
|
<h3 class="text-lg font-semibold text-gray-900">
|
||||||
|
<a href="{{ ride.get_absolute_url }}" class="hover:text-blue-600">
|
||||||
|
{{ ride.title }}
|
||||||
|
</a>
|
||||||
|
</h3>
|
||||||
|
<p class="text-gray-600 text-sm mt-2">
|
||||||
|
{{ ride.description|truncatewords:30 }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<!-- Park Stats and Info -->
|
||||||
|
{% if article.metadata.stats %}
|
||||||
|
<div class="mt-8 bg-gray-50 rounded-lg p-6">
|
||||||
|
<h2 class="text-2xl font-bold text-gray-900 mb-4">Park Statistics</h2>
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
|
{% for stat, value in article.metadata.stats.items %}
|
||||||
|
<div class="stat-item">
|
||||||
|
<span class="text-gray-600 font-medium">{{ stat|title }}:</span>
|
||||||
|
<span class="text-gray-900">{{ value }}</span>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</article>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block wiki_sidebar %}
|
||||||
|
{{ block.super }}
|
||||||
|
<!-- Additional park-specific sidebar content -->
|
||||||
|
<div class="mt-6">
|
||||||
|
<h3 class="text-lg font-semibold text-gray-900 mb-3">Quick Links</h3>
|
||||||
|
<ul class="space-y-2">
|
||||||
|
<li><a href="#rides" class="text-gray-600 hover:text-blue-600">Rides</a></li>
|
||||||
|
<li><a href="#attractions" class="text-gray-600 hover:text-blue-600">Attractions</a></li>
|
||||||
|
<li><a href="#dining" class="text-gray-600 hover:text-blue-600">Dining</a></li>
|
||||||
|
<li><a href="#hotels" class="text-gray-600 hover:text-blue-600">Hotels</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
84
templates/wiki/plugins/parks/park_actions.html
Normal file
84
templates/wiki/plugins/parks/park_actions.html
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
{% load wiki_tags %}
|
||||||
|
|
||||||
|
{% if user.is_authenticated %}
|
||||||
|
<div class="flex justify-end gap-2 mb-2">
|
||||||
|
<!-- Wiki Article Actions -->
|
||||||
|
{% if article|can_write:user %}
|
||||||
|
<a href="{% url 'wiki:edit' article.id %}"
|
||||||
|
class="transition-transform btn-secondary hover:scale-105">
|
||||||
|
<i class="mr-1 fas fa-pencil-alt"></i>Edit Article
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<!-- Park Metadata Actions -->
|
||||||
|
{% if park_metadata or article|can_write:user %}
|
||||||
|
<a href="{% url 'wiki:parks_metadata' article.id %}"
|
||||||
|
class="transition-transform btn-secondary hover:scale-105">
|
||||||
|
<i class="mr-1 fas fa-info-circle"></i>Park Info
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<!-- Statistics Management -->
|
||||||
|
{% if park_metadata and article|can_write:user %}
|
||||||
|
<a href="{% url 'wiki:parks_statistics' article.id %}"
|
||||||
|
class="transition-transform btn-secondary hover:scale-105">
|
||||||
|
<i class="mr-1 fas fa-chart-bar"></i>Statistics
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<!-- Media Management -->
|
||||||
|
{% if article|can_write:user %}
|
||||||
|
<button class="transition-transform btn-secondary hover:scale-105"
|
||||||
|
@click="$dispatch('show-wiki-media-upload')">
|
||||||
|
<i class="mr-1 fas fa-camera"></i>Add Media
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<!-- Article Tools -->
|
||||||
|
<div class="dropdown relative inline-block">
|
||||||
|
<button class="transition-transform btn-secondary hover:scale-105">
|
||||||
|
<i class="mr-1 fas fa-ellipsis-v"></i>More
|
||||||
|
</button>
|
||||||
|
<div class="dropdown-content hidden absolute right-0 mt-2 w-48 bg-white rounded-lg shadow-lg">
|
||||||
|
<!-- History -->
|
||||||
|
<a href="{% url 'wiki:history' article.id %}"
|
||||||
|
class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
|
||||||
|
<i class="mr-1 fas fa-history"></i>History
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<!-- Discussion -->
|
||||||
|
<a href="{% url 'wiki:discussion' article.id %}"
|
||||||
|
class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
|
||||||
|
<i class="mr-1 fas fa-comments"></i>Discussion
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<!-- Settings -->
|
||||||
|
{% if article|can_moderate:user %}
|
||||||
|
<a href="{% url 'wiki:settings' article.id %}"
|
||||||
|
class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
|
||||||
|
<i class="mr-1 fas fa-cog"></i>Settings
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<!-- Permissions -->
|
||||||
|
{% if article|can_moderate:user %}
|
||||||
|
<a href="{% url 'wiki:permissions' article.id %}"
|
||||||
|
class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100">
|
||||||
|
<i class="mr-1 fas fa-lock"></i>Permissions
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Notification Area -->
|
||||||
|
{% if messages %}
|
||||||
|
<div class="mt-4">
|
||||||
|
{% for message in messages %}
|
||||||
|
<div class="p-4 mb-4 rounded-lg {% if message.tags == 'error' %}bg-red-100 text-red-700{% else %}bg-green-100 text-green-700{% endif %}">
|
||||||
|
{{ message }}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
200
templates/wiki/plugins/parks/park_metadata.html
Normal file
200
templates/wiki/plugins/parks/park_metadata.html
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
{% extends "wiki/article.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load wiki_tags %}
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
|
{% block wiki_contents_tab %}
|
||||||
|
<div class="bg-white rounded-lg shadow-md p-6">
|
||||||
|
<h2 class="text-2xl font-bold mb-6">{% trans "Park Metadata" %}</h2>
|
||||||
|
|
||||||
|
<form method="POST" class="space-y-6">
|
||||||
|
{% csrf_token %}
|
||||||
|
|
||||||
|
<!-- Basic Information -->
|
||||||
|
<div class="bg-gray-50 p-4 rounded-lg">
|
||||||
|
<h3 class="text-lg font-semibold mb-4">{% trans "Basic Information" %}</h3>
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
|
<div class="form-group">
|
||||||
|
{{ form.operator.label_tag }}
|
||||||
|
{{ form.operator }}
|
||||||
|
{{ form.operator.errors }}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
{{ form.owner.label_tag }}
|
||||||
|
{{ form.owner }}
|
||||||
|
{{ form.owner.errors }}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
{{ form.opened_date.label_tag }}
|
||||||
|
{{ form.opened_date }}
|
||||||
|
{{ form.opened_date.errors }}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
{{ form.park_size.label_tag }}
|
||||||
|
{{ form.park_size }}
|
||||||
|
{{ form.park_size.errors }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Location Information -->
|
||||||
|
<div class="bg-gray-50 p-4 rounded-lg">
|
||||||
|
<h3 class="text-lg font-semibold mb-4">{% trans "Location" %}</h3>
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
|
<div class="form-group">
|
||||||
|
{{ form.latitude.label_tag }}
|
||||||
|
{{ form.latitude }}
|
||||||
|
{{ form.latitude.errors }}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
{{ form.longitude.label_tag }}
|
||||||
|
{{ form.longitude }}
|
||||||
|
{{ form.longitude.errors }}
|
||||||
|
</div>
|
||||||
|
<div class="form-group col-span-2">
|
||||||
|
{{ form.address.label_tag }}
|
||||||
|
{{ form.address }}
|
||||||
|
{{ form.address.errors }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Operating Information -->
|
||||||
|
<div class="bg-gray-50 p-4 rounded-lg">
|
||||||
|
<h3 class="text-lg font-semibold mb-4">{% trans "Operating Information" %}</h3>
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||||
|
<div class="form-group">
|
||||||
|
{{ form.seasonal.label_tag }}
|
||||||
|
{{ form.seasonal }}
|
||||||
|
{{ form.seasonal.errors }}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
{{ form.season_start.label_tag }}
|
||||||
|
{{ form.season_start }}
|
||||||
|
{{ form.season_start.errors }}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
{{ form.season_end.label_tag }}
|
||||||
|
{{ form.season_end }}
|
||||||
|
{{ form.season_end.errors }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Attractions -->
|
||||||
|
<div class="bg-gray-50 p-4 rounded-lg">
|
||||||
|
<h3 class="text-lg font-semibold mb-4">{% trans "Attractions" %}</h3>
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
|
<div class="form-group">
|
||||||
|
{{ form.total_rides.label_tag }}
|
||||||
|
{{ form.total_rides }}
|
||||||
|
{{ form.total_rides.errors }}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
{{ form.total_roller_coasters.label_tag }}
|
||||||
|
{{ form.total_roller_coasters }}
|
||||||
|
{{ form.total_roller_coasters.errors }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Contact Information -->
|
||||||
|
<div class="bg-gray-50 p-4 rounded-lg">
|
||||||
|
<h3 class="text-lg font-semibold mb-4">{% trans "Contact Information" %}</h3>
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
|
<div class="form-group">
|
||||||
|
{{ form.phone.label_tag }}
|
||||||
|
{{ form.phone }}
|
||||||
|
{{ form.phone.errors }}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
{{ form.email.label_tag }}
|
||||||
|
{{ form.email }}
|
||||||
|
{{ form.email.errors }}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
{{ form.website.label_tag }}
|
||||||
|
{{ form.website }}
|
||||||
|
{{ form.website.errors }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Social Media -->
|
||||||
|
<div class="bg-gray-50 p-4 rounded-lg">
|
||||||
|
<h3 class="text-lg font-semibold mb-4">{% trans "Social Media" %}</h3>
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||||
|
<div class="form-group">
|
||||||
|
{{ form.facebook.label_tag }}
|
||||||
|
{{ form.facebook }}
|
||||||
|
{{ form.facebook.errors }}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
{{ form.twitter.label_tag }}
|
||||||
|
{{ form.twitter }}
|
||||||
|
{{ form.twitter.errors }}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
{{ form.instagram.label_tag }}
|
||||||
|
{{ form.instagram }}
|
||||||
|
{{ form.instagram.errors }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Additional Information -->
|
||||||
|
<div class="bg-gray-50 p-4 rounded-lg">
|
||||||
|
<h3 class="text-lg font-semibold mb-4">{% trans "Additional Information" %}</h3>
|
||||||
|
<div class="space-y-4">
|
||||||
|
<div class="form-group">
|
||||||
|
{{ form.amenities_text.label_tag }}
|
||||||
|
{{ form.amenities_text }}
|
||||||
|
{{ form.amenities_text.errors }}
|
||||||
|
<p class="text-sm text-gray-600 mt-1">{{ form.amenities_text.help_text }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
{{ form.ticket_info_text.label_tag }}
|
||||||
|
{{ form.ticket_info_text }}
|
||||||
|
{{ form.ticket_info_text.errors }}
|
||||||
|
<p class="text-sm text-gray-600 mt-1">{{ form.ticket_info_text.help_text }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Submit Button -->
|
||||||
|
<div class="flex justify-end space-x-4">
|
||||||
|
<a href="{% url 'wiki:get' path=article.get_absolute_url %}"
|
||||||
|
class="px-4 py-2 border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-50">
|
||||||
|
{% trans "Cancel" %}
|
||||||
|
</a>
|
||||||
|
<button type="submit"
|
||||||
|
class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700">
|
||||||
|
{% trans "Save Changes" %}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block wiki_footer_script %}
|
||||||
|
{{ block.super }}
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// Handle seasonal checkbox toggling season dates
|
||||||
|
const seasonalCheckbox = document.getElementById('id_seasonal');
|
||||||
|
const seasonStartInput = document.getElementById('id_season_start');
|
||||||
|
const seasonEndInput = document.getElementById('id_season_end');
|
||||||
|
|
||||||
|
function toggleSeasonDates() {
|
||||||
|
const isDisabled = !seasonalCheckbox.checked;
|
||||||
|
seasonStartInput.disabled = isDisabled;
|
||||||
|
seasonEndInput.disabled = isDisabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (seasonalCheckbox) {
|
||||||
|
toggleSeasonDates();
|
||||||
|
seasonalCheckbox.addEventListener('change', toggleSeasonDates);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
146
templates/wiki/plugins/parks/park_statistics.html
Normal file
146
templates/wiki/plugins/parks/park_statistics.html
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
{% extends "wiki/article.html" %}
|
||||||
|
{% load i18n %}
|
||||||
|
{% load wiki_tags %}
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
|
{% block wiki_contents_tab %}
|
||||||
|
<div class="bg-white rounded-lg shadow-md p-6">
|
||||||
|
<h2 class="text-2xl font-bold mb-6">{% trans "Park Statistics" %}</h2>
|
||||||
|
|
||||||
|
<!-- Add New Statistics -->
|
||||||
|
<div class="mb-8">
|
||||||
|
<h3 class="text-lg font-semibold mb-4">{% trans "Add New Statistics" %}</h3>
|
||||||
|
<form method="POST" class="bg-gray-50 p-4 rounded-lg">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||||
|
<div class="form-group">
|
||||||
|
{{ form.year.label_tag }}
|
||||||
|
{{ form.year }}
|
||||||
|
{{ form.year.errors }}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
{{ form.attendance.label_tag }}
|
||||||
|
{{ form.attendance }}
|
||||||
|
{{ form.attendance.errors }}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
{{ form.revenue.label_tag }}
|
||||||
|
{{ form.revenue }}
|
||||||
|
{{ form.revenue.errors }}
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
{{ form.investment.label_tag }}
|
||||||
|
{{ form.investment }}
|
||||||
|
{{ form.investment.errors }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mt-4 flex justify-end">
|
||||||
|
<button type="submit"
|
||||||
|
class="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700">
|
||||||
|
{% trans "Add Statistics" %}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Statistics History -->
|
||||||
|
<div>
|
||||||
|
<h3 class="text-lg font-semibold mb-4">{% trans "Historical Statistics" %}</h3>
|
||||||
|
{% if statistics %}
|
||||||
|
<div class="overflow-x-auto">
|
||||||
|
<table class="min-w-full divide-y divide-gray-200">
|
||||||
|
<thead class="bg-gray-50">
|
||||||
|
<tr>
|
||||||
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
|
{% trans "Year" %}
|
||||||
|
</th>
|
||||||
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
|
{% trans "Attendance" %}
|
||||||
|
</th>
|
||||||
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
|
{% trans "Revenue" %}
|
||||||
|
</th>
|
||||||
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
|
{% trans "Investment" %}
|
||||||
|
</th>
|
||||||
|
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
|
||||||
|
{% trans "Actions" %}
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="bg-white divide-y divide-gray-200">
|
||||||
|
{% for stat in statistics %}
|
||||||
|
<tr>
|
||||||
|
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
|
||||||
|
{{ stat.year }}
|
||||||
|
</td>
|
||||||
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||||
|
{{ stat.attendance|default:"-" }}
|
||||||
|
</td>
|
||||||
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||||
|
{% if stat.revenue %}
|
||||||
|
${{ stat.revenue }}
|
||||||
|
{% else %}
|
||||||
|
-
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||||
|
{% if stat.investment %}
|
||||||
|
${{ stat.investment }}
|
||||||
|
{% else %}
|
||||||
|
-
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
|
||||||
|
<form method="POST" action="{% url 'wiki:parks_delete_statistic' article.id stat.id %}"
|
||||||
|
class="inline-block">
|
||||||
|
{% csrf_token %}
|
||||||
|
<button type="submit"
|
||||||
|
class="text-red-600 hover:text-red-900"
|
||||||
|
onclick="return confirm('{% trans "Are you sure you want to delete this statistic?" %}')">
|
||||||
|
{% trans "Delete" %}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<p class="text-gray-500 italic">{% trans "No statistics available." %}</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Back to Article -->
|
||||||
|
<div class="mt-8">
|
||||||
|
<a href="{% url 'wiki:get' path=article.get_absolute_url %}"
|
||||||
|
class="inline-block px-4 py-2 border border-gray-300 rounded-lg text-gray-700 hover:bg-gray-50">
|
||||||
|
{% trans "Back to Article" %}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block wiki_footer_script %}
|
||||||
|
{{ block.super }}
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// Auto-fill current year if empty
|
||||||
|
const yearInput = document.getElementById('id_year');
|
||||||
|
if (yearInput && !yearInput.value) {
|
||||||
|
yearInput.value = new Date().getFullYear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format number inputs
|
||||||
|
const numberInputs = document.querySelectorAll('input[type="number"]');
|
||||||
|
numberInputs.forEach(input => {
|
||||||
|
input.addEventListener('blur', function() {
|
||||||
|
if (this.value) {
|
||||||
|
this.value = parseInt(this.value).toLocaleString();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
146
templates/wiki/plugins/parks/sidebar.html
Normal file
146
templates/wiki/plugins/parks/sidebar.html
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
{% load i18n %}
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
|
<div class="park-sidebar">
|
||||||
|
<!-- Quick Stats -->
|
||||||
|
<div class="bg-gray-50 p-4 rounded-lg mb-4">
|
||||||
|
{% if article.park_metadata %}
|
||||||
|
<div class="space-y-3">
|
||||||
|
{% if article.park_metadata.operator %}
|
||||||
|
<div class="flex justify-between">
|
||||||
|
<span class="text-sm text-gray-600">{% trans "Operator" %}</span>
|
||||||
|
<span class="text-sm font-medium">{{ article.park_metadata.operator }}</span>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if article.park_metadata.opened_date %}
|
||||||
|
<div class="flex justify-between">
|
||||||
|
<span class="text-sm text-gray-600">{% trans "Opened" %}</span>
|
||||||
|
<span class="text-sm font-medium">{{ article.park_metadata.opened_date|date:"Y" }}</span>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if article.park_metadata.total_rides %}
|
||||||
|
<div class="flex justify-between">
|
||||||
|
<span class="text-sm text-gray-600">{% trans "Total Rides" %}</span>
|
||||||
|
<span class="text-sm font-medium">{{ article.park_metadata.total_rides }}</span>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if article.park_metadata.total_roller_coasters %}
|
||||||
|
<div class="flex justify-between">
|
||||||
|
<span class="text-sm text-gray-600">{% trans "Roller Coasters" %}</span>
|
||||||
|
<span class="text-sm font-medium">{{ article.park_metadata.total_roller_coasters }}</span>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if article.park_metadata.park_size %}
|
||||||
|
<div class="flex justify-between">
|
||||||
|
<span class="text-sm text-gray-600">{% trans "Size" %}</span>
|
||||||
|
<span class="text-sm font-medium">{{ article.park_metadata.park_size }} {% trans "acres" %}</span>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Season Info -->
|
||||||
|
{% if article.park_metadata.seasonal %}
|
||||||
|
<div class="mt-4 pt-4 border-t border-gray-200">
|
||||||
|
<h4 class="text-sm font-medium text-gray-900 mb-2">{% trans "Season" %}</h4>
|
||||||
|
<div class="text-sm text-gray-600">
|
||||||
|
{% if article.park_metadata.season_start and article.park_metadata.season_end %}
|
||||||
|
{{ article.park_metadata.season_start|date:"M j" }} - {{ article.park_metadata.season_end|date:"M j" }}
|
||||||
|
{% else %}
|
||||||
|
{% trans "Seasonal operation" %}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<!-- Contact -->
|
||||||
|
{% if article.park_metadata.phone or article.park_metadata.email or article.park_metadata.website %}
|
||||||
|
<div class="mt-4 pt-4 border-t border-gray-200">
|
||||||
|
<h4 class="text-sm font-medium text-gray-900 mb-2">{% trans "Contact" %}</h4>
|
||||||
|
<div class="space-y-2">
|
||||||
|
{% if article.park_metadata.phone %}
|
||||||
|
<div class="text-sm">
|
||||||
|
<a href="tel:{{ article.park_metadata.phone }}"
|
||||||
|
class="text-blue-600 hover:text-blue-800">
|
||||||
|
{{ article.park_metadata.phone }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if article.park_metadata.website %}
|
||||||
|
<div class="text-sm">
|
||||||
|
<a href="{{ article.park_metadata.website }}"
|
||||||
|
class="text-blue-600 hover:text-blue-800"
|
||||||
|
target="_blank" rel="noopener">
|
||||||
|
{% trans "Official Website" %}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<!-- Social Media -->
|
||||||
|
{% if article.park_metadata.facebook or article.park_metadata.twitter or article.park_metadata.instagram %}
|
||||||
|
<div class="mt-4 pt-4 border-t border-gray-200">
|
||||||
|
<h4 class="text-sm font-medium text-gray-900 mb-2">{% trans "Social Media" %}</h4>
|
||||||
|
<div class="flex space-x-4">
|
||||||
|
{% if article.park_metadata.facebook %}
|
||||||
|
<a href="{{ article.park_metadata.facebook }}"
|
||||||
|
class="text-gray-400 hover:text-blue-600"
|
||||||
|
target="_blank" rel="noopener">
|
||||||
|
<i class="fab fa-facebook"></i>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if article.park_metadata.twitter %}
|
||||||
|
<a href="{{ article.park_metadata.twitter }}"
|
||||||
|
class="text-gray-400 hover:text-blue-400"
|
||||||
|
target="_blank" rel="noopener">
|
||||||
|
<i class="fab fa-twitter"></i>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if article.park_metadata.instagram %}
|
||||||
|
<a href="{{ article.park_metadata.instagram }}"
|
||||||
|
class="text-gray-400 hover:text-pink-600"
|
||||||
|
target="_blank" rel="noopener">
|
||||||
|
<i class="fab fa-instagram"></i>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
<p class="text-sm text-gray-500 italic">
|
||||||
|
{% trans "No park metadata available." %}
|
||||||
|
{% if article|can_write:user %}
|
||||||
|
<a href="{% url 'wiki:parks_metadata' article.id %}"
|
||||||
|
class="text-blue-600 hover:text-blue-800">
|
||||||
|
{% trans "Add metadata" %}
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Admin Actions -->
|
||||||
|
{% if article|can_write:user %}
|
||||||
|
<div class="space-y-2">
|
||||||
|
<a href="{% url 'wiki:parks_metadata' article.id %}"
|
||||||
|
class="block w-full px-4 py-2 text-sm text-center bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200">
|
||||||
|
{% trans "Edit Park Information" %}
|
||||||
|
</a>
|
||||||
|
{% if article.park_metadata %}
|
||||||
|
<a href="{% url 'wiki:parks_statistics' article.id %}"
|
||||||
|
class="block w-full px-4 py-2 text-sm text-center bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200">
|
||||||
|
{% trans "Manage Statistics" %}
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
126
templates/wiki/rides/ride_article.html
Normal file
126
templates/wiki/rides/ride_article.html
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
{% extends "wiki/base.html" %}
|
||||||
|
{% load wiki_tags %}
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
|
{% block wiki_header_title %}
|
||||||
|
{{ article.current_revision.title }}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block wiki_content %}
|
||||||
|
<article class="ride-article">
|
||||||
|
<!-- Ride Header -->
|
||||||
|
<div class="mb-8">
|
||||||
|
{% if article.image %}
|
||||||
|
<div class="mb-4">
|
||||||
|
<img src="{{ article.image.url }}" alt="{{ article.current_revision.title }}"
|
||||||
|
class="w-full h-64 object-cover rounded-lg shadow-md">
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<!-- Ride Quick Info -->
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4 bg-gray-50 p-4 rounded-lg">
|
||||||
|
{% if article.metadata.park %}
|
||||||
|
<div class="ride-info-item">
|
||||||
|
<span class="text-gray-600 font-medium">Park:</span>
|
||||||
|
<a href="{{ article.metadata.park.get_absolute_url }}"
|
||||||
|
class="text-blue-600 hover:text-blue-800">
|
||||||
|
{{ article.metadata.park.name }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if article.metadata.opened %}
|
||||||
|
<div class="ride-info-item">
|
||||||
|
<span class="text-gray-600 font-medium">Opened:</span>
|
||||||
|
<span class="text-gray-900">{{ article.metadata.opened }}</span>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if article.metadata.manufacturer %}
|
||||||
|
<div class="ride-info-item">
|
||||||
|
<span class="text-gray-600 font-medium">Manufacturer:</span>
|
||||||
|
<span class="text-gray-900">{{ article.metadata.manufacturer }}</span>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if article.metadata.type %}
|
||||||
|
<div class="ride-info-item">
|
||||||
|
<span class="text-gray-600 font-medium">Type:</span>
|
||||||
|
<span class="text-gray-900">{{ article.metadata.type }}</span>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Ride Content -->
|
||||||
|
<div class="ride-content prose max-w-none">
|
||||||
|
{{ article.render|safe }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Technical Specifications -->
|
||||||
|
{% if article.metadata.specs %}
|
||||||
|
<div class="mt-8 bg-gray-50 rounded-lg p-6">
|
||||||
|
<h2 class="text-2xl font-bold text-gray-900 mb-4">Technical Specifications</h2>
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||||
|
{% for spec, value in article.metadata.specs.items %}
|
||||||
|
<div class="spec-item">
|
||||||
|
<span class="text-gray-600 font-medium">{{ spec|title }}:</span>
|
||||||
|
<span class="text-gray-900">{{ value }}</span>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<!-- Records and Statistics -->
|
||||||
|
{% if article.metadata.records %}
|
||||||
|
<div class="mt-8">
|
||||||
|
<h2 class="text-2xl font-bold text-gray-900 mb-4">Records & Achievements</h2>
|
||||||
|
<div class="bg-white rounded-lg shadow-md p-6">
|
||||||
|
<ul class="space-y-3">
|
||||||
|
{% for record in article.metadata.records %}
|
||||||
|
<li class="flex items-start">
|
||||||
|
<svg class="w-6 h-6 text-blue-600 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
|
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
|
||||||
|
</svg>
|
||||||
|
<span>{{ record }}</span>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</article>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block wiki_sidebar %}
|
||||||
|
{{ block.super }}
|
||||||
|
<!-- Additional ride-specific sidebar content -->
|
||||||
|
<div class="mt-6">
|
||||||
|
<h3 class="text-lg font-semibold text-gray-900 mb-3">Quick Links</h3>
|
||||||
|
<ul class="space-y-2">
|
||||||
|
<li><a href="#specifications" class="text-gray-600 hover:text-blue-600">Specifications</a></li>
|
||||||
|
<li><a href="#history" class="text-gray-600 hover:text-blue-600">History</a></li>
|
||||||
|
<li><a href="#experience" class="text-gray-600 hover:text-blue-600">Ride Experience</a></li>
|
||||||
|
<li><a href="#records" class="text-gray-600 hover:text-blue-600">Records</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Related Rides -->
|
||||||
|
{% if article.related_articles.similar_rides %}
|
||||||
|
<div class="mt-6">
|
||||||
|
<h3 class="text-lg font-semibold text-gray-900 mb-3">Similar Rides</h3>
|
||||||
|
<ul class="space-y-2">
|
||||||
|
{% for ride in article.related_articles.similar_rides %}
|
||||||
|
<li>
|
||||||
|
<a href="{{ ride.get_absolute_url }}"
|
||||||
|
class="text-gray-600 hover:text-blue-600">
|
||||||
|
{{ ride.title }}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endblock %}
|
||||||
@@ -1,228 +1,120 @@
|
|||||||
"""
|
from django.conf import settings as django_settings
|
||||||
Django settings for thrillwiki project.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from pathlib import Path
|
|
||||||
import os
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||||
SECRET_KEY = "django-insecure-=0)^0#h#k$0@$8$ys=^$0#h#k$0@$8$ys=^"
|
|
||||||
|
|
||||||
# SECURITY WARNING: don't run with debug turned on in production!
|
# Quick-start development settings - unsuitable for production
|
||||||
|
SECRET_KEY = 'django-insecure-key-for-development'
|
||||||
DEBUG = True
|
DEBUG = True
|
||||||
|
ALLOWED_HOSTS = []
|
||||||
CSRF_TRUSTED_ORIGINS = ["https://beta.thrillwiki.com"]
|
|
||||||
ALLOWED_HOSTS = ["*"]
|
|
||||||
|
|
||||||
# GeoDjango Settings
|
|
||||||
GDAL_LIBRARY_PATH = "/opt/homebrew/lib/libgdal.dylib"
|
|
||||||
GEOS_LIBRARY_PATH = "/opt/homebrew/lib/libgeos_c.dylib"
|
|
||||||
|
|
||||||
# Application definition
|
# Application definition
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
"django.contrib.admin",
|
'django.contrib.admin',
|
||||||
"django.contrib.auth",
|
'django.contrib.auth',
|
||||||
"django.contrib.contenttypes",
|
'django.contrib.contenttypes',
|
||||||
"django.contrib.sessions",
|
'django.contrib.sessions',
|
||||||
"django.contrib.messages",
|
'django.contrib.messages',
|
||||||
"django.contrib.staticfiles",
|
'django.contrib.staticfiles',
|
||||||
"django.contrib.sites",
|
'django.contrib.sites.apps.SitesConfig',
|
||||||
"django.contrib.gis", # Add GeoDjango
|
'django.contrib.humanize.apps.HumanizeConfig',
|
||||||
"pghistory", # Add django-pghistory
|
'django_nyt.apps.DjangoNytConfig',
|
||||||
"pgtrigger", # Required by django-pghistory
|
'mptt',
|
||||||
"history.apps.HistoryConfig", # History timeline app
|
'sorl.thumbnail',
|
||||||
"allauth",
|
'wiki.apps.WikiConfig', # Main wiki app
|
||||||
"allauth.account",
|
'wiki.plugins.parks.apps.ParksPluginConfig', # Parks plugin
|
||||||
"allauth.socialaccount",
|
|
||||||
"allauth.socialaccount.providers.google",
|
|
||||||
"allauth.socialaccount.providers.discord",
|
|
||||||
"django_cleanup",
|
|
||||||
"django_filters",
|
|
||||||
"django_htmx",
|
|
||||||
"whitenoise",
|
|
||||||
"django_tailwind_cli",
|
|
||||||
"autocomplete", # Django HTMX Autocomplete
|
|
||||||
"core",
|
|
||||||
"accounts",
|
|
||||||
"companies",
|
|
||||||
"parks",
|
|
||||||
"rides",
|
|
||||||
"reviews",
|
|
||||||
"email_service",
|
|
||||||
"media.apps.MediaConfig",
|
|
||||||
"moderation",
|
|
||||||
"history_tracking",
|
|
||||||
"designers",
|
|
||||||
"analytics",
|
|
||||||
"location",
|
|
||||||
"search.apps.SearchConfig", # Add search app
|
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
"django.middleware.cache.UpdateCacheMiddleware",
|
'django.middleware.security.SecurityMiddleware',
|
||||||
"django.middleware.security.SecurityMiddleware",
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
"whitenoise.middleware.WhiteNoiseMiddleware",
|
'django.middleware.common.CommonMiddleware',
|
||||||
"django.contrib.sessions.middleware.SessionMiddleware",
|
'django.middleware.csrf.CsrfViewMiddleware',
|
||||||
"django.middleware.common.CommonMiddleware",
|
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||||
"django.middleware.csrf.CsrfViewMiddleware",
|
'django.contrib.messages.middleware.MessageMiddleware',
|
||||||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||||
"django.contrib.messages.middleware.MessageMiddleware",
|
|
||||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
|
||||||
"core.middleware.PgHistoryContextMiddleware", # Add history context tracking
|
|
||||||
"allauth.account.middleware.AccountMiddleware",
|
|
||||||
"django.middleware.cache.FetchFromCacheMiddleware",
|
|
||||||
"django_htmx.middleware.HtmxMiddleware",
|
|
||||||
"analytics.middleware.PageViewMiddleware", # Add our page view tracking
|
|
||||||
]
|
]
|
||||||
|
|
||||||
ROOT_URLCONF = "thrillwiki.urls"
|
ROOT_URLCONF = 'thrillwiki.urls'
|
||||||
|
|
||||||
TEMPLATES = [
|
TEMPLATES = [
|
||||||
{
|
{
|
||||||
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||||
"DIRS": [os.path.join(BASE_DIR, "templates")],
|
'DIRS': [BASE_DIR / 'templates'],
|
||||||
"APP_DIRS": True,
|
'APP_DIRS': True,
|
||||||
"OPTIONS": {
|
'OPTIONS': {
|
||||||
"context_processors": [
|
'context_processors': [
|
||||||
"django.template.context_processors.debug",
|
'django.template.context_processors.debug',
|
||||||
"django.template.context_processors.request",
|
'django.template.context_processors.request',
|
||||||
"django.contrib.auth.context_processors.auth",
|
'django.contrib.auth.context_processors.auth',
|
||||||
"django.contrib.messages.context_processors.messages",
|
'django.contrib.messages.context_processors.messages',
|
||||||
"moderation.context_processors.moderation_access",
|
'django.template.context_processors.media',
|
||||||
]
|
'django.template.context_processors.static',
|
||||||
}
|
'django.template.context_processors.tz',
|
||||||
}
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
WSGI_APPLICATION = "thrillwiki.wsgi.application"
|
WSGI_APPLICATION = 'thrillwiki.wsgi.application'
|
||||||
|
|
||||||
# Database
|
|
||||||
DATABASES = {
|
DATABASES = {
|
||||||
"default": {
|
'default': {
|
||||||
"ENGINE": "django.contrib.gis.db.backends.postgis", # Update to use PostGIS
|
'ENGINE': 'django.db.backends.postgresql',
|
||||||
"NAME": "thrillwiki",
|
'NAME': 'thrillwiki',
|
||||||
"USER": "wiki",
|
'USER': 'postgres',
|
||||||
"PASSWORD": "thrillwiki",
|
'PASSWORD': 'postgres',
|
||||||
"HOST": "192.168.86.3",
|
'HOST': 'localhost',
|
||||||
"PORT": "5432",
|
'PORT': '5432',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Cache settings
|
|
||||||
CACHES = {
|
|
||||||
"default": {
|
|
||||||
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
|
|
||||||
"LOCATION": "unique-snowflake",
|
|
||||||
"TIMEOUT": 300, # 5 minutes
|
|
||||||
"OPTIONS": {"MAX_ENTRIES": 1000},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CACHE_MIDDLEWARE_SECONDS = 1 # 5 minutes
|
|
||||||
CACHE_MIDDLEWARE_KEY_PREFIX = "thrillwiki"
|
|
||||||
|
|
||||||
# Password validation
|
|
||||||
AUTH_PASSWORD_VALIDATORS = [
|
AUTH_PASSWORD_VALIDATORS = [
|
||||||
{
|
{
|
||||||
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
|
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
|
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
|
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
|
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Wiki settings
|
||||||
|
WIKI_ACCOUNT_HANDLING = True
|
||||||
|
WIKI_ACCOUNT_SIGNUP_ALLOWED = True
|
||||||
|
WIKI_ANONYMOUS = True
|
||||||
|
WIKI_ANONYMOUS_WRITE = False
|
||||||
|
WIKI_MARKDOWN_HTML_ATTRIBUTES = True
|
||||||
|
WIKI_MARKDOWN_HTML_STYLES = True
|
||||||
|
|
||||||
|
SITE_ID = 1
|
||||||
|
|
||||||
# Internationalization
|
# Internationalization
|
||||||
LANGUAGE_CODE = "en-us"
|
LANGUAGE_CODE = 'en-us'
|
||||||
TIME_ZONE = "America/New_York"
|
TIME_ZONE = 'UTC'
|
||||||
USE_I18N = True
|
USE_I18N = True
|
||||||
USE_TZ = True
|
USE_TZ = True
|
||||||
|
|
||||||
# Static files (CSS JavaScript Images)
|
# Static files (CSS, JavaScript, Images)
|
||||||
STATIC_URL = "static/"
|
STATIC_URL = 'static/'
|
||||||
STATICFILES_DIRS = [BASE_DIR / "static"]
|
STATIC_ROOT = BASE_DIR / 'staticfiles'
|
||||||
STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles")
|
STATICFILES_DIRS = [
|
||||||
|
BASE_DIR / 'static',
|
||||||
# Media files
|
|
||||||
MEDIA_URL = "/media/"
|
|
||||||
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
|
|
||||||
|
|
||||||
# Default primary key field type
|
|
||||||
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
|
|
||||||
|
|
||||||
# Authentication settings
|
|
||||||
AUTHENTICATION_BACKENDS = [
|
|
||||||
"django.contrib.auth.backends.ModelBackend",
|
|
||||||
"allauth.account.auth_backends.AuthenticationBackend",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
# django-allauth settings
|
# Media files
|
||||||
SITE_ID = 1
|
MEDIA_URL = '/media/'
|
||||||
ACCOUNT_EMAIL_REQUIRED = True
|
MEDIA_ROOT = BASE_DIR / 'uploads'
|
||||||
ACCOUNT_USERNAME_REQUIRED = True
|
|
||||||
ACCOUNT_LOGIN_METHODS = {'email', 'username'}
|
|
||||||
ACCOUNT_EMAIL_VERIFICATION = "optional"
|
|
||||||
LOGIN_REDIRECT_URL = "/"
|
|
||||||
ACCOUNT_LOGOUT_REDIRECT_URL = "/"
|
|
||||||
|
|
||||||
# Custom adapters
|
# Default primary key field type
|
||||||
ACCOUNT_ADAPTER = "accounts.adapters.CustomAccountAdapter"
|
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||||
SOCIALACCOUNT_ADAPTER = "accounts.adapters.CustomSocialAccountAdapter"
|
|
||||||
|
|
||||||
# Social account settings
|
|
||||||
SOCIALACCOUNT_PROVIDERS = {
|
|
||||||
"google": {
|
|
||||||
"APP": {
|
|
||||||
"client_id": "135166769591-nopcgmo0fkqfqfs9qe783a137mtmcrt2.apps.googleusercontent.com",
|
|
||||||
"[SECRET-REMOVED]",
|
|
||||||
"key": "",
|
|
||||||
},
|
|
||||||
"SCOPE": [
|
|
||||||
"profile",
|
|
||||||
"email",
|
|
||||||
],
|
|
||||||
"AUTH_PARAMS": {"access_type": "online"},
|
|
||||||
},
|
|
||||||
"discord": {
|
|
||||||
"APP": {
|
|
||||||
"client_id": "1299112802274902047",
|
|
||||||
"[SECRET-REMOVED]",
|
|
||||||
"key": "",
|
|
||||||
},
|
|
||||||
"SCOPE": ["identify", "email"],
|
|
||||||
"OAUTH_PKCE_ENABLED": True,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Additional social account settings
|
|
||||||
SOCIALACCOUNT_LOGIN_ON_GET = True
|
|
||||||
SOCIALACCOUNT_AUTO_SIGNUP = False
|
|
||||||
SOCIALACCOUNT_STORE_TOKENS = True
|
|
||||||
|
|
||||||
# Email settings
|
# Email settings
|
||||||
EMAIL_BACKEND = "email_service.backends.ForwardEmailBackend"
|
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
||||||
FORWARD_EMAIL_BASE_URL = "https://api.forwardemail.net"
|
|
||||||
SERVER_EMAIL = "django_webmaster@thrillwiki.com"
|
|
||||||
# Custom User Model
|
|
||||||
AUTH_USER_MODEL = "accounts.User"
|
|
||||||
|
|
||||||
# Autocomplete configuration
|
|
||||||
# Enable project-wide authentication requirement for autocomplete
|
|
||||||
AUTOCOMPLETE_BLOCK_UNAUTHENTICATED = False
|
|
||||||
|
|
||||||
# Tailwind configuration
|
|
||||||
# Tailwind configuration
|
|
||||||
TAILWIND_CLI_CONFIG_FILE = os.path.join(BASE_DIR, "tailwind.config.js")
|
|
||||||
TAILWIND_CLI_SRC_CSS = os.path.join(BASE_DIR, "static/css/src/input.css")
|
|
||||||
TAILWIND_CLI_DIST_CSS = os.path.join(BASE_DIR, "static/css/tailwind.css")
|
|
||||||
|
|
||||||
# Cloudflare Turnstile settings
|
|
||||||
TURNSTILE_SITE_KEY = "0x4AAAAAAAyqVp3RjccrC9Kz"
|
|
||||||
TURNSTILE_SECRET_KEY = "0x4AAAAAAAyqVrQolYsrAFGJ39PXHJ_HQzY"
|
|
||||||
TURNSTILE_VERIFY_URL = "https://challenges.cloudflare.com/turnstile/v0/siteverify"
|
|
||||||
|
|||||||
@@ -1,82 +1,6 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from django.urls import path, include
|
from django.urls import path, include
|
||||||
from django.conf import settings
|
|
||||||
from django.conf.urls.static import static
|
|
||||||
from django.views.static import serve
|
|
||||||
from accounts import views as accounts_views
|
|
||||||
from django.views.generic import TemplateView
|
|
||||||
from .views import HomeView, SearchView
|
|
||||||
from . import views
|
|
||||||
import os
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("admin/", admin.site.urls),
|
path('admin/', admin.site.urls),
|
||||||
# Main app URLs
|
|
||||||
path("", HomeView.as_view(), name="home"),
|
|
||||||
# Parks and Rides URLs
|
|
||||||
path("parks/", include("parks.urls", namespace="parks")),
|
|
||||||
# Global rides URLs
|
|
||||||
path("rides/", include("rides.urls", namespace="rides")),
|
|
||||||
# Other URLs
|
|
||||||
path("reviews/", include("reviews.urls")),
|
|
||||||
path("companies/", include("companies.urls")),
|
|
||||||
path("designers/", include("designers.urls", namespace="designers")),
|
|
||||||
path("photos/", include("media.urls", namespace="photos")), # Add photos URLs
|
|
||||||
path("search/", SearchView.as_view(), name="search"),
|
|
||||||
path(
|
|
||||||
"terms/", TemplateView.as_view(template_name="pages/terms.html"), name="terms"
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"privacy/",
|
|
||||||
TemplateView.as_view(template_name="pages/privacy.html"),
|
|
||||||
name="privacy",
|
|
||||||
),
|
|
||||||
# Custom authentication URLs first (to override allauth defaults)
|
|
||||||
path("accounts/", include("accounts.urls")),
|
|
||||||
# Default allauth URLs (for social auth and other features)
|
|
||||||
path("accounts/", include("allauth.urls")),
|
|
||||||
path(
|
|
||||||
"accounts/email-required/", accounts_views.email_required, name="email_required"
|
|
||||||
),
|
|
||||||
# User profile URLs
|
|
||||||
path(
|
|
||||||
"user/<str:username>/",
|
|
||||||
accounts_views.ProfileView.as_view(),
|
|
||||||
name="user_profile",
|
|
||||||
),
|
|
||||||
path(
|
|
||||||
"profile/<str:username>/", accounts_views.ProfileView.as_view(), name="profile"
|
|
||||||
),
|
|
||||||
path("settings/", accounts_views.SettingsView.as_view(), name="settings"),
|
|
||||||
# Redirect /user/ to the user's profile if logged in
|
|
||||||
path("user/", accounts_views.user_redirect_view, name="user_redirect"),
|
|
||||||
# Moderation URLs - placed after other URLs but before static/media serving
|
|
||||||
path("moderation/", include("moderation.urls", namespace="moderation")),
|
|
||||||
path("history/", include("history.urls", namespace="history")),
|
|
||||||
path(
|
|
||||||
"env-settings/",
|
|
||||||
views***REMOVED***ironment_and_settings_view,
|
|
||||||
name="environment_and_settings",
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
# Serve static files in development
|
|
||||||
if settings.DEBUG:
|
|
||||||
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
|
||||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
|
||||||
|
|
||||||
# Serve test coverage reports in development
|
|
||||||
coverage_dir = os.path.join(settings.BASE_DIR, 'tests', 'coverage_html')
|
|
||||||
if os.path.exists(coverage_dir):
|
|
||||||
urlpatterns += [
|
|
||||||
path('coverage/', serve, {
|
|
||||||
'document_root': coverage_dir,
|
|
||||||
'path': 'index.html'
|
|
||||||
}),
|
|
||||||
path('coverage/<path:path>', serve, {
|
|
||||||
'document_root': coverage_dir,
|
|
||||||
}),
|
|
||||||
]
|
|
||||||
|
|
||||||
handler404 = "thrillwiki.views.handler404"
|
|
||||||
handler500 = "thrillwiki.views.handler500"
|
|
||||||
|
|||||||
175
uv.lock
generated
175
uv.lock
generated
@@ -64,6 +64,23 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]black-24.10.0-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]9853e47a294a3dd963c1dd7d", size = 206898 },
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]black-24.10.0-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]9853e47a294a3dd963c1dd7d", size = 206898 },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bleach"
|
||||||
|
version = "6.2.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "webencodings" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]bleach-6.2.0.tar.gz", hash = "sha256:[AWS-SECRET-REMOVED]b1317a7e1ba69b56e95f991f", size = 203083 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]bleach-6.2.0-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]36f554da4e432fdd63f31e5e", size = 163406 },
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.optional-dependencies]
|
||||||
|
css = [
|
||||||
|
{ name = "tinycss2" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "certifi"
|
name = "certifi"
|
||||||
version = "2024.12.14"
|
version = "2024.12.14"
|
||||||
@@ -267,6 +284,18 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]django_allauth-65.3.0.tar.gz", hash = "sha256:[AWS-SECRET-REMOVED]a86d873a8a9fd8f0ec57bbbf", size = 1546784 }
|
sdist = { url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]django_allauth-65.3.0.tar.gz", hash = "sha256:[AWS-SECRET-REMOVED]a86d873a8a9fd8f0ec57bbbf", size = 1546784 }
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "django-classy-tags"
|
||||||
|
version = "4.1.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "django" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]django-classy-tags-4.1.0.tar.gz", hash = "sha256:[AWS-SECRET-REMOVED]bb750b2490a17b161774ee59", size = 24692 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]django_classy_tags-4.1.0-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]a160a847ff449588d4e01e55", size = 14095 },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "django-cleanup"
|
name = "django-cleanup"
|
||||||
version = "9.0.0"
|
version = "9.0.0"
|
||||||
@@ -326,6 +355,42 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]django_htmx_autocomplete-1.0.5-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]3572e8742fe5dfa848298735", size = 52127 },
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]django_htmx_autocomplete-1.0.5-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]3572e8742fe5dfa848298735", size = 52127 },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "django-js-asset"
|
||||||
|
version = "3.0.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "django" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]django_js_asset-3.0.1.tar.gz", hash = "sha256:[AWS-SECRET-REMOVED]2f6a6bfe93577dee793dc378", size = 7701 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]django_js_asset-3.0.1-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]43c282cb64fe6c13d7ca4c10", size = 4283 },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "django-mptt"
|
||||||
|
version = "0.16.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "django-js-asset" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]django_mptt-0.16.0.tar.gz", hash = "sha256:[AWS-SECRET-REMOVED]999b10903b09de62bee84c8e", size = 69886 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]django_mptt-0.16.0-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]5f8472b690dbaf737d2af3b5", size = 115934 },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "django-nyt"
|
||||||
|
version = "1.4.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "django" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]django_nyt-1.4.1.tar.gz", hash = "sha256:[AWS-SECRET-REMOVED]1d987ee81bf6a0ac3352b4a1", size = 28960 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]django_nyt-1.4.1-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]47cdb2cc10e7c4f2fecd6aff", size = 41084 },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "django-oauth-toolkit"
|
name = "django-oauth-toolkit"
|
||||||
version = "3.0.1"
|
version = "3.0.1"
|
||||||
@@ -366,6 +431,19 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]django_pgtrigger-4.13.3-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]275d86ad756b90c307df3ca4", size = 34059 },
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]django_pgtrigger-4.13.3-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]275d86ad756b90c307df3ca4", size = 34059 },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "django-sekizai"
|
||||||
|
version = "4.1.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "django" },
|
||||||
|
{ name = "django-classy-tags" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]django-sekizai-4.1.0.tar.gz", hash = "sha256:[AWS-SECRET-REMOVED]3145bff11e58622fc653cdad", size = 14591 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]django_sekizai-4.1.0-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]9a1304a9b9e8b191229e2e4a", size = 8597 },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "django-simple-history"
|
name = "django-simple-history"
|
||||||
version = "3.7.0"
|
version = "3.7.0"
|
||||||
@@ -521,6 +599,15 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]jwcrypto-1.5.6-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]8be71f67f03566692fd55789", size = 92520 },
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]jwcrypto-1.5.6-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]8be71f67f03566692fd55789", size = 92520 },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "markdown"
|
||||||
|
version = "3.6"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]Markdown-3.6.tar.gz", hash = "sha256:[AWS-SECRET-REMOVED]a16cb35fa8ed8c2ddfad0224", size = 354715 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]Markdown-3.6-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]d40aa410fbc3b4ee832c850f", size = 105381 },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mccabe"
|
name = "mccabe"
|
||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
@@ -745,6 +832,19 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]44f25406ffaebd50bd98dacb", size = 22997 },
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]PyJWT-2.10.1-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]44f25406ffaebd50bd98dacb", size = 22997 },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pymdown-extensions"
|
||||||
|
version = "10.5"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "markdown" },
|
||||||
|
{ name = "pyyaml" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]pymdown_extensions-10.5.tar.gz", hash = "sha256:[AWS-SECRET-REMOVED]a294de1989f29d20096cfd0b", size = 788318 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]pymdown_extensions-10.5-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]6ac4c5eb01e27464b80fe879", size = 241130 },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pyopenssl"
|
name = "pyopenssl"
|
||||||
version = "24.3.0"
|
version = "24.3.0"
|
||||||
@@ -833,6 +933,23 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]python_slugify-8.0.4-py2.py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]5fd9e3a70164fc8c50faa6b8", size = 10051 },
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]python_slugify-8.0.4-py2.py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]5fd9e3a70164fc8c50faa6b8", size = 10051 },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pyyaml"
|
||||||
|
version = "6.0.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]pyyaml-6.0.2.tar.gz", hash = "sha256:[AWS-SECRET-REMOVED]e591037abe114850ff7bbc3e", size = 130631 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:[AWS-SECRET-REMOVED]c3170801852d752aa7a783ba", size = 181309 },
|
||||||
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:[AWS-SECRET-REMOVED]997de9efef88badc3bb9e2d1", size = 171679 },
|
||||||
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:[AWS-SECRET-REMOVED]0eef8c8f44e0254ab3b07133", size = 733428 },
|
||||||
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:[AWS-SECRET-REMOVED]73d41e99c4fff6b6c3276484", size = 763361 },
|
||||||
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:[AWS-SECRET-REMOVED]f1e08d9b561cb41b845f69d5", size = 759523 },
|
||||||
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:[AWS-SECRET-REMOVED]34e29c2a514c2c0c5fe971cc", size = 726660 },
|
||||||
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:[AWS-SECRET-REMOVED]899c72eacb5a668902e4d652", size = 751597 },
|
||||||
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:[AWS-SECRET-REMOVED]36abab80d4681424b84c1183", size = 140527 },
|
||||||
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:[AWS-SECRET-REMOVED]dd57cdeb95f3f2e085687563", size = 156446 },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redis"
|
name = "redis"
|
||||||
version = "5.2.1"
|
version = "5.2.1"
|
||||||
@@ -890,6 +1007,15 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]56c89140852d1120324e8686", size = 9755 },
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]56c89140852d1120324e8686", size = 9755 },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sorl-thumbnail"
|
||||||
|
version = "12.11.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]sorl_thumbnail-12.11.0.tar.gz", hash = "sha256:[AWS-SECRET-REMOVED]f439b2e17b938b91eea463b3", size = 667102 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]sorl_thumbnail-12.11.0-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]4af5e9dc3f31cb605df765b5", size = 42789 },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlparse"
|
name = "sqlparse"
|
||||||
version = "0.5.3"
|
version = "0.5.3"
|
||||||
@@ -926,6 +1052,8 @@ dependencies = [
|
|||||||
{ name = "django-filter" },
|
{ name = "django-filter" },
|
||||||
{ name = "django-htmx" },
|
{ name = "django-htmx" },
|
||||||
{ name = "django-htmx-autocomplete" },
|
{ name = "django-htmx-autocomplete" },
|
||||||
|
{ name = "django-mptt" },
|
||||||
|
{ name = "django-nyt" },
|
||||||
{ name = "django-oauth-toolkit" },
|
{ name = "django-oauth-toolkit" },
|
||||||
{ name = "django-pghistory" },
|
{ name = "django-pghistory" },
|
||||||
{ name = "django-simple-history" },
|
{ name = "django-simple-history" },
|
||||||
@@ -943,7 +1071,9 @@ dependencies = [
|
|||||||
{ name = "pytest-playwright" },
|
{ name = "pytest-playwright" },
|
||||||
{ name = "python-dotenv" },
|
{ name = "python-dotenv" },
|
||||||
{ name = "requests" },
|
{ name = "requests" },
|
||||||
|
{ name = "sorl-thumbnail" },
|
||||||
{ name = "whitenoise" },
|
{ name = "whitenoise" },
|
||||||
|
{ name = "wiki" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.metadata]
|
[package.metadata]
|
||||||
@@ -961,6 +1091,8 @@ requires-dist = [
|
|||||||
{ name = "django-filter", specifier = ">=23.5" },
|
{ name = "django-filter", specifier = ">=23.5" },
|
||||||
{ name = "django-htmx", specifier = ">=1.17.2" },
|
{ name = "django-htmx", specifier = ">=1.17.2" },
|
||||||
{ name = "django-htmx-autocomplete", specifier = ">=1.0.5" },
|
{ name = "django-htmx-autocomplete", specifier = ">=1.0.5" },
|
||||||
|
{ name = "django-mptt", specifier = ">=0.16.0" },
|
||||||
|
{ name = "django-nyt", specifier = ">=1.4.1" },
|
||||||
{ name = "django-oauth-toolkit", specifier = ">=3.0.1" },
|
{ name = "django-oauth-toolkit", specifier = ">=3.0.1" },
|
||||||
{ name = "django-pghistory", specifier = ">=3.5.2" },
|
{ name = "django-pghistory", specifier = ">=3.5.2" },
|
||||||
{ name = "django-simple-history", specifier = ">=3.5.0" },
|
{ name = "django-simple-history", specifier = ">=3.5.0" },
|
||||||
@@ -978,7 +1110,21 @@ requires-dist = [
|
|||||||
{ name = "pytest-playwright", specifier = ">=0.4.3" },
|
{ name = "pytest-playwright", specifier = ">=0.4.3" },
|
||||||
{ name = "python-dotenv", specifier = ">=1.0.1" },
|
{ name = "python-dotenv", specifier = ">=1.0.1" },
|
||||||
{ name = "requests", specifier = ">=2.32.3" },
|
{ name = "requests", specifier = ">=2.32.3" },
|
||||||
|
{ name = "sorl-thumbnail", specifier = ">=12.11.0" },
|
||||||
{ name = "whitenoise", specifier = ">=6.6.0" },
|
{ name = "whitenoise", specifier = ">=6.6.0" },
|
||||||
|
{ name = "wiki", specifier = ">=0.11.2" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tinycss2"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "webencodings" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]tinycss2-1.4.0.tar.gz", hash = "sha256:[AWS-SECRET-REMOVED]c1ae10ebccdea16fb404a9b7", size = 87085 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]tinycss2-1.4.0-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]4bb905a5775adb0d884c5289", size = 26610 },
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1055,6 +1201,15 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]urllib3-2.3.0-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]710050facf0dd6911440e3df", size = 128369 },
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]urllib3-2.3.0-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]710050facf0dd6911440e3df", size = 128369 },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "webencodings"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]webencodings-0.5.1.tar.gz", hash = "sha256:[AWS-SECRET-REMOVED]865afcc4aab16748587e1923", size = 9721 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]3f95be16fc9acd2947514a78", size = 11774 },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "whitenoise"
|
name = "whitenoise"
|
||||||
version = "6.8.2"
|
version = "6.8.2"
|
||||||
@@ -1064,6 +1219,26 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]whitenoise-6.8.2-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]ab9726e5772ac50fb45d2280", size = 20158 },
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]whitenoise-6.8.2-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]ab9726e5772ac50fb45d2280", size = 20158 },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wiki"
|
||||||
|
version = "0.11.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "bleach", extra = ["css"] },
|
||||||
|
{ name = "django" },
|
||||||
|
{ name = "django-mptt" },
|
||||||
|
{ name = "django-nyt" },
|
||||||
|
{ name = "django-sekizai" },
|
||||||
|
{ name = "markdown" },
|
||||||
|
{ name = "pillow" },
|
||||||
|
{ name = "pymdown-extensions" },
|
||||||
|
{ name = "sorl-thumbnail" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]wiki-0.11.2.tar.gz", hash = "sha256:[AWS-SECRET-REMOVED]4140b4c9c64497736c1594d7", size = 2274191 }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.[AWS-SECRET-REMOVED][AWS-SECRET-REMOVED]wiki-0.11.2-py3-none-any.whl", hash = "sha256:[AWS-SECRET-REMOVED]a1182bd105f2cbfebbeb20aa", size = 2436316 },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zope-interface"
|
name = "zope-interface"
|
||||||
version = "7.2"
|
version = "7.2"
|
||||||
|
|||||||
1
wiki/__init__.py
Normal file
1
wiki/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
default_app_config = "wiki.apps.WikiConfig"
|
||||||
11
wiki/apps.py
Normal file
11
wiki/apps.py
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
class WikiConfig(AppConfig):
|
||||||
|
name = 'wiki'
|
||||||
|
verbose_name = 'Wiki'
|
||||||
|
|
||||||
|
def ready(self):
|
||||||
|
"""
|
||||||
|
Register signals and perform other initialization
|
||||||
|
"""
|
||||||
|
pass
|
||||||
1
wiki/plugins/parks/__init__.py
Normal file
1
wiki/plugins/parks/__init__.py
Normal file
@@ -0,0 +1 @@
|
|||||||
|
default_app_config = "wiki.plugins.parks.apps.ParksPluginConfig"
|
||||||
13
wiki/plugins/parks/apps.py
Normal file
13
wiki/plugins/parks/apps.py
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
class ParksPluginConfig(AppConfig):
|
||||||
|
name = "wiki.plugins.parks"
|
||||||
|
label = "wiki_parks"
|
||||||
|
verbose_name = "Wiki Parks Plugin"
|
||||||
|
|
||||||
|
def ready(self):
|
||||||
|
"""
|
||||||
|
Register plugin with wiki system when the app is ready.
|
||||||
|
Plugin registration is deferred until wiki core is available.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
34
wiki/plugins/parks/models.py
Normal file
34
wiki/plugins/parks/models.py
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
from django.db import models
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
class ParkMetadata(models.Model):
|
||||||
|
article = models.OneToOneField(
|
||||||
|
'wiki.Article', # Using string reference to avoid import issues
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name='park_metadata'
|
||||||
|
)
|
||||||
|
|
||||||
|
operator = models.CharField(
|
||||||
|
max_length=255,
|
||||||
|
verbose_name=_('Operator'),
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
|
|
||||||
|
opened_date = models.DateField(
|
||||||
|
verbose_name=_('Opening Date'),
|
||||||
|
null=True,
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
|
|
||||||
|
location = models.CharField(
|
||||||
|
max_length=255,
|
||||||
|
verbose_name=_('Location'),
|
||||||
|
blank=True
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _('Park Metadata')
|
||||||
|
verbose_name_plural = _('Park Metadata')
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"Park info for {self.article.current_revision.title}"
|
||||||
14
wiki/plugins/parks/wiki_plugin.py
Normal file
14
wiki/plugins/parks/wiki_plugin.py
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
|
class ParksPlugin:
|
||||||
|
"""
|
||||||
|
Plugin for handling parks in the wiki system.
|
||||||
|
Core registration will be added later.
|
||||||
|
"""
|
||||||
|
slug = 'parks'
|
||||||
|
|
||||||
|
sidebar = {
|
||||||
|
'headline': _('Park Information'),
|
||||||
|
'icon_class': 'fa-info-circle',
|
||||||
|
'template': 'wiki/plugins/parks/sidebar.html',
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user