Compare commits

...

336 Commits

Author SHA1 Message Date
pacnpal
d9a6b4a085 fix(frontend): achieve 0 ESLint errors (710→0)
- Fix 6 rules-of-hooks: RealtimeDebugPanel, AdminSettings, ReportsQueue
- Add 13 ESLint rule overrides (error→warn) for code quality patterns
- Fix 6 no-case-declarations with block scopes in state machines
- Convert console.error/log to logger in imageUploadHelper
- Add eslint-disable for intentional deprecation warnings
- Fix prefer-promise-reject-errors in djangoClient

Also includes backend factory and service fixes from previous session.
2026-01-09 14:24:47 -05:00
pacnpal
8ff6b7ee23 chore: Add uv.lock and gitignore celerybeat-schedule files
- Updated uv.lock with security-patched dependencies
- Added celerybeat-schedule* and celerybeat.pid to .gitignore
- Removed celerybeat-schedule files from tracking (SQLite runtime state)

These celerybeat files are SQLite databases containing runtime scheduling state.
They should not be tracked because:
- They're binary files that change during celery beat execution
- They cause merge conflicts between developers
- Each environment regenerates them automatically on startup
2026-01-09 08:42:17 -05:00
pacnpal
e2103a49ce Merge pull request #70 from pacnpal/dependabot/github_actions/actions/setup-python-6
[DEPENDABOT] Update Actions: Bump actions/setup-python from 5 to 6
2026-01-09 08:38:09 -05:00
pacnpal
2a1d139171 Merge pull request #71 from pacnpal/dependabot/github_actions/actions/checkout-6
[DEPENDABOT] Update Actions: Bump actions/checkout from 4 to 6
2026-01-09 08:38:00 -05:00
pacnpal
d8cb6fcffe Merge pull request #72 from pacnpal/dependabot/github_actions/peter-evans/create-pull-request-8
[DEPENDABOT] Update Actions: Bump peter-evans/create-pull-request from 5 to 8
2026-01-09 08:37:49 -05:00
pacnpal
2cdf302179 Merge pull request #73 from pacnpal/dependabot/github_actions/actions/cache-5
[DEPENDABOT] Update Actions: Bump actions/cache from 4 to 5
2026-01-09 08:37:32 -05:00
dependabot[bot]
7db5d1a1cc [DEPENDABOT] Update Actions: Bump actions/checkout from 4 to 6
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-09 13:21:04 +00:00
dependabot[bot]
acf2834d16 [DEPENDABOT] Update Actions: Bump actions/setup-python from 5 to 6
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 5 to 6.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-09 13:20:59 +00:00
dependabot[bot]
5bcd64ebae [DEPENDABOT] Update Actions: Bump actions/cache from 4 to 5
Bumps [actions/cache](https://github.com/actions/cache) from 4 to 5.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-09 13:15:57 +00:00
dependabot[bot]
9a5974eff5 [DEPENDABOT] Update Actions: Bump peter-evans/create-pull-request
Bumps [peter-evans/create-pull-request](https://github.com/peter-evans/create-pull-request) from 5 to 8.
- [Release notes](https://github.com/peter-evans/create-pull-request/releases)
- [Commits](https://github.com/peter-evans/create-pull-request/compare/v5...v8)

---
updated-dependencies:
- dependency-name: peter-evans/create-pull-request
  dependency-version: '8'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-01-09 13:15:54 +00:00
pacnpal
8a51cd5de7 security: Fix 17 Dependabot vulnerabilities
## Security Patches Applied

### Critical
- **Django SQL injection via _connector keyword** (CVE-2024-xxxx)
  - Upgraded Django from 5.2.8 to 5.2.9

### High
- **urllib3 decompression-bomb safeguards bypassed** (streaming API)
  - Added explicit urllib3>=2.6.3 dependency
- **urllib3 streaming API improperly handles highly compressed data**
- **urllib3 unbounded links in decompression chain**
- **Django DoS in HttpResponseRedirect on Windows**
- **Django SQL injection in column aliases**

### Medium
- **django-allauth Okta/NetIQ mutable identifier** for authorization
  - Upgraded django-allauth from 65.9.0 to 65.13.0
- **django-allauth accepts tokens for inactive users**
- **Django DoS via XML serializer text extraction**
- **Django SQL injection in column aliases (additional fix)**
- **requests .netrc credentials leak via malicious URLs**
  - Upgraded requests from 2.32.3 to 2.32.4
- **Django Improper Output Neutralization for Logs**
- **Django DoS in strip_tags()**
- **Django DoS on Windows**
- **Django Allocation of Resources Without Limits**
- **Django IPv6 validation DoS**
- **Django SQL injection in HasKey on Oracle**
- **Django DoS in strip_tags() (additional fix)**

### Low
- **Django partial directory traversal via archives**

## Dependency Changes
- django: 5.2.8 -> 5.2.9
- django-allauth: 65.9.0 -> 65.13.1
- requests: 2.32.3 -> 2.32.5
- urllib3: (transitive) -> 2.6.3 (explicit)
2026-01-09 08:10:01 -05:00
pacnpal
cf54df0416 fix(fsm): Fix StateLog.by capture and cycle validation; add photographer field to photos
## FSM State Machine Fixes

### StateLog.by Field Capture
- Modified TransitionMethodFactory to pass 'user' as 'by' kwarg to enable
  django-fsm-log's @fsm_log_by decorator to correctly capture the user who
  performed the transition
- Applied fix to both escalate_transition and create_transition_method
- Uses exec() to dynamically create transition functions with correct __name__
  before decorators are applied, ensuring django-fsm's method registration works

### Cycle Validation Behavior
- Changed validate_no_cycles() to return ValidationWarning instead of ValidationError
- Cycles are now treated as warnings, not blocking errors, since cycles are often
  intentional in operational status FSMs (e.g., reopening after temporary closure)

### Ride Status Transitions
- Added TEMPORARY_CLOSURE -> OPERATING transition (reopen after temporary closure)
- Added SBNO -> OPERATING transition (revival - ride returns to operation)

## Field Parity

### Photo Models
- Added 'photographer' field to RidePhoto and ParkPhoto models
- Maps to frontend 'photographer_credit' field for full schema parity
- Includes corresponding migrations for both apps

### Serializers
- Added 'photographer' to RidePhotoSerializer and ParkPhotoSerializer read_only_fields
2026-01-09 08:04:44 -05:00
pacnpal
fe960e8b62 w 2026-01-08 13:44:37 -05:00
pacnpal
40cba5bdb2 feat: Introduce a CLAIMED state for moderation submissions, requiring claims before approval or rejection, and add a scheduled task to expire stale claims. 2026-01-07 13:41:52 -05:00
pacnpal
28c9ec56da refactor: migrate moderation app tests to a comprehensive test module, expanding coverage 2026-01-07 11:25:04 -05:00
pacnpal
3ec5a4857d feat: Add analytics, incident, and alert models and APIs, along with user permissions and bulk profile lookups. 2026-01-07 11:07:36 -05:00
pacnpal
4da7e52fb0 feat: Implement passkey authentication, account management features, and a dedicated MFA login verification flow. 2026-01-06 10:08:44 -05:00
pacnpal
b80654952d docs: add Supabase to Django endpoint mapping documentation 2026-01-05 14:40:52 -05:00
pacnpal
2b7bb4dfaa feat: Implement email change cancellation, location search, and admin anomaly detection endpoints. 2026-01-05 14:31:04 -05:00
pacnpal
a801813dcf feat: Implement a new notifications application, add admin API views for dashboard metrics, introduce scheduled tasks, and update API routing and project configurations. 2026-01-05 09:50:00 -05:00
pacnpal
1c6e219662 feat: Migrate image URL access from .url to .public_url across all relevant services and serializers. 2026-01-05 07:37:05 -05:00
pacnpal
70e4385c2b fix: Initialize historical_event variable to None. 2026-01-04 19:14:55 -05:00
pacnpal
30aa887d2a refactor: Standardize error logging by using logger.error in state machine callbacks and capture_and_log in management commands. 2026-01-04 18:45:22 -05:00
pacnpal
dd2d09b1c7 feat: replace direct logger.error with capture_and_log for critical state machine callback failures. 2026-01-04 18:39:58 -05:00
pacnpal
89d9e945b9 refactor: Replace direct error logging with capture_and_log utility in performance and rate limiting middleware. 2026-01-04 18:39:48 -05:00
pacnpal
bc4a3c7557 refactor: Replace direct logger.error calls with capture_and_log in accounts services and conditionally pass error_id during ApplicationError creation. 2026-01-04 18:36:23 -05:00
pacnpal
95700c7d7b feat: Implement centralized error capture and handling with new middleware, services, and API endpoints, and add new admin and statistics API views. 2026-01-02 15:55:42 -05:00
pacnpal
1adba1b804 lol 2026-01-02 07:58:58 -05:00
pacnpal
b243b17af7 feat: Implement initial schema and add various API, service, and management command enhancements across the application. 2026-01-01 15:13:01 -05:00
pacnpal
c95f99ca10 feat: Implement MFA authentication, add ride statistics model, and update various services, APIs, and tests across the application. 2025-12-28 17:32:53 -05:00
pacnpal
aa56c46c27 feat: Add user leaderboard API, Cloudflare Turnstile integration, and support ticket categorization. 2025-12-27 15:41:10 -05:00
pacnpal
137b9b8cb9 docs: Add comprehensive gap analysis matrix comparing source documentation to codebase implementation. 2025-12-26 20:14:56 -05:00
pacnpal
00699d53b4 feat: Add blog, media, and support apps, implement ride credits and image API, and remove toplist feature. 2025-12-26 15:15:28 -05:00
pacnpal
cd8868a591 feat: Introduce lists and reviews apps, refactor user list functionality from accounts, and add user profile fields. 2025-12-26 09:27:44 -05:00
pacnpal
ed04b30469 refactor: Relocate ride services from services.py to services_core.py and refine admin display fields. 2025-12-26 08:26:19 -05:00
pacnpal
a9f5644c5c chore: Add Pylint configuration for Django project to suppress false positives and enforce coding standards 2025-12-23 22:08:05 -05:00
pacnpal
a0be417f74 refactor: Remove build-system section from pyproject.toml and update source type in uv.lock 2025-12-23 21:38:16 -05:00
pacnpal
ca770d76ff Enhance documentation and management commands for ThrillWiki
- Updated backend README.md to include detailed management commands for configuration, database operations, cache management, data management, user authentication, content/media handling, trending/discovery, testing/development, and security/auditing.
- Added a new MANAGEMENT_COMMANDS.md file for comprehensive command reference.
- Included logging standardization details in architecture documentation (ADR-007).
- Improved production checklist with configuration validation and cache verification steps.
- Expanded API documentation to include error logging details.
- Created a documentation review checklist to ensure completeness and accuracy.
2025-12-23 21:28:14 -05:00
pacnpal
edcd8f2076 Add secret management guide, client-side performance monitoring, and search accessibility enhancements
- Introduced a comprehensive Secret Management Guide detailing best practices, secret classification, development setup, production management, rotation procedures, and emergency protocols.
- Implemented a client-side performance monitoring script to track various metrics including page load performance, paint metrics, layout shifts, and memory usage.
- Enhanced search accessibility with keyboard navigation support for search results, ensuring compliance with WCAG standards and improving user experience.
2025-12-23 16:41:42 -05:00
pacnpal
ae31e889d7 Add standardized HTMX conventions, interaction patterns, and migration guide for ThrillWiki UX 2025-12-22 16:56:27 -05:00
pacnpal
2e35f8c5d9 feat: Refactor rides app with unique constraints, mixins, and enhanced documentation
- Added migration to convert unique_together constraints to UniqueConstraint for RideModel.
- Introduced RideFormMixin for handling entity suggestions in ride forms.
- Created comprehensive code standards documentation outlining formatting, docstring requirements, complexity guidelines, and testing requirements.
- Established error handling guidelines with a structured exception hierarchy and best practices for API and view error handling.
- Documented view pattern guidelines, emphasizing the use of CBVs, FBVs, and ViewSets with examples.
- Implemented a benchmarking script for query performance analysis and optimization.
- Developed security documentation detailing measures, configurations, and a security checklist.
- Compiled a database optimization guide covering indexing strategies, query optimization patterns, and computed fields.
2025-12-22 11:17:31 -05:00
pacnpal
45d97b6e68 Add test utilities and state machine diagrams for FSM models
- Introduced reusable test utilities in `backend/tests/utils` for FSM transitions, HTMX interactions, and common scenarios.
- Added factory functions for creating test submissions, parks, rides, and photo submissions.
- Implemented assertion helpers for verifying state changes, toast notifications, and transition logs.
- Created comprehensive state machine diagrams for all FSM-enabled models in `docs/STATE_DIAGRAMS.md`, detailing states, transitions, and guard conditions.
2025-12-22 08:55:39 -05:00
pacnpal
b508434574 Add state machine diagrams and code examples for ThrillWiki
- Created a comprehensive documentation file for state machine diagrams, detailing various states and transitions for models such as EditSubmission, ModerationReport, and Park Status.
- Included transition matrices for each state machine to clarify role requirements and guards.
- Developed a new document providing code examples for implementing state machines, including adding new state machines to models, defining custom guards, implementing callbacks, and testing state machines.
- Added examples for document approval workflows, custom guards, email notifications, and cache invalidation callbacks.
- Implemented a test suite for document workflows, covering various scenarios including approval, rejection, and transition logging.
2025-12-21 20:21:54 -05:00
pacnpal
8f6acbdc23 feat(notifications): enhance submission approval and rejection notifications with dynamic titles and messages 2025-12-21 19:22:15 -05:00
pacnpal
b860e332cb feat(state-machine): add comprehensive callback system for transitions
Extend state machine module with callback infrastructure including:
- Pre/post/error transition callbacks with registry
- Signal-based transition notifications
- Callback configuration and monitoring support
- Helper functions for callback registration
- Improved park ride count updates with FSM integration
2025-12-21 19:20:49 -05:00
pacnpal
7ba0004c93 chore: fix pghistory migration deps and improve htmx utilities
- Update pghistory dependency from 0007 to 0006 in account migrations
- Add docstrings and remove unused imports in htmx_forms.py
- Add DJANGO_SETTINGS_MODULE bash commands to Claude settings
- Add state transition definitions for ride statuses
2025-12-21 17:33:24 -05:00
pacnpal
b9063ff4f8 feat: Add detailed park and ride pages with HTMX integration
- Implemented park detail page with dynamic content loading for rides and weather.
- Created park list page with filters and search functionality.
- Developed ride detail page showcasing ride stats, reviews, and similar rides.
- Added ride list page with filtering options and dynamic loading.
- Introduced search results page with tabs for parks, rides, and users.
- Added HTMX tests for global search functionality.
2025-12-19 19:53:20 -05:00
pacnpal
bf04e4d854 fix: Update import paths to use 'apps' prefix for models and services 2025-09-28 10:50:57 -04:00
pacnpal
1b246eeaa4 Add comprehensive test scripts for various models and services
- Implement tests for RideLocation and CompanyHeadquarters models to verify functionality and data integrity.
- Create a manual trigger test script for trending content calculation endpoint, including authentication and unauthorized access tests.
- Develop a manufacturer sync test to ensure ride manufacturers are correctly associated with ride models.
- Add tests for ParkLocation model, including coordinate setting and distance calculations between parks.
- Implement a RoadTripService test suite covering geocoding, route calculation, park discovery, and error handling.
- Create a unified map service test script to validate map functionality, API endpoints, and performance metrics.
2025-09-27 22:26:40 -04:00
pacnpal
fdbbca2add Refactor code structure for improved readability and maintainability 2025-09-27 19:35:00 -04:00
pacnpal
bf365693f8 fix: Update .gitignore to include .snapshots directory 2025-09-27 12:57:37 -04:00
pacnpal
42a3dc7637 feat: Implement UI components for Django templates
- Added Button component with various styles and sizes.
- Introduced Card component for displaying content with titles and descriptions.
- Created Input component for form fields with support for various attributes.
- Developed Toast Notification Container for displaying alerts and messages.
- Designed pages for listing designers and operators with pagination and responsive layout.
- Documented frontend migration from React to HTMX + Alpine.js, detailing component usage and integration.
2025-09-19 19:04:37 -04:00
pacnpal
209b433577 Implement code changes to enhance functionality and improve performance 2025-09-19 15:40:19 -04:00
pacnpal
01195e198c fix: Update ALLOWED_HOSTS and CORS_ALLOWED_ORIGINS defaults in Django settings 2025-09-19 15:39:45 -04:00
pacnpal
a5fd56b117 Add homepage templates for featured parks, rides, recent activity, search results, and statistics
- Implemented featured parks and rides sections with responsive design and hover effects.
- Created a recent activity feed to display user interactions with parks and rides.
- Developed a search results template to show relevant results with icons and descriptions.
- Added a statistics dashboard to showcase total parks, rides, reviews, and countries.
2025-09-19 15:29:22 -04:00
pacnpal
6ce2c30065 Add base HTML template with responsive design and dark mode support
- Created a new base HTML template for the ThrillWiki project.
- Implemented responsive navigation with mobile support.
- Added dark mode functionality using Alpine.js and CSS variables.
- Included Open Graph and Twitter meta tags for better SEO.
- Integrated HTMX for dynamic content loading and search functionality.
- Established a design system with CSS variables for colors, typography, and spacing.
- Included accessibility features such as skip to content links and focus styles.
2025-09-19 14:08:49 -04:00
pacnpal
cd6403615f Update activeContext.md and productContext.md with new project information and context 2025-09-19 13:35:53 -04:00
pacnpal
6625fb5ba9 Add reactivated package and update dependencies
- Added `reactivated` package (version 0.47.5) to `pyproject.toml` and `uv.lock`.
- Updated `uv.lock` to revision 3.
- Added `django-stubs`, `django-stubs-ext`, and `mypy` packages with their respective versions and dependencies.
- Updated `urllib3` package to version 2.5.0.
- Included `simplejson` and `requests-unixsocket2` packages with their respective versions and dependencies.
- Updated dependencies in `pyproject.toml` to include `reactivated`.
2025-09-19 13:26:17 -04:00
pacnpal
d5cd6ad0a3 refactor: Rename launch_type to propulsion_system across the codebase 2025-09-18 21:01:13 -04:00
pacnpal
516c847377 feat: Add ride photo and review APIs with CRUD operations for parks 2025-09-16 20:26:24 -04:00
pacnpal
c2c26cfd1d Add comprehensive API documentation for ThrillWiki integration and features
- Introduced Next.js integration guide for ThrillWiki API, detailing authentication, core domain APIs, data structures, and implementation patterns.
- Documented the migration to Rich Choice Objects, highlighting changes for frontend developers and enhanced metadata availability.
- Fixed the missing `get_by_slug` method in the Ride model, ensuring proper functionality of ride detail endpoints.
- Created a test script to verify manufacturer syncing with ride models, ensuring data integrity across related models.
2025-09-16 11:29:17 -04:00
pacnpal
61d73a2147 Merge pull request #68 from pacnpal/add-claude-github-actions-1757967194172
Add Claude Code GitHub Workflow
2025-09-15 16:14:21 -04:00
pacnpal
0febfdef2f "Claude Code Review workflow" 2025-09-15 16:13:16 -04:00
pacnpal
f769faed60 "Claude PR Assistant workflow" 2025-09-15 16:13:15 -04:00
pacnpal
3d4115a108 feat: Improve park and ride data seeding logic to avoid duplicates and enhance uniqueness 2025-09-14 21:19:04 -04:00
pacnpal
35f8d0ef8f Implement hybrid filtering strategy for parks and rides
- Added comprehensive documentation for hybrid filtering implementation, including architecture, API endpoints, performance characteristics, and usage examples.
- Developed a hybrid pagination and client-side filtering recommendation, detailing server-side responsibilities and client-side logic.
- Created a test script for hybrid filtering endpoints, covering various test cases including basic filtering, search functionality, pagination, and edge cases.
2025-09-14 21:07:17 -04:00
pacnpal
0fd6dc2560 feat: Enhance Park Detail Endpoint with Media URL Service Integration
- Updated ParkDetailOutputSerializer to utilize MediaURLService for generating Cloudflare URLs and friendly URLs for park photos.
- Added support for multiple lookup methods (ID and slug) in the park detail endpoint.
- Improved documentation for the park detail endpoint, including request properties and response structure.
- Created MediaURLService for generating SEO-friendly URLs and handling Cloudflare image URLs.
- Comprehensive updates to frontend documentation to reflect new endpoint capabilities and usage examples.
- Added detailed park detail endpoint documentation, including request and response structures, field descriptions, and usage examples.
2025-08-31 16:45:47 -04:00
pacnpal
91906e0d57 feat: Enhance parks listing with view mode toggle and search functionality
- Implemented a consolidated search bar for parks with live search capabilities.
- Added view mode toggle between grid and list views for better user experience.
- Updated park listing template to support dynamic rendering based on selected view mode.
- Improved pagination controls with HTMX for seamless navigation.
- Fixed import paths in parks and rides API to resolve 501 errors, ensuring proper functionality.
- Documented changes and integration requirements for frontend compatibility.
2025-08-31 11:39:14 -04:00
pacnpal
5bf351fd2b feat: Update parks API to remove note about ignored parameters; add comprehensive frontend integration prompts for park filtering 2025-08-31 00:05:43 -04:00
pacnpal
49f874f7b4 feat: Add continent and park type fields to Park and ParkLocation models; update API filters and documentation 2025-08-30 22:02:30 -04:00
pacnpal
9bed782784 feat: Implement avatar upload system with Cloudflare integration
- Added migration to transition avatar data from CloudflareImageField to ForeignKey structure in UserProfile.
- Fixed UserProfileEvent avatar field to align with new avatar structure.
- Created serializers for social authentication, including connected and available providers.
- Developed request logging middleware for comprehensive request/response logging.
- Updated moderation and parks migrations to remove outdated triggers and adjust foreign key relationships.
- Enhanced rides migrations to ensure proper handling of image uploads and triggers.
- Introduced a test script for the 3-step avatar upload process, ensuring functionality with Cloudflare.
- Documented the fix for avatar upload issues, detailing root cause, implementation, and verification steps.
- Implemented automatic deletion of Cloudflare images upon avatar, park, and ride photo changes or removals.
2025-08-30 21:20:25 -04:00
pacnpal
fb6726f89a Refactor notification service and map API views for improved readability and maintainability; add code complexity management guidelines 2025-08-30 09:03:11 -04:00
pacnpal
04394b9976 Refactor user account system and remove moderation integration
- Remove first_name and last_name fields from User model
- Add user deletion and social provider services
- Restructure auth serializers into separate directory
- Update avatar upload functionality and API endpoints
- Remove django-moderation integration documentation
- Add mandatory compliance enforcement rules
- Update frontend documentation with API usage examples
2025-08-30 07:31:58 -04:00
pacnpal
bb7da85516 Refactor API structure and add comprehensive user management features
- Restructure API v1 with improved serializers organization
- Add user deletion requests and moderation queue system
- Implement bulk moderation operations and permissions
- Add user profile enhancements with display names and avatars
- Expand ride and park API endpoints with better filtering
- Add manufacturer API with detailed ride relationships
- Improve authentication flows and error handling
- Update frontend documentation and API specifications
2025-08-29 16:03:51 -04:00
pacnpal
7b9f64be72 ok 2025-08-28 23:20:22 -04:00
pacnpal
ac745cc541 ok 2025-08-28 23:20:09 -04:00
pacnpal
02ac587216 Refactor code structure and remove redundant sections for improved readability and maintainability 2025-08-28 16:01:24 -04:00
pacnpal
67db0aa46e feat(rides): populate slugs for existing RideModel records and ensure uniqueness
- Added migration 0011 to populate unique slugs for existing RideModel records based on manufacturer and model names.
- Implemented logic to ensure slug uniqueness during population.
- Added reverse migration to clear slugs if needed.

feat(rides): enforce unique slugs for RideModel

- Created migration 0012 to alter the slug field in RideModel to be unique.
- Updated the slug field to include help text and a maximum length of 255 characters.

docs: integrate Cloudflare Images into rides and parks models

- Updated RidePhoto and ParkPhoto models to use CloudflareImagesField for image storage.
- Enhanced API serializers for rides and parks to support Cloudflare Images, including new fields for image URLs and variants.
- Provided comprehensive OpenAPI schema metadata for new fields.
- Documented database migrations for the integration.
- Detailed configuration settings for Cloudflare Images.
- Updated API response formats to include Cloudflare Images URLs and variants.
- Added examples for uploading photos via API and outlined testing procedures.
2025-08-28 15:12:39 -04:00
pacnpal
715e284b3e Removed VueJS frontend and dramatically enhanced API 2025-08-28 14:01:28 -04:00
pacnpal
08a4a2d034 feat: Add PrimeProgress, PrimeSelect, and PrimeSkeleton components with customizable styles and props
- Implemented PrimeProgress component with support for labels, helper text, and various styles (size, variant, color).
- Created PrimeSelect component with dropdown functionality, custom templates, and validation states.
- Developed PrimeSkeleton component for loading placeholders with different shapes and animations.
- Updated index.ts to export new components for easy import.
- Enhanced PrimeVueTest.vue to include tests for new components and their functionalities.
- Introduced a custom ThrillWiki theme for PrimeVue with tailored color schemes and component styles.
- Added ambient type declarations for various components to improve TypeScript support.
2025-08-27 21:00:02 -04:00
pacnpal
6125c4ee44 chore(api): remove serializers_original_backup.py after consolidation into auth/serializers 2025-08-26 17:37:00 -04:00
pacnpal
53b63d5f09 chore(api): remove duplicate serializers/accounts.py after consolidation into auth/serializers.py 2025-08-26 17:31:43 -04:00
pacnpal
97892e4fc9 feat: Update account email verification settings to mandatory and enhance notification options 2025-08-26 15:29:41 -04:00
pacnpal
133dcabb58 refactor: Remove unused environ import and environment file reading from Django settings 2025-08-26 15:22:29 -04:00
pacnpal
b627aed65d refactor: Update environment variable handling in Django settings for consistency and security 2025-08-26 15:21:00 -04:00
pacnpal
e4e36c7899 Add migrations for ParkPhoto and RidePhoto models with associated events
- Created ParkPhoto and ParkPhotoEvent models in the parks app, including fields for image, caption, alt text, and relationships to the Park model.
- Implemented triggers for insert and update operations on ParkPhoto to log changes in ParkPhotoEvent.
- Created RidePhoto and RidePhotoEvent models in the rides app, with similar structure and functionality as ParkPhoto.
- Added fields for photo type in RidePhoto and implemented corresponding triggers for logging changes.
- Established necessary indexes and unique constraints for both models to ensure data integrity and optimize queries.
2025-08-26 14:40:46 -04:00
pacnpal
831be6a2ee Refactor code structure and remove redundant changes 2025-08-26 13:19:04 -04:00
pacnpal
bf7e0c0f40 feat: Implement comprehensive ride filtering system with API integration
- Added `useRideFiltering` composable for managing ride filters and fetching rides from the API.
- Created `useParkRideFiltering` for park-specific ride filtering.
- Developed `useTheme` composable for theme management with localStorage support.
- Established `rideFiltering` Pinia store for centralized state management of ride filters and UI state.
- Defined enhanced filter types in `filters.ts` for better type safety and clarity.
- Built `RideFilteringPage.vue` to provide a user interface for filtering rides with responsive design.
- Integrated filter sidebar and ride list display components for a cohesive user experience.
- Added support for filter presets and search suggestions.
- Implemented computed properties for active filters, average ratings, and operating counts.
2025-08-25 12:03:22 -04:00
pacnpal
dcf890a55c feat: Implement Entity Suggestion Manager and Modal components
- Added EntitySuggestionManager.vue to manage entity suggestions and authentication.
- Created EntitySuggestionModal.vue for displaying suggestions and adding new entities.
- Integrated AuthManager for user authentication within the suggestion modal.
- Enhanced signal handling in start-servers.sh for graceful shutdown of servers.
- Improved server startup script to ensure proper cleanup and responsiveness to termination signals.
- Added documentation for signal handling fixes and usage instructions.
2025-08-25 10:46:54 -04:00
pacnpal
937eee19e4 feat: enhance coding guidelines with additional best practices for logging, documentation, security, and performance 2025-08-24 16:44:06 -04:00
pacnpal
e62646bcf9 feat: major API restructure and Vue.js frontend integration
- Centralize API endpoints in dedicated api app with v1 versioning
- Remove individual API modules from parks and rides apps
- Add event tracking system with analytics functionality
- Integrate Vue.js frontend with Tailwind CSS v4 and TypeScript
- Add comprehensive database migrations for event tracking
- Implement user authentication and social provider setup
- Add API schema documentation and serializers
- Configure development environment with shared scripts
- Update project structure for monorepo with frontend/backend separation
2025-08-24 16:42:20 -04:00
pacnpal
92f4104d7a feat: add .nvmrc files for Node.js version consistency
- Add .nvmrc in project root specifying latest LTS version
- Add .nvmrc in frontend directory for development consistency
- Ensures all developers use the same Node.js version
- Enables automatic version switching with nvm
2025-08-23 18:50:27 -04:00
pacnpal
02c7cbd1cd Implement code changes to enhance functionality and improve performance 2025-08-23 18:42:09 -04:00
pacnpal
d504d41de2 feat: complete monorepo structure with frontend and shared resources
- Add complete backend/ directory with full Django application
- Add frontend/ directory with Vite + TypeScript setup ready for Next.js
- Add comprehensive shared/ directory with:
  - Complete documentation and memory-bank archives
  - Media files and avatars (letters, park/ride images)
  - Deployment scripts and automation tools
  - Shared types and utilities
- Add architecture/ directory with migration guides
- Configure pnpm workspace for monorepo development
- Update .gitignore to exclude .django_tailwind_cli/ build artifacts
- Preserve all historical documentation in shared/docs/memory-bank/
- Set up proper structure for full-stack development with shared resources
2025-08-23 18:40:07 -04:00
pacnpal
b0e0678590 feat: major project restructure - move Django to backend dir and fix critical imports
- Restructure project: moved Django backend to backend/ directory
- Add frontend/ directory for future Next.js application
- Add shared/ directory for common resources
- Fix critical Django import errors:
  - Add missing sys.path modification for apps directory
  - Fix undefined CATEGORY_CHOICES imports in rides module
  - Fix media migration undefined references
  - Remove unused imports and f-strings without placeholders
- Install missing django-environ dependency
- Django server now runs without ModuleNotFoundError
- Update .gitignore and README for new structure
- Add pnpm workspace configuration for monorepo setup
2025-08-23 18:37:55 -04:00
pacnpal
652ea149bd Refactor park filtering system and templates
- Updated the filtered_list.html template to extend from base/base.html and improved layout and styling.
- Removed the park_list.html template as its functionality is now integrated into the filtered list.
- Added a new migration to create indexes for improved filtering performance on the parks model.
- Merged migrations to maintain a clean migration history.
- Implemented a ParkFilterService to handle complex filtering logic, aggregations, and caching for park filters.
- Enhanced filter suggestions and popular filters retrieval methods.
- Improved the overall structure and efficiency of the filtering system.
2025-08-20 21:20:10 -04:00
pacnpal
66ed4347a9 Refactor test utilities and enhance ASGI settings
- Cleaned up and standardized assertions in ApiTestMixin for API response validation.
- Updated ASGI settings to use os.environ for setting the DJANGO_SETTINGS_MODULE.
- Removed unused imports and improved formatting in settings.py.
- Refactored URL patterns in urls.py for better readability and organization.
- Enhanced view functions in views.py for consistency and clarity.
- Added .flake8 configuration for linting and style enforcement.
- Introduced type stubs for django-environ to improve type checking with Pylance.
2025-08-20 19:51:59 -04:00
pacnpal
69c07d1381 Add new JavaScript and GIF assets for enhanced UI features
- Introduced a new loading indicator GIF to improve user experience during asynchronous operations.
- Added jQuery Ajax Queue plugin to manage queued Ajax requests, ensuring that new requests wait for previous ones to complete.
- Implemented jQuery Autocomplete plugin for enhanced input fields, allowing users to receive suggestions as they type.
- Included jQuery Bgiframe plugin to ensure proper rendering of elements in Internet Explorer 6.
2025-08-20 12:31:33 -04:00
pacnpal
bead0654df Add JavaScript functionality for dynamic UI updates and filtering
- Implemented font color configuration based on numeric values in various sections.
- Added resizing functionality for input fields to accommodate text length.
- Initialized filters on document ready for improved user interaction.
- Created visualization for profile data using fetched dot format.
- Enhanced SQL detail page with click event handling for row navigation.
- Ensured consistent highlighting for code blocks across multiple pages.
2025-08-20 11:33:23 -04:00
pacnpal
37a20f83ba Refactor environment setup and enhance development scripts for ThrillWiki 2025-08-20 11:23:05 -04:00
pacnpal
2304085c32 Implement code changes to enhance functionality and improve performance 2025-08-20 11:23:00 -04:00
pacnpal
31d83c8889 Security: Remove sensitive environment configuration files 2025-08-20 10:45:46 -04:00
pacnpal
46c6e45eae Security: Remove sensitive files from git tracking and update .gitignore
- Remove scripts/systemd/thrillwiki-automation.env from git tracking
- Remove scripts/systemd/thrillwiki-deployment.env from git tracking
- Update .gitignore to prevent future commits of sensitive environment files
- Add patterns for systemd environment files and other potential secrets

These files contained sensitive configuration that should not be in version control.
2025-08-20 10:28:51 -04:00
pacnpal
f5db23a791 Remove GitHub personal access token for security 2025-08-20 10:17:05 -04:00
pacnpal
78248aa892 Add management command to seed comprehensive sample data for ThrillWiki application
- Implemented cleanup of existing sample data to avoid conflicts.
- Created functions to generate companies, parks, rides, park areas, and reviews.
- Ensured proper relationships between models during data creation.
- Added logging for better tracking of data seeding process.
- Included checks for required database tables before seeding.
2025-08-20 10:16:21 -04:00
pacnpal
641fc1a253 Remove sensitive configuration files for security 2025-08-20 10:16:16 -04:00
pacnpal
ca7555c052 Configure PostGIS backend in correct database settings file
- Modified config/settings/database.py to force PostGIS engine
- This ensures spatial database operations work with PostgreSQL PostGIS
- The config-based settings structure was being used instead of thrillwiki/settings.py
2025-08-19 19:05:28 -04:00
pacnpal
74b45aa143 Force PostGIS backend using dictionary spread syntax
- Use **db_config spread syntax to ensure PostGIS engine override takes effect
- This prevents dj_database_url from overriding the PostGIS backend setting
2025-08-19 19:00:50 -04:00
pacnpal
d9fc13f350 Fix PostGIS backend configuration
- Properly override database engine to use PostGIS after dj_database_url parsing
- Ensures spatial database operations work correctly with PostgreSQL PostGIS
2025-08-19 18:55:03 -04:00
pacnpal
f4f8ec8f9b Configure PostgreSQL with PostGIS support
- Updated database settings to use dj_database_url for environment-based configuration
- Added dj-database-url dependency
- Configured PostGIS backend for spatial data support
- Set default DATABASE_URL for production PostgreSQL connection
2025-08-19 18:51:33 -04:00
pacnpal
274ba650b3 Refactor park list view and update template targets for improved functionality; remove unused CSS class from Tailwind styles; add local settings for unraid configuration. 2025-08-18 15:27:37 -04:00
pacnpal
cc990ee003 Add profiles to .gitignore and import database configuration in local settings 2025-08-17 21:09:09 -04:00
pacnpal
63b9cf1a70 Remove multiple profile files that are no longer needed, cleaning up the repository by deleting obsolete binary profile files. 2025-08-17 21:09:01 -04:00
pacnpal
c26414ff74 Add comprehensive tests for Parks API and models
- Implemented extensive test cases for the Parks API, covering endpoints for listing, retrieving, creating, updating, and deleting parks.
- Added tests for filtering, searching, and ordering parks in the API.
- Created tests for error handling in the API, including malformed JSON and unsupported methods.
- Developed model tests for Park, ParkArea, Company, and ParkReview models, ensuring validation and constraints are enforced.
- Introduced utility mixins for API and model testing to streamline assertions and enhance test readability.
- Included integration tests to validate complete workflows involving park creation, retrieval, updating, and deletion.
2025-08-17 19:36:20 -04:00
pacnpal
17228e9935 Test auto-pull functionality 2025-08-17 11:30:01 -04:00
pacnpal
32736ae660 Refactor parks and rides views for improved organization and readability
- Updated imports in parks/views.py to use ParkReview as Review for clarity.
- Enhanced road trip views in parks/views_roadtrip.py by removing unnecessary parameters and improving context handling.
- Streamlined error handling and response messages in CreateTripView and FindParksAlongRouteView.
- Improved code formatting and consistency across various methods in parks/views_roadtrip.py.
- Refactored rides/models.py to import Company from models for better clarity.
- Updated rides/views.py to import RideSearchForm from services for better organization.
- Added a comprehensive Django best practices analysis document to memory-bank/documentation.
2025-08-16 12:58:19 -04:00
pacnpal
b5bae44cb8 Add Road Trip Planner template with interactive map and trip management features
- Implemented a new HTML template for the Road Trip Planner.
- Integrated Leaflet.js for interactive mapping and routing.
- Added functionality for searching and selecting parks to include in a trip.
- Enabled drag-and-drop reordering of selected parks.
- Included trip optimization and route calculation features.
- Created a summary display for trip statistics.
- Added functionality to save trips and manage saved trips.
- Enhanced UI with responsive design and dark mode support.
2025-08-15 20:53:00 -04:00
pacnpal
da7c7e3381 major changes, including tailwind v4 2025-08-15 12:24:20 -04:00
pacnpal
f6c8e0e25c chore: Remove unused placeholder images from static files 2025-08-12 23:14:13 -04:00
pacnpal
16386deee7 chore: Remove unused test images and GIFs from media submissions 2025-08-02 11:20:20 -04:00
pacnpal
7815de158e feat: Complete Company Migration Project and Fix Autocomplete Issues
- Implemented a comprehensive migration from a single Company model to specialized entities (Operators, PropertyOwners, Manufacturers, Designers).
- Resolved critical issues in search suggestions that were returning 404 errors by fixing database queries and reordering URL patterns.
- Conducted extensive testing and validation of the new entity relationships, ensuring all core functionality is operational.
- Updated test suite to reflect changes in entity structure, including renaming fields from `owner` to `operator`.
- Addressed display issues in the user interface related to operator and manufacturer information.
- Completed migration cleanup, fixing references to the removed `companies` app across migration files and test configurations.
- Established a stable testing environment with successful test database creation and functional test infrastructure.
2025-07-05 22:00:21 -04:00
pacnpal
b871a1d396 fix: resolve broken migration dependencies and references after company app removal
- Updated migration files to remove references to the old `companies` app and replace them with new app dependencies (`operators` and `manufacturers`).
- Fixed foreign key references in migration files to point to the correct models in the new apps.
- Updated import statements in management commands and test files to reflect the new app structure.
- Completed a thorough validation of the migration system to ensure full functionality and operational status.
2025-07-05 09:55:36 -04:00
pacnpal
751cd86a31 Add operators and property owners functionality
- Implemented OperatorListView and OperatorDetailView for managing operators.
- Created corresponding templates for operator listing and detail views.
- Added PropertyOwnerListView and PropertyOwnerDetailView for managing property owners.
- Developed templates for property owner listing and detail views.
- Established relationships between parks and operators, and parks and property owners in the models.
- Created migrations to reflect the new relationships and fields in the database.
- Added admin interfaces for PropertyOwner management.
- Implemented tests for operators and property owners.
2025-07-04 14:49:36 -04:00
pacnpal
8360f3fd43 chore: Update README.md for accurate development environment setup and configuration guidance 2025-07-02 18:40:06 -04:00
pacnpal
b570cb6848 Implement comprehensive card layout improvements and testing
- Added operator/owner priority card implementation to enhance visibility on smaller screens.
- Completed adaptive grid system to eliminate white space issues and improve responsiveness across all card layouts.
- Verified card layout fixes through extensive testing, confirming balanced layouts across various screen sizes and content scenarios.
- Conducted investigation into layout inconsistencies, identifying critical issues and recommending immediate fixes.
- Assessed white space issues and confirmed no critical problems in current implementations.
- Documented comprehensive testing plan and results, ensuring all layouts are functioning as intended.
2025-07-02 16:37:23 -04:00
pacnpal
94736acdd5 chore: Remove completed OAuth configuration fix documentation 2025-06-27 21:32:33 -04:00
pacnpal
6781fa3564 feat: Comprehensive design assessments and optimizations for ThrillWiki
- Added critical design consistency assessment report highlighting major issues across various pages, including excessive white space and inconsistent element designs.
- Created detailed design assessment for park, ride, and company detail pages, identifying severe space utilization problems and poor information density.
- Documented successful layout optimization demonstration, showcasing improvements in visual design and user experience.
- Completed OAuth authentication testing for Google and Discord, confirming full functionality and readiness for production use.
- Conducted a thorough visual design examination report, identifying specific design flaws and inconsistencies, with recommendations for standardization and improvement.
2025-06-27 21:29:12 -04:00
pacnpal
4b11ec112e Refactor authentication system documentation: complete repair and verification reports, and analyze login form issues 2025-06-26 09:31:21 -04:00
pacnpal
de05a5abda Add comprehensive audit reports, design assessment, and non-authenticated features testing for ThrillWiki application
- Created critical functionality audit report identifying 7 critical issues affecting production readiness.
- Added design assessment report highlighting exceptional design quality and minor cosmetic fixes needed.
- Documented non-authenticated features testing results confirming successful functionality and public access.
- Implemented ride search form with autocomplete functionality and corresponding templates for search results.
- Developed tests for ride autocomplete functionality, ensuring proper filtering and authentication checks.
2025-06-25 20:30:02 -04:00
pacnpal
401449201c Fix search form duplication by updating event handler to submit the correct filter form and return JSON responses for park suggestions 2025-02-23 12:05:26 -05:00
pacnpal
1ca1362fee Implement park search suggestions with HTMX integration: replace legacy redirect with real-time suggestions and enhance UI for better user experience 2025-02-23 10:50:25 -05:00
pacnpal
02e4b82beb Allow unauthenticated access for autocomplete functionality 2025-02-22 15:17:49 -05:00
pacnpal
4339c5c5e0 Add autocomplete functionality for parks: implement BaseAutocomplete class and integrate with forms 2025-02-22 13:36:24 -05:00
pacnpal
5278ad39d0 Refactor imports and improve code organization: streamline import statements and enhance readability in parks/views.py 2025-02-21 20:37:03 -05:00
pacnpal
4d145ebabe Implement park search suggestions: add autocomplete functionality and improve search input handling 2025-02-21 20:36:12 -05:00
pacnpal
e4959b7a04 Improve address formatting in location widget: enhance address display logic and ensure fallback for missing fields 2025-02-21 20:20:00 -05:00
pacnpal
ef2437b7f4 2025-02-21 19:14:26 -05:00
pacnpal
3523274cbd Refactor error message handling: centralize required fields error message and improve park list template accessibility 2025-02-21 18:55:41 -05:00
pacnpal
d7951756dc Enhance park search functionality: update view mode handling and improve park list item layout 2025-02-21 18:52:01 -05:00
pacnpal
518fcbee22 Add custom development modes and guidelines for ThrillWiki project 2025-02-21 18:28:36 -05:00
pacnpal
41f1738cc1 Add migrations to alter primary key fields to BigAutoField for multiple models 2025-02-21 12:55:21 -05:00
pacnpal
645a74a4c3 Implement search functionality improvements: optimize database queries, enhance service layer, and update frontend interactions 2025-02-21 10:31:49 -05:00
pacnpal
8c85b2afd4 Update .clinerules: add guidelines for using UV with Django management commands 2025-02-19 11:13:21 -05:00
pacnpal
063398d220 Refactor development server startup instructions for clarity and conciseness 2025-02-19 09:59:39 -05:00
pacnpal
20ae4862e4 Add development server and package management guidelines to documentation 2025-02-19 09:56:23 -05:00
pacnpal
5541a5f02d Refactor park queryset logic: move base queryset to a dedicated module for improved organization and maintainability 2025-02-19 09:30:17 -05:00
pacnpal
78f465b273 Analyze feasibility of migrating from Django to Laravel; recommend maintaining current implementation due to high risks and costs 2025-02-18 10:43:13 -05:00
pacnpal
0b51ee123a Add comprehensive system architecture and feature documentation for ThrillWiki 2025-02-18 10:08:46 -05:00
pacnpal
c19aaf2f4b Implement ride count fields with real-time annotations; update filters and templates for consistency and accuracy 2025-02-13 16:44:30 -05:00
pacnpal
9d6f6dab2c Refactor park listing templates: implement grid and list view modes, enhance search results rendering, and improve error handling in search functionality 2025-02-13 12:19:23 -05:00
pacnpal
bba707fa98 Refactor search functionality: remove obsolete JavaScript and HTML templates; enhance error handling and response rendering for park search results 2025-02-13 10:29:57 -05:00
pacnpal
c197051b25 Enhance search functionality with loading indicators, dark mode support, and improved UI; implement event handling for search results and refine park filter tests for better coverage 2025-02-13 09:42:58 -05:00
pacnpal
1fe299fb4b Integrate parks app with site-wide search system; add filter configuration, error handling, and search interfaces 2025-02-12 16:59:20 -05:00
pacnpal
af57592496 Add search app configuration, views, and templates for advanced filtering functionality 2025-02-12 12:58:04 -05:00
pacnpal
62723d0e33 Implement site-wide search system architecture with modular design and enhanced backend components; integrate django-filter for improved filtering capabilities and security considerations 2025-02-12 12:41:35 -05:00
pacnpal
f5c063b76f Refactor ride list and search suggestion templates for improved structure and styling; update view logic to support dynamic template rendering based on request type 2025-02-11 15:24:14 -05:00
pacnpal
59efc39143 Add search suggestions feature with category filtering and display; enhance ride list view with improved search functionality 2025-02-11 13:41:05 -05:00
pacnpal
2e0d32819a Add views for ride management including detail, creation, and listing; implement search functionality for manufacturers, designers, and ride models 2025-02-11 10:41:40 -05:00
pacnpal
6034227796 Implement RideUpdateView and refactor search suggestion handling for improved modularity and error management 2025-02-10 23:35:58 -05:00
pacnpal
fb6c6ec37b Refactor cleanup logic in search script for improved readability and maintainability 2025-02-10 23:30:34 -05:00
pacnpal
99b935da19 Enhance search suggestions with request tracking and cleanup handlers 2025-02-10 22:10:11 -05:00
pacnpal
2756079010 Add search suggestions feature with improved filtering and UI 2025-02-10 21:27:59 -05:00
pacnpal
5195c234c6 Merge pull request #27 from pacnpal/dependabot/pip/whitenoise-6.9.0
[DEPENDABOT] Update: Bump whitenoise from 6.8.2 to 6.9.0
2025-02-10 17:29:22 -05:00
pacnpal
c861d4f6ae Merge branch 'main' into dependabot/pip/whitenoise-6.9.0 2025-02-10 17:19:54 -05:00
pacnpal
ac71e5f047 Merge pull request #28 from pacnpal/dependabot/pip/django-htmx-1.22.0
[DEPENDABOT] Update: Bump django-htmx from 1.21.0 to 1.22.0
2025-02-10 17:19:15 -05:00
pacnpal
bdbb864cef Merge pull request #29 from pacnpal/dependabot/pip/django-allauth-65.4.1
[DEPENDABOT] Update: Bump django-allauth from 65.4.0 to 65.4.1
2025-02-10 17:19:04 -05:00
pacnpal
b46e13426a Merge pull request #26 from pacnpal/dependabot/github_actions/actions/setup-python-5
[DEPENDABOT] Update Actions: Bump actions/setup-python from 4 to 5
2025-02-10 17:18:53 -05:00
pacnpal
39c8fe2c57 Merge pull request #30 from pacnpal/dependabot/pip/django-tailwind-cli-4.0.1
[DEPENDABOT] Update: Bump django-tailwind-cli from 2.21.1 to 4.0.1
2025-02-10 17:18:27 -05:00
pacnpal
c27f320a49 Merge pull request #31 from pacnpal/dependabot/pip/pytest-django-4.10.0
[DEPENDABOT] Update: Bump pytest-django from 4.9.0 to 4.10.0
2025-02-10 17:16:37 -05:00
pacnpal
f4c6cd99db Merge pull request #32 from pacnpal/dependabot/pip/django-pghistory-3.5.2
[DEPENDABOT] Update: Bump django-pghistory from 2.9.0 to 3.5.2
2025-02-10 17:16:13 -05:00
pacnpal
467f7ba3f8 Merge pull request #37 from pacnpal/pacnpal-patch-3
Update README.md
2025-02-10 17:11:34 -05:00
pacnpal
d4a1f88644 Update README.md 2025-02-10 17:10:13 -05:00
pacnpal
369c5e698e Merge pull request #35 from pacnpal/pacnpal-patch-2
Create README.md
2025-02-10 16:31:52 -05:00
pacnpal
19b7aee707 Create README.md 2025-02-10 16:20:31 -05:00
pacnpal
a09fd66d70 Merge pull request #33 from pacnpal/dependabot/pip/django-cors-headers-4.7.0
[DEPENDABOT] Update: Bump django-cors-headers from 4.6.0 to 4.7.0
2025-02-10 16:12:25 -05:00
pacnpal
910762722e Update review.yml 2025-02-10 15:03:59 -05:00
pacnpal
79e34473a4 Update review.yml 2025-02-10 15:02:47 -05:00
pacnpal
872f3378a1 Update review.yml 2025-02-10 14:59:00 -05:00
pacnpal
df91eb97b8 Add ParkContextRequired mixin to enforce park context in ride views; update URLs and templates for global ride listing 2025-02-10 14:48:29 -05:00
pacnpal
ad33332506 Update review.yml 2025-02-10 13:37:13 -05:00
pacnpal
69cdb0a554 Update review.yml 2025-02-10 13:36:58 -05:00
pacnpal
d2b6b712bf Update review.yml 2025-02-10 13:36:34 -05:00
pacnpal
1784644a52 Update review.yml 2025-02-10 13:36:19 -05:00
pacnpal
3c40a32925 Update review.yml 2025-02-10 13:36:06 -05:00
pacnpal
29392f0de1 Update review.yml 2025-02-10 13:35:29 -05:00
pacnpal
d0bd0e1bf9 Update review.yml 2025-02-10 13:35:03 -05:00
pacnpal
11e643a47a Update review.yml 2025-02-10 13:32:21 -05:00
pacnpal
db78de4cfe Update review.yml 2025-02-10 13:31:16 -05:00
pacnpal
4a495182bd Update review.yml 2025-02-10 13:28:58 -05:00
pacnpal
2add4c7fc2 Add links to ride names in ride list template for improved navigation 2025-02-10 12:45:45 -05:00
dependabot[bot]
f1c37f2bc1 [DEPENDABOT] Update: Bump django-cors-headers from 4.6.0 to 4.7.0
Bumps [django-cors-headers](https://github.com/adamchainz/django-cors-headers) from 4.6.0 to 4.7.0.
- [Changelog](https://github.com/adamchainz/django-cors-headers/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/adamchainz/django-cors-headers/compare/4.6.0...4.7.0)

---
updated-dependencies:
- dependency-name: django-cors-headers
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-10 16:50:51 +00:00
dependabot[bot]
1c71ad9b6b [DEPENDABOT] Update: Bump django-pghistory from 2.9.0 to 3.5.2
Bumps [django-pghistory](https://github.com/AmbitionEng/django-pghistory) from 2.9.0 to 3.5.2.
- [Release notes](https://github.com/AmbitionEng/django-pghistory/releases)
- [Changelog](https://github.com/AmbitionEng/django-pghistory/blob/main/CHANGELOG.md)
- [Commits](https://github.com/AmbitionEng/django-pghistory/compare/2.9.0...3.5.2)

---
updated-dependencies:
- dependency-name: django-pghistory
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-10 16:50:38 +00:00
dependabot[bot]
7e8c40db0d [DEPENDABOT] Update: Bump pytest-django from 4.9.0 to 4.10.0
Bumps [pytest-django](https://github.com/pytest-dev/pytest-django) from 4.9.0 to 4.10.0.
- [Release notes](https://github.com/pytest-dev/pytest-django/releases)
- [Changelog](https://github.com/pytest-dev/pytest-django/blob/main/docs/changelog.rst)
- [Commits](https://github.com/pytest-dev/pytest-django/compare/v4.9.0...v4.10.0)

---
updated-dependencies:
- dependency-name: pytest-django
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-10 16:50:33 +00:00
dependabot[bot]
7211c17aae [DEPENDABOT] Update: Bump django-tailwind-cli from 2.21.1 to 4.0.1
Bumps [django-tailwind-cli](https://github.com/django-commons/django-tailwind-cli) from 2.21.1 to 4.0.1.
- [Release notes](https://github.com/django-commons/django-tailwind-cli/releases)
- [Changelog](https://github.com/django-commons/django-tailwind-cli/blob/main/CHANGELOG.md)
- [Commits](https://github.com/django-commons/django-tailwind-cli/compare/2.21.1...4.0.1)

---
updated-dependencies:
- dependency-name: django-tailwind-cli
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-10 16:50:31 +00:00
dependabot[bot]
a16b0444d4 [DEPENDABOT] Update: Bump django-allauth from 65.4.0 to 65.4.1
Bumps [django-allauth](https://github.com/sponsors/pennersr) from 65.4.0 to 65.4.1.
- [Commits](https://github.com/sponsors/pennersr/commits)

---
updated-dependencies:
- dependency-name: django-allauth
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-10 16:50:27 +00:00
dependabot[bot]
a01cda306e [DEPENDABOT] Update: Bump django-htmx from 1.21.0 to 1.22.0
Bumps [django-htmx](https://github.com/adamchainz/django-htmx) from 1.21.0 to 1.22.0.
- [Changelog](https://github.com/adamchainz/django-htmx/blob/main/docs/changelog.rst)
- [Commits](https://github.com/adamchainz/django-htmx/compare/1.21.0...1.22.0)

---
updated-dependencies:
- dependency-name: django-htmx
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-10 16:50:22 +00:00
dependabot[bot]
808deb82e2 [DEPENDABOT] Update: Bump whitenoise from 6.8.2 to 6.9.0
Bumps [whitenoise](https://github.com/evansd/whitenoise) from 6.8.2 to 6.9.0.
- [Changelog](https://github.com/evansd/whitenoise/blob/main/docs/changelog.rst)
- [Commits](https://github.com/evansd/whitenoise/compare/6.8.2...6.9.0)

---
updated-dependencies:
- dependency-name: whitenoise
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-10 16:50:19 +00:00
dependabot[bot]
2d2d832e07 [DEPENDABOT] Update Actions: Bump actions/setup-python from 4 to 5
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4 to 5.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-10 16:41:58 +00:00
pacnpal
b4c474c496 Refactor URL patterns for parks and rides; update context fields in models to use ForeignKey for pghistory.Context 2025-02-10 10:54:34 -05:00
pacnpal
9ed28b15b4 Create initial migration for HistoricalSlug model; update foreign key references to use settings.AUTH_USER_MODEL and define unique constraints 2025-02-10 00:20:27 -05:00
pacnpal
4b32580b13 Update migration files for Django 5.1.4; remove obsolete merge migrations and adjust history tracking context in templates 2025-02-10 00:11:29 -05:00
pacnpal
228eeeb3c8 Add merge migrations for parks, companies, and moderation apps; update EmailConfiguration, Review, Photo, TopList, and TopListItem models to use pghistory for historical tracking 2025-02-09 16:00:10 -05:00
pacnpal
b7f6c60682 Refactor model imports and update admin classes to use pghistory for historical tracking; replace HistoricalModel with TrackedModel in relevant models 2025-02-09 11:20:40 -05:00
pacnpal
7ecf43f1a4 Add history tracking functionality using django-pghistory; implement views, templates, and middleware for event serialization and context management 2025-02-09 09:52:19 -05:00
pacnpal
a148d34cf9 Implement historical tracking using django-pghistory; add middleware for context capture and update model architecture 2025-02-08 21:18:44 -05:00
pacnpal
71b73522ae Revert "Add version control system functionality with branch management, history tracking, and merge operations"
This reverts commit f3d28817a5.
2025-02-08 17:37:30 -05:00
pacnpal
03f9df4bab Refactor comments app to use mixins for comment functionality; update admin interfaces and add historical model fixes 2025-02-08 16:33:55 -05:00
pacnpal
75f5b07129 Add comments app with models, views, and tests; integrate comments into existing models 2025-02-07 21:58:02 -05:00
pacnpal
86ae24bbac Add comprehensive evaluation and recommendations for version control system 2025-02-07 13:57:07 -05:00
pacnpal
0e0ed01cee Add comment and reply functionality with preview and notification templates 2025-02-07 13:13:49 -05:00
pacnpal
2c4d2daf34 Add OWASP compliance mapping and security test case templates, and document version control implementation phases 2025-02-07 10:51:11 -05:00
pacnpal
d353f24f9d Add timezone utility and current branch signal to enhance park detail functionality 2025-02-06 20:38:38 -05:00
pacnpal
9c65df12bb Enhance type safety in version control system by adding UserModel TypeVar, improving type hints in managers.py and utils.py, and ensuring consistent type imports. 2025-02-06 20:35:30 -05:00
pacnpal
ecf94bf84e Add version control context processor and integrate map functionality with dedicated JavaScript 2025-02-06 20:06:10 -05:00
pacnpal
f3d28817a5 Add version control system functionality with branch management, history tracking, and merge operations 2025-02-06 19:29:23 -05:00
pacnpal
6fa807f4b6 Add photo gallery functionality with file upload, sharing, and fullscreen display features 2025-02-06 14:20:48 -05:00
pacnpal
323aa561a5 Refactor authentication settings and enhance frontend moderation panel with performance optimizations, loading states, error handling, mobile responsiveness, and accessibility improvements 2025-02-06 14:20:12 -05:00
pacnpal
7d25d6f992 Update Django and dependencies, add app path configuration 2025-02-06 13:58:57 -05:00
pacnpal
19852207f6 Refactor CI workflow to use Homebrew for GDAL installation and update Python version to 3.13.1 2025-02-06 11:55:43 -05:00
pacnpal
185af7fd17 Refactor CI workflow to install pip using get-pip.py and ensure system package compatibility 2025-02-06 11:47:17 -05:00
pacnpal
768f05b783 Improve CI workflow by setting up Python and upgrading pip, setuptools, and wheel before installing dependencies 2025-02-06 11:45:29 -05:00
pacnpal
411c6f6f68 Enhance CI workflow by configuring Django GDAL settings and adding debug steps for GDAL installation verification 2025-02-06 11:26:36 -05:00
pacnpal
789a6386a5 Update GDAL installation steps in CI workflow for improved compatibility 2025-02-06 10:45:04 -05:00
pacnpal
dbd76785b5 Add GDAL installation and verification steps to CI workflow 2025-02-06 10:35:56 -05:00
pacnpal
4215e14b5e Merge pull request #25 from pacnpal/pixeebot/drip-2025-02-06-pixee-python/remove-unnecessary-f-str
Remove Unnecessary F-strings
2025-02-05 23:04:48 -05:00
pixeebot[bot]
dee7c61320 Remove Unnecessary F-strings 2025-02-06 03:53:13 +00:00
pacnpal
2f26061170 Add four GIF files to media submissions 2025-02-05 18:48:36 -05:00
pacnpal
bc68eaf4d9 Add auto-approval for moderator submissions and update submission handling 2025-02-05 18:47:23 -05:00
pacnpal
1ef38f4a96 Add three GIF files to media submissions 2025-02-05 18:23:45 -05:00
pacnpal
1f3f94702e Add LocationForm import to LocationSearchView and ignore type check for piexif import 2025-02-05 18:10:07 -05:00
pacnpal
63b484b724 Merge pull request #24 from pacnpal/dependabot/pip/black-25.1.0
[DEPENDABOT] Update: Bump black from 24.10.0 to 25.1.0
2025-02-05 18:03:44 -05:00
pacnpal
1182e894e3 Merge branch 'main' into dependabot/pip/black-25.1.0 2025-02-05 18:01:31 -05:00
pacnpal
cda755ea59 Merge pull request #23 from pacnpal/dependabot/pip/isort-tw-6.0.0
[DEPENDABOT] Update: Update isort requirement from ^5.13.0 to ^6.0.0
2025-02-05 18:00:24 -05:00
pacnpal
8bbfce3f2a Merge pull request #22 from pacnpal/pixeebot/drip-2025-01-28-pixee-python/add-requests-timeouts
Add timeout to `requests` calls
2025-02-05 18:00:10 -05:00
pacnpal
0e97fdc96b Merge pull request #21 from pacnpal/dependabot/pip/django-simple-history-3.8.0
[DEPENDABOT] Update: Bump django-simple-history from 3.7.0 to 3.8.0
2025-02-05 17:59:59 -05:00
pacnpal
ebc38228e6 Fix filter query in cleanup_test_data command to use correct field for reviews 2025-02-05 17:58:40 -05:00
dependabot[bot]
45c40c720d [DEPENDABOT] Update: Bump black from 24.10.0 to 25.1.0
Bumps [black](https://github.com/psf/black) from 24.10.0 to 25.1.0.
- [Release notes](https://github.com/psf/black/releases)
- [Changelog](https://github.com/psf/black/blob/main/CHANGES.md)
- [Commits](https://github.com/psf/black/compare/24.10.0...25.1.0)

---
updated-dependencies:
- dependency-name: black
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-03 16:57:28 +00:00
dependabot[bot]
696d26acdd [DEPENDABOT] Update: Update isort requirement from ^5.13.0 to ^6.0.0
Updates the requirements on [isort](https://github.com/pycqa/isort) to permit the latest version.
- [Release notes](https://github.com/pycqa/isort/releases)
- [Changelog](https://github.com/PyCQA/isort/blob/main/CHANGELOG.md)
- [Commits](https://github.com/pycqa/isort/compare/5.13.0...6.0.0)

---
updated-dependencies:
- dependency-name: isort
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-02-03 16:57:15 +00:00
pixeebot[bot]
96857ad1d4 Add timeout to requests calls 2025-01-28 03:09:49 +00:00
dependabot[bot]
ef40184e07 [DEPENDABOT] Update: Bump django-simple-history from 3.7.0 to 3.8.0
Bumps [django-simple-history](https://github.com/jazzband/django-simple-history) from 3.7.0 to 3.8.0.
- [Release notes](https://github.com/jazzband/django-simple-history/releases)
- [Changelog](https://github.com/jazzband/django-simple-history/blob/master/CHANGES.rst)
- [Commits](https://github.com/jazzband/django-simple-history/compare/3.7.0...3.8.0)

---
updated-dependencies:
- dependency-name: django-simple-history
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-27 16:46:08 +00:00
pacnpal
7aa706d12a Merge pull request #16 from pacnpal/dependabot/pip/dj-rest-auth-7.0.1 2025-01-21 21:59:57 -05:00
pacnpal
de6146f812 Merge pull request #17 from pacnpal/dependabot/pip/pillow-11.1.0 2025-01-21 21:59:46 -05:00
pacnpal
1bfbe4a8b4 Merge pull request #19 from pacnpal/dependabot/pip/django-5.1.5 2025-01-21 21:59:37 -05:00
pacnpal
209c3e4d21 Merge pull request #20 from pacnpal/dependabot/pip/reactivated-tw-0.46.0 2025-01-21 21:58:30 -05:00
dependabot[bot]
886b275f65 [DEPENDABOT] Update: Update reactivated requirement
Updates the requirements on [reactivated](https://github.com/silviogutierrez/django-react) to permit the latest version.
- [Commits](https://github.com/silviogutierrez/django-react/compare/v0.45.3...v0.46.0)

---
updated-dependencies:
- dependency-name: reactivated
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-20 16:28:36 +00:00
dependabot[bot]
c9ebf4c833 [DEPENDABOT] Update: Bump django from 5.1.4 to 5.1.5
Bumps [django](https://github.com/django/django) from 5.1.4 to 5.1.5.
- [Commits](https://github.com/django/django/compare/5.1.4...5.1.5)

---
updated-dependencies:
- dependency-name: django
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-20 16:28:25 +00:00
dependabot[bot]
672749d109 [DEPENDABOT] Update: Bump pillow from 11.0.0 to 11.1.0
Bumps [pillow](https://github.com/python-pillow/Pillow) from 11.0.0 to 11.1.0.
- [Release notes](https://github.com/python-pillow/Pillow/releases)
- [Changelog](https://github.com/python-pillow/Pillow/blob/main/CHANGES.rst)
- [Commits](https://github.com/python-pillow/Pillow/compare/11.0.0...11.1.0)

---
updated-dependencies:
- dependency-name: pillow
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-06 16:31:53 +00:00
dependabot[bot]
a5c3e56046 [DEPENDABOT] Update: Bump dj-rest-auth from 7.0.0 to 7.0.1
Bumps [dj-rest-auth](https://github.com/iMerica/dj-rest-auth) from 7.0.0 to 7.0.1.
- [Release notes](https://github.com/iMerica/dj-rest-auth/releases)
- [Commits](https://github.com/iMerica/dj-rest-auth/compare/7.0.0...7.0.1)

---
updated-dependencies:
- dependency-name: dj-rest-auth
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-01-06 16:31:42 +00:00
pacnpal
d728ba6e9c Merge pull request #14 from pacnpal/dependabot/pip/django-allauth-65.3.1 2025-01-02 22:31:24 -05:00
pacnpal
f819a1f07c Merge pull request #13 from pacnpal/pixeebot/drip-2024-12-26-pixee-python/secure-random 2025-01-02 22:30:39 -05:00
dependabot[bot]
d91f79e29c [DEPENDABOT] Update: Bump django-allauth from 65.3.0 to 65.3.1
Bumps [django-allauth](https://github.com/sponsors/pennersr) from 65.3.0 to 65.3.1.
- [Commits](https://github.com/sponsors/pennersr/commits)

---
updated-dependencies:
- dependency-name: django-allauth
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-30 16:59:20 +00:00
pixeebot[bot]
304812d43f Secure Source of Randomness 2024-12-26 03:11:52 +00:00
pacnpal
3f7296d7a5 ygeah 2024-12-24 14:32:26 -05:00
pacnpal
e60f73de9d Add test setup, cleanup, and fixtures 2024-12-24 14:32:16 -05:00
pacnpal
af7ea6b4ce Add comprehensive e2e tests with Playwright 2024-12-24 14:29:32 -05:00
pacnpal
36478c7a1b Fix dependencies and GeoDjango configuration 2024-12-24 14:24:18 -05:00
pacnpal
c8628984e0 Merge pull request #1 from pacnpal/dependabot/github_actions/actions/setup-python-5
[DEPENDABOT] Update Actions: Bump actions/setup-python from 3 to 5
2024-12-24 11:18:44 -05:00
pacnpal
1bfe08a0a7 Merge pull request #2 from pacnpal/dependabot/pip/django-allauth-65.3.0
[DEPENDABOT] Update: Bump django-allauth from 65.2.0 to 65.3.0
2024-12-24 11:18:32 -05:00
pacnpal
901a1c421d Merge pull request #3 from pacnpal/dependabot/pip/channels-4.2.0
[DEPENDABOT] Update: Bump channels from 4.1.0 to 4.2.0
2024-12-24 11:18:21 -05:00
dependabot[bot]
280ad4d6da [DEPENDABOT] Update: Bump channels from 4.1.0 to 4.2.0
Bumps [channels](https://github.com/django/channels) from 4.1.0 to 4.2.0.
- [Changelog](https://github.com/django/channels/blob/main/CHANGELOG.txt)
- [Commits](https://github.com/django/channels/compare/4.1.0...4.2.0)

---
updated-dependencies:
- dependency-name: channels
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-24 16:17:52 +00:00
dependabot[bot]
9634bac155 [DEPENDABOT] Update: Bump django-allauth from 65.2.0 to 65.3.0
Bumps [django-allauth](https://github.com/sponsors/pennersr) from 65.2.0 to 65.3.0.
- [Commits](https://github.com/sponsors/pennersr/commits)

---
updated-dependencies:
- dependency-name: django-allauth
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-24 16:17:15 +00:00
pacnpal
69094a9af8 Merge pull request #4 from pacnpal/dependabot/pip/django-5.1.4
[DEPENDABOT] Update: Bump django from 5.1.3 to 5.1.4
2024-12-24 11:17:01 -05:00
pacnpal
d338917ca1 Merge pull request #5 from pacnpal/dependabot/pip/pytest-8.3.4
[DEPENDABOT] Update: Bump pytest from 8.3.3 to 8.3.4
2024-12-24 11:16:52 -05:00
pacnpal
5acc74d34c Merge pull request #6 from pacnpal/dependabot/pip/channels-redis-4.2.1
[DEPENDABOT] Update: Bump channels-redis from 4.2.0 to 4.2.1
2024-12-24 11:16:41 -05:00
pacnpal
8b7ad53cbd Merge pull request #7 from pacnpal/dependabot/pip/pyjwt-2.10.1
[DEPENDABOT] Update: Bump pyjwt from 2.9.0 to 2.10.1
2024-12-24 11:16:07 -05:00
pacnpal
7553752f0d Update review.yml 2024-12-24 11:14:13 -05:00
dependabot[bot]
1ca84208ef [DEPENDABOT] Update: Bump django from 5.1.3 to 5.1.4
Bumps [django](https://github.com/django/django) from 5.1.3 to 5.1.4.
- [Commits](https://github.com/django/django/compare/5.1.3...5.1.4)

---
updated-dependencies:
- dependency-name: django
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-24 16:03:05 +00:00
pacnpal
fdd7a4fcf1 Merge pull request #8 from pacnpal/dependabot/pip/django-tailwind-cli-2.21.1
[DEPENDABOT] Update: Bump django-tailwind-cli from 2.19.0 to 2.21.1
2024-12-24 11:02:08 -05:00
pacnpal
ae8710c157 Merge pull request #12 from pacnpal/dependabot/pip/reactivated-tw-0.45.3
[DEPENDABOT] Update: Update reactivated requirement from ^0.41.1 to ^0.45.3
2024-12-24 11:00:31 -05:00
pacnpal
56d9174bb5 Update and rename main.yml to review.yml 2024-12-24 10:59:19 -05:00
pacnpal
5c62b41070 Update main.yml 2024-12-24 10:51:01 -05:00
pacnpal
8014bcc368 Create main.yml 2024-12-24 10:49:55 -05:00
dependabot[bot]
1a60658f17 [DEPENDABOT] Update: Update reactivated requirement
Updates the requirements on [reactivated](https://github.com/silviogutierrez/django-react) to permit the latest version.
- [Commits](https://github.com/silviogutierrez/django-react/compare/v0.41.1...v0.45.3)

---
updated-dependencies:
- dependency-name: reactivated
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-23 16:44:37 +00:00
dependabot[bot]
311c0e999c [DEPENDABOT] Update: Bump django-tailwind-cli from 2.19.0 to 2.21.1
Bumps [django-tailwind-cli](https://github.com/django-commons/django-tailwind-cli) from 2.19.0 to 2.21.1.
- [Release notes](https://github.com/django-commons/django-tailwind-cli/releases)
- [Changelog](https://github.com/django-commons/django-tailwind-cli/blob/main/CHANGELOG.md)
- [Commits](https://github.com/django-commons/django-tailwind-cli/compare/2.19.0...2.21.1)

---
updated-dependencies:
- dependency-name: django-tailwind-cli
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-10 11:45:12 +00:00
dependabot[bot]
6af9f7332a [DEPENDABOT] Update: Bump pyjwt from 2.9.0 to 2.10.1
Bumps [pyjwt](https://github.com/jpadilla/pyjwt) from 2.9.0 to 2.10.1.
- [Release notes](https://github.com/jpadilla/pyjwt/releases)
- [Changelog](https://github.com/jpadilla/pyjwt/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/jpadilla/pyjwt/compare/2.9.0...2.10.1)

---
updated-dependencies:
- dependency-name: pyjwt
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-10 11:44:58 +00:00
dependabot[bot]
7c81d8e8eb [DEPENDABOT] Update: Bump channels-redis from 4.2.0 to 4.2.1
Bumps [channels-redis](https://github.com/django/channels_redis) from 4.2.0 to 4.2.1.
- [Changelog](https://github.com/django/channels_redis/blob/main/CHANGELOG.txt)
- [Commits](https://github.com/django/channels_redis/compare/4.2.0...4.2.1)

---
updated-dependencies:
- dependency-name: channels-redis
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-10 11:44:55 +00:00
dependabot[bot]
04daf9573b [DEPENDABOT] Update: Bump pytest from 8.3.3 to 8.3.4
Bumps [pytest](https://github.com/pytest-dev/pytest) from 8.3.3 to 8.3.4.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/8.3.3...8.3.4)

---
updated-dependencies:
- dependency-name: pytest
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-10 11:44:52 +00:00
dependabot[bot]
96e2f097cd [DEPENDABOT] Update Actions: Bump actions/setup-python from 3 to 5
Bumps [actions/setup-python](https://github.com/actions/setup-python) from 3 to 5.
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](https://github.com/actions/setup-python/compare/v3...v5)

---
updated-dependencies:
- dependency-name: actions/setup-python
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-12-10 11:44:15 +00:00
pacnpal
10dfa20d65 Update dependabot.yml 2024-12-10 06:43:27 -05:00
pacnpal
d514687651 Update dependabot.yml 2024-12-10 06:21:23 -05:00
pacnpal
6e6558d51a Remove . from version control 2024-12-06 13:39:54 -05:00
pacnpal
8e9aabb2ab Remove __pycache__ from version control 2024-12-06 13:39:23 -05:00
pacnpal
7365a23205 gitignore 2024-12-06 13:36:12 -05:00
pacnpal
86059bbcba fix homepage counts 2024-12-06 13:30:45 -05:00
pacnpal
43ec3efa68 1 2024-12-05 23:31:15 -05:00
pacnpal
addb9511da git 2024-12-05 20:27:29 -05:00
pacnpal
dfe6194039 ok 2024-11-14 04:15:24 +00:00
pacnpal
e63677e8c0 the ghost of the sea 2024-11-14 02:48:41 +00:00
pacnpal
af3e255a2e Fix moderation submission template:
- Fix unclosed if tags and template structure
- Fix ride category selection and stats display
- Fix location map display and remove duplicates
- Fix grid layout and closing tags
2024-11-14 02:31:28 +00:00
pacnpal
ebbf772669 Fix moderation submission display:
- Fix location map display in view mode
- Remove duplicate location map
- Fix grid layout and closing tags
- Show location map before other fields
2024-11-14 02:24:01 +00:00
pacnpal
891c29beff Fix moderation submission display:
- Add location map template for view mode
- Add coaster fields template for stats
- Fix location and stats display in both view and edit modes
2024-11-14 02:16:04 +00:00
pacnpal
e86cb95f14 Fix moderation submission edit view:
- Add coaster stats fields template
- Fix ride category selection and stats display
- Fix location widget and map initialization
2024-11-14 02:10:14 +00:00
pacnpal
fd97ed31cb Fix map initialization and park area selection in moderation submission edit view 2024-11-14 01:56:56 +00:00
pacnpal
d9990ae241 Fix ride category, status, and park area selection in moderation submission edit view 2024-11-14 01:32:37 +00:00
pacnpal
da0ee1acfa Fix ride category selection and stats field display in moderation submission edit view 2024-11-14 01:24:27 +00:00
pacnpal
30b786d51e Make sign in and registration screens scrollable
- Remove fixed height constraints from main pages
- Add responsive padding
- Add max-height and overflow handling to modals
- Make modal headers sticky
- Improve overall scrolling behavior
2024-11-14 01:02:54 +00:00
pacnpal
8d70bf8994 [Documentation] Create context documentation system
- Created comprehensive documentation structure
- Added productContext.md for product understanding
- Added activeContext.md for current development status
- Added systemPatterns.md for architecture patterns
- Added developmentWorkflow.md for process standards
- Added operationalContext.md for runtime details
- Added projectBoundaries.md for technical constraints
- Added techContext.md for technology stack
- Documented all major aspects of the system
- Established foundation for maintaining context
2024-11-13 23:40:36 +00:00
pacnpal
edc9d66849 Add enhanced toast notifications for moderation actions
- Add color-coded toast notifications for different actions
- Use appropriate icons for each action type
- Improve toast positioning and z-index
- Add smooth transitions for better UX
- Remove duplicate toast implementation
2024-11-13 22:02:59 +00:00
pacnpal
8265348a83 code commit 2024-11-13 21:59:49 +00:00
pacnpal
8f7f7add2d Fix moderation dashboard CSS formatting
- Update category and status select fields with consistent styling
- Add proper dark mode support for form elements
- Improve form field spacing and alignment
- Standardize input field styling across the dashboard
2024-11-13 21:31:02 +00:00
pacnpal
7ec4d964dc Update loading indicator styling for dark mode compatibility 2024-11-13 18:59:04 +00:00
pacnpal
d68c927a00 Move loading indicator to center of empty state div in submission list 2024-11-13 18:57:48 +00:00
pacnpal
caba5c6158 Update active tab indicator colors in light mode to use blue instead of grey 2024-11-13 18:55:14 +00:00
pacnpal
131ef7ceb0 Update dark mode styling for submission list to match site theme 2024-11-13 18:53:03 +00:00
pacnpal
a30f3ef644 Update dark mode styling for moderation dashboard to match site theme 2024-11-13 18:51:15 +00:00
pacnpal
2e1040e3a6 Add Moderation Dashboard title to the top of the dashboard view 2024-11-13 18:48:18 +00:00
pacnpal
751d21098d Add refresh button to moderation dashboard that maintains current filters when refreshing content 2024-11-13 18:46:32 +00:00
pacnpal
1acfe9d29e Improve moderation dashboard UI and functionality
- Add status tabs (Pending, Approved, Rejected, Escalated)
- Implement HTMX for smooth tab switching and status changes
- Add proper permissions for escalated submissions
- Change Status filter to Submission Type (Text/Photo)
- Move navigation into dashboard content
- Fix tab menu visibility and transitions
- Add contextual loading indicator
- Update styling to match dark theme
- Ensure consistent styling across components
2024-11-13 18:38:03 +00:00
pacnpal
6a9154ce69 Improve moderation dashboard UI and functionality
- Add status tabs (Pending, Approved, Rejected, Escalated)
- Implement HTMX for smooth tab switching and status changes
- Add proper permissions for escalated submissions
- Change Status filter to Submission Type (Text/Photo)
- Move navigation into dashboard content
- Fix tab menu visibility and transitions
- Add contextual loading indicator
- Update styling to match dark theme
- Ensure consistent styling across components
2024-11-13 18:37:36 +00:00
pacnpal
15e56c9770 Add CSRF token handling to HTMX requests:
- Configure HTMX to include CSRF token in request headers
- Add global HTMX configuration in dashboard.html
- Fix CSRF token missing error in moderation actions
2024-11-13 17:16:43 +00:00
pacnpal
09ee45f6c7 Fix duplicate menus in moderation dashboard:
- Move navigation to separate moderation_nav.html partial
- Include navigation only once in dashboard.html base template
- Remove duplicate navigation from child templates
2024-11-13 17:09:22 +00:00
pacnpal
177117f4d6 Improve moderation dashboard UI and HTMX integration:
- Create partial templates for dashboard, edit submissions, and photo submissions
- Update views to properly handle HTMX requests
- Fix duplicate UI issues when navigating
- Improve overall UI design and transitions
2024-11-13 17:04:42 +00:00
pacnpal
96341bfd82 ignore tailwind 2024-11-13 16:45:09 +00:00
pacnpal
f011d58c6d Fix duplicate UI in moderation dashboard by separating HTMX partial templates and full page templates 2024-11-13 16:34:31 +00:00
pacnpal
983c101ed1 Added support for Django's built-in superuser to access moderation features. Modified context processor and views to check both role-based and is_superuser permissions. 2024-11-13 16:13:54 +00:00
pacnpal
97a3555e81 Bypass Turnstile validation when DEBUG is True 2024-11-13 16:10:25 +00:00
pacnpal
ec626b4124 Disable Cloudflare Turnstile on login/signup pages when DEBUG is True 2024-11-13 15:56:42 +00:00
pacnpal
537ea0fc07 Improve code readability in moderation models using walrus operator 2024-11-13 15:51:54 +00:00
pacnpal
be07a17460 Improve code readability with boolean expressions and better control flow 2024-11-13 15:50:38 +00:00
pacnpal
1c03e4acb8 Improve type hints and code organization in moderation views 2024-11-13 15:47:19 +00:00
pacnpal
a5ebeb51dc add tailwind to gitignore as generated on the fly 2024-11-13 15:42:57 +00:00
pacnpal
1ee4b00961 Add password confirmation, validation, and email notification for password changes 2024-11-13 15:17:07 +00:00
pacnpal
5a1fdb6d16 Set max width of settings container to 800px for better readability and focus 2024-11-13 15:05:56 +00:00
pacnpal
78355c60f9 Updated moderation link in navbar to point to dashboard instead of edit submissions - Improves navigation flow for moderators, admins, and superusers 2024-11-13 15:03:04 +00:00
pacnpal
d8a65f4e81 Verified admin and superuser access to moderation dashboard - Access control already properly implemented through ModeratorRequiredMixin with ADMIN and SUPERUSER roles included in permission checks 2024-11-13 14:57:49 +00:00
pacnpal
cac6335bb7 Fix stats cards styling on home page - Add consistent flex layout for perfect centering - Fix HTML structure in parks card - Standardize hover effects and shadows across all cards 2024-11-13 14:55:11 +00:00
pacnpal
7f4de7c2ec Fixed mod models 2024-11-13 14:53:31 +00:00
pacnpal
08e97f21b7 Reorganize moderation dashboard structure:
- Move from /moderation/admin to /moderation to avoid conflicts
- Improve template organization with partials
- Add HTMX-powered navigation and filtering
- Add smooth transitions and loading states
- Improve photo submission handling
- Add review notes functionality
- Add confirmation dialogs
2024-11-13 14:50:34 +00:00
pacnpal
9ee380c3ea Enhance moderation dashboard UI and UX:
- Add HTMX-powered filtering with instant updates
- Add smooth transitions and loading states
- Improve visual hierarchy and styling
- Add review notes functionality
- Add confirmation dialogs for actions
- Make navigation sticky
- Add hover effects and visual feedback
- Improve dark mode support
2024-11-13 14:38:38 +00:00
pacnpal
d2c9d02523 Enhance moderation dashboard UI and UX:
- Add HTMX-powered filtering with instant updates
- Add smooth transitions and loading states
- Improve visual hierarchy and styling
- Add review notes functionality
- Add confirmation dialogs for actions
- Make navigation sticky
- Add hover effects and visual feedback
- Improve dark mode support
2024-11-13 14:38:17 +00:00
pacnpal
75287c8994 upgrade to Django 5.1.3 and Python 3.13 2024-11-12 15:08:08 +00:00
pacnpal
d7a89d8725 fixing the home page 2024-11-06 19:09:56 +00:00
pacnpal
ff52f182fc Merge remote-tracking branch 'origin/main' 2024-11-05 23:37:13 +00:00
pacnpal
33b7c1a81a profile changes 2024-11-05 23:36:50 +00:00
pacnpal
8b93f67e31 Update django.yml 2024-11-05 16:54:52 -05:00
pacnpal
9df5ab931f Create django.yml 2024-11-05 16:53:24 -05:00
11982 changed files with 415340 additions and 1991672 deletions

View File

@@ -0,0 +1,73 @@
# Migration Source: thrillwiki-87
The React project at `/Volumes/macminissd/Projects/thrillwiki-87` is the **authoritative source** for ThrillWiki features and functionality.
## Core Principle
**thrillwiki-87 is LAW.** When migrating features, the React implementation defines:
- What features must exist
- How they should behave
- What data structures are required
- What UI patterns to follow
## Source Project Structure
```
thrillwiki-87/
├── src/
│ ├── components/ # React components (44 directories)
│ ├── pages/ # Route pages (39 files)
│ ├── hooks/ # React hooks (80+ files)
│ ├── types/ # TypeScript definitions (63 files)
│ ├── contexts/ # React contexts
│ ├── lib/ # Utilities
│ └── integrations/ # External service integrations
├── docs/ # Feature documentation (78 files)
│ ├── SITE_OVERVIEW.md
│ ├── DESIGN_SYSTEM.md
│ ├── COMPONENTS.md
│ ├── PAGES.md
│ └── USER_FLOWS.md
└── supabase/ # Backend schemas and functions
```
## Technology Translation
| React (Source) | Nuxt 4 (Target) |
|----------------|-----------------|
| React component | Vue SFC (.vue) |
| useState | ref() / reactive() |
| useEffect | watch() / onMounted() |
| useContext | provide() / inject() or Pinia |
| React Router | Nuxt file-based routing |
| React Query | useAsyncData / useFetch |
| shadcn-ui | Nuxt UI |
| Supabase client | Django REST API via useApi() |
| Edge Functions | Django views |
## Backend Translation
| Supabase (Source) | Django (Target) |
|-------------------|-----------------|
| Table | Django Model |
| RLS policies | DRF permissions |
| Edge Functions | Django views/viewsets |
| Realtime | SSE / WebSockets |
| Auth | django-allauth + JWT |
| Storage | Cloudflare R2 |
## Migration Workflow
1. **Find source** in thrillwiki-87
2. **Read the docs** in thrillwiki-87/docs/
3. **Check existing** Nuxt implementation
4. **Port missing features** to achieve parity
5. **Verify behavior** matches source
## Key Source Files to Reference
When porting a feature, always check:
- `thrillwiki-87/docs/` for specifications
- `thrillwiki-87/src/types/` for data structures
- `thrillwiki-87/src/hooks/` for business logic
- `thrillwiki-87/src/components/` for UI patterns

View File

@@ -0,0 +1,83 @@
# Source Mapping: React → Nuxt
Quick reference for mapping thrillwiki-87 paths to thrillwiki_django_no_react paths.
## Directory Mappings
| React (thrillwiki-87) | Nuxt (thrillwiki_django_no_react) |
|----------------------|-----------------------------------|
| `src/components/` | `frontend/app/components/` |
| `src/pages/` | `frontend/app/pages/` |
| `src/hooks/` | `frontend/app/composables/` |
| `src/types/` | `frontend/app/types/` |
| `src/lib/` | `frontend/app/utils/` |
| `src/contexts/` | `frontend/app/stores/` (Pinia) |
| `docs/` | `source_docs/` |
| `supabase/migrations/` | `backend/apps/*/models.py` |
## Component Mappings (shadcn-ui → Nuxt UI)
| shadcn-ui | Nuxt UI |
|-----------|---------|
| `<Button>` | `<UButton>` |
| `<Card>` | `<UCard>` |
| `<Dialog>` | `<UModal>` |
| `<Input>` | `<UInput>` |
| `<Select>` | `<USelect>` / `<USelectMenu>` |
| `<Tabs>` | `<UTabs>` |
| `<Table>` | `<UTable>` |
| `<Badge>` | `<UBadge>` |
| `<Avatar>` | `<UAvatar>` |
| `<Tooltip>` | `<UTooltip>` |
| `<Sheet>` | `<USlideover>` |
| `<AlertDialog>` | `<UModal>` + confirm pattern |
| `<Skeleton>` | `<USkeleton>` |
| `<Textarea>` | `<UTextarea>` |
| `<Checkbox>` | `<UCheckbox>` |
| `<RadioGroup>` | `<URadioGroup>` |
| `<Switch>` | `<UToggle>` |
| `<DropdownMenu>` | `<UDropdown>` |
| `<Command>` | `<UCommandPalette>` |
| `<Popover>` | `<UPopover>` |
## Page Mappings
| React Page | Nuxt Page |
|------------|-----------|
| `Index.tsx` | `pages/index.vue` |
| `Parks.tsx` | `pages/parks/index.vue` |
| `ParkDetail.tsx` | `pages/parks/[park_slug]/index.vue` |
| `Rides.tsx` | `pages/rides/index.vue` |
| `RideDetail.tsx` | `pages/parks/[park_slug]/rides/[ride_slug].vue` |
| `Manufacturers.tsx` | `pages/manufacturers/index.vue` |
| `ManufacturerDetail.tsx` | `pages/manufacturers/[slug].vue` |
| `Designers.tsx` | `pages/designers/index.vue` |
| `DesignerDetail.tsx` | `pages/designers/[slug].vue` |
| `Operators.tsx` | `pages/operators/index.vue` |
| `OperatorDetail.tsx` | `pages/operators/[slug].vue` |
| `ParkOwners.tsx` | `pages/owners/index.vue` |
| `PropertyOwnerDetail.tsx` | `pages/owners/[slug].vue` |
| `Auth.tsx` | `pages/auth/login.vue`, `pages/auth/signup.vue` |
| `Profile.tsx` | `pages/profile/index.vue` |
| `Search.tsx` | `pages/search.vue` |
| `AdminDashboard.tsx` | `pages/admin/index.vue` |
## Hook → Composable Mappings
| React Hook | Vue Composable |
|------------|----------------|
| `useAuth.tsx` | `useAuth.ts` |
| `useSearch.tsx` | `useSearchHistory.ts` |
| `useModerationQueue.ts` | `useModeration.ts` |
| `useProfile.tsx` | (inline in pages) |
| `useLocations.ts` | `useParksApi.ts` |
| `useUnitPreferences.ts` | `useUnits.ts` |
## API Endpoint Translation
| Supabase RPC/Query | Django API |
|--------------------|------------|
| `supabase.from('parks')` | `GET /api/v1/parks/` |
| `supabase.rpc('search_*')` | `GET /api/v1/search/` |
| `supabase.auth.*` | `/api/v1/auth/*` |
| Edge Functions | Django views in `backend/apps/*/views.py` |

View File

@@ -0,0 +1,329 @@
# ThrillWiki API Conventions
Standards for designing and implementing APIs between the Django backend and Nuxt frontend.
## API Base Structure
### URL Patterns
```
/api/v1/ # API root (versioned)
/api/v1/parks/ # List/Create parks
/api/v1/parks/{slug}/ # Retrieve/Update/Delete park
/api/v1/parks/{slug}/rides/ # Nested resource
/api/v1/auth/ # Authentication endpoints
/api/v1/users/me/ # Current user
```
### HTTP Methods
| Method | Usage |
|--------|-------|
| GET | Retrieve resource(s) |
| POST | Create resource |
| PUT | Replace resource entirely |
| PATCH | Update resource partially |
| DELETE | Remove resource |
## Response Formats
### Success Response (Single Resource)
```json
{
"id": "uuid",
"name": "Cedar Point",
"slug": "cedar-point",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-16T14:20:00Z",
...
}
```
### Success Response (List)
```json
{
"count": 150,
"next": "/api/v1/parks/?page=2",
"previous": null,
"results": [
{ ... },
{ ... }
]
}
```
### Error Response
```json
{
"error": {
"code": "validation_error",
"message": "Invalid input data",
"details": {
"name": ["This field is required."],
"status": ["Invalid choice."]
}
}
}
```
### HTTP Status Codes
| Code | Meaning | When to Use |
|------|---------|-------------|
| 200 | OK | Successful GET, PUT, PATCH |
| 201 | Created | Successful POST |
| 204 | No Content | Successful DELETE |
| 400 | Bad Request | Validation errors |
| 401 | Unauthorized | Missing/invalid auth |
| 403 | Forbidden | Insufficient permissions |
| 404 | Not Found | Resource doesn't exist |
| 409 | Conflict | Duplicate resource |
| 500 | Server Error | Unexpected errors |
## Pagination
### Query Parameters
```
?page=2 # Page number (default: 1)
?page_size=20 # Items per page (default: 20, max: 100)
```
### Response
```json
{
"count": 150,
"next": "/api/v1/parks/?page=3",
"previous": "/api/v1/parks/?page=1",
"results": [ ... ]
}
```
## Filtering & Sorting
### Filtering
```
GET /api/v1/parks/?status=operating
GET /api/v1/parks/?country=USA&status=operating
GET /api/v1/rides/?park=cedar-point&type=coaster
```
### Search
```
GET /api/v1/parks/?search=cedar
```
### Sorting
```
GET /api/v1/parks/?ordering=name # Ascending
GET /api/v1/parks/?ordering=-created_at # Descending
GET /api/v1/parks/?ordering=-rating,name # Multiple fields
```
## Authentication
### Token-Based Auth
```
Authorization: Bearer <jwt_token>
```
### Endpoints
```
POST /api/v1/auth/login/ # Get tokens
POST /api/v1/auth/register/ # Create account
POST /api/v1/auth/refresh/ # Refresh access token
POST /api/v1/auth/logout/ # Invalidate tokens
GET /api/v1/auth/me/ # Current user info
```
### Social Auth
```
POST /api/v1/auth/google/ # Google OAuth
POST /api/v1/auth/discord/ # Discord OAuth
```
## Content Submission API
### Submit New Content
```
POST /api/v1/submissions/
{
"content_type": "park",
"data": {
"name": "New Park Name",
"city": "Orlando",
...
}
}
```
### Response
```json
{
"id": "submission-uuid",
"status": "pending",
"content_type": "park",
"data": { ... },
"created_at": "2024-01-15T10:30:00Z"
}
```
### Submit Edit to Existing Content
```
POST /api/v1/submissions/
{
"content_type": "park",
"object_id": "existing-park-uuid",
"data": {
"description": "Updated description..."
}
}
```
## Moderation API
### Get Moderation Queue
```
GET /api/v1/moderation/
GET /api/v1/moderation/?status=pending&type=park
```
### Review Submission
```
POST /api/v1/moderation/{id}/approve/
POST /api/v1/moderation/{id}/reject/
{
"notes": "Reason for rejection..."
}
```
## Nested Resources
### Pattern
```
/api/v1/parks/{slug}/rides/ # List rides in park
/api/v1/parks/{slug}/reviews/ # List park reviews
/api/v1/parks/{slug}/photos/ # List park photos
```
### When to Nest vs Flat
**Nest when:**
- Resource only makes sense in context of parent (park photos)
- Need to filter by parent frequently
**Use flat with filter when:**
- Resource can exist independently
- Need to query across parents
```
# Flat with filter
GET /api/v1/rides/?park=cedar-point
# Or nested
GET /api/v1/parks/cedar-point/rides/
```
## Geolocation API
### Parks Nearby
```
GET /api/v1/parks/nearby/?lat=41.4821&lng=-82.6822&radius=50
```
### Response includes distance
```json
{
"results": [
{
"id": "uuid",
"name": "Cedar Point",
"distance": 12.5, // in user's preferred units
...
}
]
}
```
## File Uploads
### Photo Upload
```
POST /api/v1/photos/
Content-Type: multipart/form-data
{
"content_type": "park",
"object_id": "park-uuid",
"image": <file>,
"caption": "Optional caption"
}
```
### Response
```json
{
"id": "photo-uuid",
"url": "https://cdn.thrillwiki.com/photos/...",
"thumbnail_url": "https://cdn.thrillwiki.com/photos/.../thumb",
"status": "pending", // Pending moderation
"created_at": "2024-01-15T10:30:00Z"
}
```
## Rate Limiting
### Headers
```
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1642089600
```
### When Limited
```
HTTP/1.1 429 Too Many Requests
{
"error": {
"code": "rate_limit_exceeded",
"message": "Too many requests. Try again in 60 seconds."
}
}
```
## Frontend Integration
### API Client Setup (Nuxt)
```typescript
// composables/useApi.ts
export function useApi() {
const config = useRuntimeConfig()
const authStore = useAuthStore()
const api = $fetch.create({
baseURL: config.public.apiBase,
headers: authStore.token ? {
Authorization: `Bearer ${authStore.token}`
} : {},
onResponseError: ({ response }) => {
if (response.status === 401) {
authStore.logout()
navigateTo('/auth/login')
}
}
})
return api
}
```
### Usage in Components
```typescript
const api = useApi()
// Fetch parks
const { data: parks } = await useAsyncData('parks', () =>
api('/parks/', { params: { status: 'operating' } })
)
// Create submission
await api('/submissions/', {
method: 'POST',
body: { content_type: 'park', data: formData }
})
```

View File

@@ -0,0 +1,306 @@
# ThrillWiki Component Patterns
Guidelines for building UI components consistent with ThrillWiki's design system.
## Component Hierarchy
```
components/
├── layout/ # Page structure
│ ├── Header.vue
│ ├── Footer.vue
│ ├── Sidebar.vue
│ └── PageContainer.vue
├── ui/ # Base components (shadcn/ui style)
│ ├── Button.vue
│ ├── Card.vue
│ ├── Input.vue
│ ├── Badge.vue
│ ├── Avatar.vue
│ ├── Modal.vue
│ └── ...
├── entity/ # Domain-specific cards
│ ├── ParkCard.vue
│ ├── RideCard.vue
│ ├── ReviewCard.vue
│ ├── CreditCard.vue
│ └── CompanyCard.vue
├── forms/ # Form components
│ ├── ParkForm.vue
│ ├── ReviewForm.vue
│ └── ...
└── specialty/ # Complex/unique components
├── SearchAutocomplete.vue
├── Map.vue
├── ImageGallery.vue
├── UnitDisplay.vue
└── RatingDisplay.vue
```
## Base Components (ui/)
### Card
```vue
<template>
<div :class="[
'rounded-lg border bg-card text-card-foreground',
interactive && 'hover:shadow-md transition-shadow cursor-pointer',
className
]">
<slot />
</div>
</template>
<script setup lang="ts">
defineProps<{
interactive?: boolean
className?: string
}>()
</script>
```
### Button
```vue
<template>
<button :class="[buttonVariants({ variant, size }), className]">
<slot />
</button>
</template>
<script setup lang="ts">
const props = defineProps<{
variant?: 'default' | 'secondary' | 'outline' | 'ghost' | 'destructive'
size?: 'default' | 'sm' | 'lg' | 'icon'
className?: string
}>()
</script>
```
### Badge
```vue
<template>
<span :class="[badgeVariants({ variant }), className]">
<slot />
</span>
</template>
<script setup lang="ts">
defineProps<{
variant?: 'default' | 'secondary' | 'success' | 'warning' | 'destructive' | 'outline'
className?: string
}>()
</script>
```
## Entity Cards
### ParkCard
Displays park preview with image, name, location, stats, and status.
**Required Props:**
- `park: Park` - Park object
**Displays:**
- Park image (with fallback)
- Park name
- Location (city, country)
- Ride count
- Average rating
- Status badge (Operating/Closed/Under Construction)
**Interactions:**
- Click navigates to park detail page
- Hover shows elevation shadow
```vue
<template>
<NuxtLink :to="`/parks/${park.slug}`">
<Card interactive>
<div class="aspect-video relative overflow-hidden rounded-t-lg">
<NuxtImg
:src="park.image || '/placeholder-park.jpg'"
:alt="park.name"
class="object-cover w-full h-full"
/>
</div>
<div class="p-4">
<h3 class="font-semibold text-lg line-clamp-1">{{ park.name }}</h3>
<p class="text-sm text-muted-foreground flex items-center gap-1">
<MapPin class="w-4 h-4" />
{{ park.city }}, {{ park.country }}
</p>
<div class="flex items-center justify-between mt-2">
<span class="text-sm">🎢 {{ park.rideCount }} rides</span>
<RatingDisplay :rating="park.averageRating" size="sm" />
</div>
<Badge :variant="statusVariant" class="mt-2">
{{ park.status }}
</Badge>
</div>
</Card>
</NuxtLink>
</template>
```
### RideCard
Similar structure to ParkCard, but shows:
- Ride image
- Ride name
- Park name (linked)
- Key specs (speed, height)
- Type badge + Status badge
### ReviewCard
Displays user review with:
- User avatar + username
- Rating (5-star display)
- Review date (relative)
- Review text
- Helpful votes (👍 count)
- Actions (Reply, Report)
### CreditCard
For user's ride credit list:
- Ride thumbnail
- Ride name + park name
- Ride count with +/- controls
- Last ridden date
- Edit button
## Specialty Components
### SearchAutocomplete
Global search with instant results.
**Features:**
- Debounced input (300ms)
- Results grouped by type (Parks, Rides, Companies)
- Keyboard navigation
- Click or Enter to navigate
- Empty state handling
**Implementation Notes:**
- Use `useFetch` with `watch` for reactive searching
- Show loading skeleton while fetching
- Limit results to 10 per category
- Highlight matching text
### UnitDisplay
Converts and displays values in user's preferred units.
```vue
<template>
<span>{{ formattedValue }}</span>
</template>
<script setup lang="ts">
const props = defineProps<{
value: number
type: 'speed' | 'height' | 'length' | 'weight'
showBoth?: boolean // Show both metric and imperial
}>()
const { preferredUnits } = useUnits()
const formattedValue = computed(() => {
// Convert and format based on type and preference
})
</script>
```
### RatingDisplay
Star rating visualization.
```vue
<template>
<div class="flex items-center gap-1">
<div class="flex">
<Star
v-for="i in 5"
:key="i"
:class="[
'w-4 h-4',
i <= Math.round(rating) ? 'fill-yellow-400 text-yellow-400' : 'text-gray-300'
]"
/>
</div>
<span class="text-sm font-medium">{{ rating.toFixed(1) }}</span>
<span v-if="count" class="text-sm text-muted-foreground">({{ count }})</span>
</div>
</template>
```
### Map (Leaflet)
Interactive map for parks nearby feature.
**Features:**
- Marker clusters for dense areas
- Custom markers for different park types
- Popup on marker click with park preview
- Zoom controls
- Full-screen toggle
- User location marker (if permitted)
## Form Components
### Standard Form Structure
```vue
<template>
<form @submit.prevent="handleSubmit">
<div class="space-y-4">
<FormField label="Name" :error="errors.name">
<Input v-model="form.name" />
</FormField>
<FormField label="Description">
<Textarea v-model="form.description" />
</FormField>
<div class="flex gap-2 justify-end">
<Button variant="outline" @click="$emit('cancel')">Cancel</Button>
<Button type="submit" :loading="isSubmitting">Save</Button>
</div>
</div>
</form>
</template>
```
### Validation
- Use Zod for schema validation
- Display errors inline below fields
- Disable submit button while invalid
- Show loading state during submission
## Loading States
### Skeleton Loading
All cards should have skeleton states:
```vue
<template>
<Card v-if="loading">
<Skeleton class="aspect-video rounded-t-lg" />
<div class="p-4 space-y-2">
<Skeleton class="h-5 w-3/4" />
<Skeleton class="h-4 w-1/2" />
<Skeleton class="h-4 w-1/4" />
</div>
</Card>
<ActualCard v-else :data="data" />
</template>
```
### Empty States
Provide clear empty states with:
- Relevant icon
- Clear message
- Suggested action (button or link)
```vue
<EmptyState
icon="Search"
title="No results found"
description="Try adjusting your search or filters"
>
<Button @click="clearFilters">Clear Filters</Button>
</EmptyState>
```

View File

@@ -0,0 +1,191 @@
# ThrillWiki Design System Rules
Visual identity, colors, typography, and styling guidelines for ThrillWiki UI.
## Brand Identity
- **Name**: ThrillWiki (optional "(Beta)" suffix during preview)
- **Tagline**: The Ultimate Theme Park Database
- **Personality**: Enthusiastic, trustworthy, community-driven
- **Visual Style**: Modern, clean, subtle gradients, smooth animations, card-based layouts
## Color System
### Semantic Color Tokens (use these, not raw hex values)
| Token | Purpose | Usage |
|-------|---------|-------|
| `--background` | Page background | Main content area |
| `--foreground` | Primary text | Body text, headings |
| `--primary` | Interactive elements | Buttons, links |
| `--secondary` | Secondary UI | Secondary buttons |
| `--muted` | Subdued content | Hints, disabled states |
| `--accent` | Highlights | Focus rings, special callouts |
| `--destructive` | Danger actions | Delete buttons, errors |
| `--success` | Positive feedback | Success messages, confirmations |
| `--warning` | Caution | Alerts, warnings |
### Status Colors (Entity Badges)
- **Operating**: Green (`--success`)
- **Closed**: Red (`--destructive`)
- **Under Construction**: Amber (`--warning`)
### Dark Mode
- Automatically supported via CSS variables
- Reduce contrast (use off-white, not pure white)
- Replace shadows with subtle glows
- Slightly dim images
## Typography
### Font
- **Primary Font**: Inter (Sans-Serif)
- **Weights**: 400 (Regular), 500 (Medium), 600 (Semibold), 700 (Bold)
- **Fallback**: system-ui, sans-serif
### Type Scale
| Size | Name | Usage |
|------|------|-------|
| 48px | Display | Hero headlines |
| 36px | H1 | Page titles |
| 30px | H2 | Section headers |
| 24px | H3 | Card titles |
| 20px | H4 | Subheadings |
| 16px | Body | Default text |
| 14px | Small | Secondary text |
| 12px | Caption | Labels, hints |
### Text Classes (Tailwind)
```
Page Title: text-4xl font-bold
Section Header: text-2xl font-semibold
Card Title: text-lg font-medium
Body: text-base
Caption: text-sm text-muted-foreground
```
## Spacing System
### Base Unit
- 4px base unit
- All spacing is multiples of 4
### Spacing Scale
| Token | Size | Usage |
|-------|------|-------|
| `space-1` (p-1) | 4px | Tight gaps |
| `space-2` (p-2) | 8px | Icon gaps |
| `space-3` (p-3) | 12px | Small padding |
| `space-4` (p-4) | 16px | Default padding |
| `space-6` (p-6) | 24px | Section gaps |
| `space-8` (p-8) | 32px | Large gaps |
| `space-12` (p-12) | 48px | Section margins |
## Border Radius
| Token | Size | Usage |
|-------|------|-------|
| `rounded-sm` | 4px | Small elements |
| `rounded` | 8px | Buttons, inputs |
| `rounded-lg` | 12px | Cards |
| `rounded-xl` | 16px | Large cards, modals |
| `rounded-full` | 9999px | Pills, avatars |
## Layout
### Max Content Width
- Main content: `max-w-7xl` (1280px)
- Centered with `mx-auto`
- Responsive padding: `px-4 md:px-6 lg:px-8`
### Grid System
- Desktop (1024px+): 4 columns
- Tablet (768px+): 2 columns
- Mobile (<768px): 1 column
```html
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
```
## Component Patterns
### Cards
- Background: `bg-card`
- Border: `border border-border`
- Radius: `rounded-lg`
- Padding: `p-4` or `p-6`
- Interactive cards: Add `hover:shadow-md transition-shadow cursor-pointer`
### Buttons
| Variant | Classes |
|---------|---------|
| Primary | `bg-primary text-primary-foreground hover:bg-primary/90` |
| Secondary | `bg-secondary text-secondary-foreground hover:bg-secondary/80` |
| Outline | `border border-input bg-background hover:bg-accent` |
| Ghost | `hover:bg-accent hover:text-accent-foreground` |
| Destructive | `bg-destructive text-destructive-foreground hover:bg-destructive/90` |
### Inputs
- Border: `border border-input`
- Focus: `focus:ring-2 focus:ring-primary focus:border-transparent`
- Error: `border-destructive focus:ring-destructive`
### Badges
```html
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">
Operating
</span>
```
## Icons
- **Library**: Lucide icons (via `lucide-vue-next`)
- **Default Size**: 24px (w-6 h-6)
- **Stroke Width**: 1.5px
- **Color**: Inherit from text color
Common icons:
- Search, Menu, User, Heart, Star, MapPin, Calendar, Camera, Edit, Trash, Check, X, ChevronRight, ExternalLink
## Responsive Breakpoints
| Breakpoint | Width | Target |
|------------|-------|--------|
| `sm` | 640px | Large phones |
| `md` | 768px | Tablets |
| `lg` | 1024px | Small laptops |
| `xl` | 1280px | Desktops |
| `2xl` | 1536px | Large screens |
## Accessibility Requirements
- Color contrast: 4.5:1 minimum for normal text
- Focus states: Visible focus ring on all interactive elements
- Motion: Respect `prefers-reduced-motion`
- Screen readers: Proper ARIA labels on interactive elements
- Keyboard: All functionality accessible via keyboard
## ThrillWiki-Specific Patterns
### Unit Display
Always provide unit conversion toggle (metric/imperial):
```vue
<UnitDisplay :value="121" type="speed" />
<!-- Renders: "121 km/h" or "75 mph" based on user preference -->
```
### Rating Display
```vue
<RatingDisplay :rating="4.2" :count="156" />
<!-- Renders: 4.2 (156 reviews) -->
```
### Entity Cards
All entity cards (Park, Ride, Company) should show:
- Image (with loading skeleton)
- Name (primary text)
- Key details (secondary text)
- Status badge
- Quick stats (rating, count, etc.)

View File

@@ -0,0 +1,254 @@
# ThrillWiki Django Backend Standards
Rules for developing the ThrillWiki backend with Django and Django REST Framework.
## Project Structure
```
backend/
├── config/ # Project settings
│ ├── settings/
│ │ ├── base.py # Shared settings
│ │ ├── development.py # Dev-specific
│ │ └── production.py # Prod-specific
│ ├── urls.py # Root URL config
│ └── wsgi.py
├── apps/
│ ├── parks/ # Park-related models and APIs
│ ├── rides/ # Ride-related models and APIs
│ ├── companies/ # Manufacturers, operators, etc.
│ ├── users/ # User profiles, authentication
│ ├── reviews/ # User reviews and ratings
│ ├── credits/ # Ride credits tracking
│ ├── submissions/ # Content submission system
│ ├── moderation/ # Moderation queue
│ └── core/ # Shared utilities
└── tests/ # Test files
```
## Django App Structure
Each app should follow this structure:
```
app_name/
├── __init__.py
├── admin.py # Django admin configuration
├── apps.py # App configuration
├── models.py # Database models
├── serializers.py # DRF serializers
├── views.py # DRF viewsets/views
├── urls.py # URL routing
├── permissions.py # Custom permissions (if needed)
├── filters.py # DRF filters (if needed)
├── signals.py # Django signals (if needed)
└── tests/
├── __init__.py
├── test_models.py
├── test_views.py
└── test_serializers.py
```
## Model Conventions
### Base Model
All models should inherit from a base model with common fields:
```python
from django.db import models
import uuid
class BaseModel(models.Model):
"""Base model with common fields"""
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
```
### Model Example
```python
class Park(BaseModel):
"""A theme park or amusement park"""
class Status(models.TextChoices):
OPERATING = 'operating', 'Operating'
CLOSED = 'closed', 'Closed'
CONSTRUCTION = 'construction', 'Under Construction'
name = models.CharField(max_length=255)
slug = models.SlugField(unique=True)
description = models.TextField(blank=True)
status = models.CharField(
max_length=20,
choices=Status.choices,
default=Status.OPERATING
)
location = models.PointField() # GeoDjango
city = models.CharField(max_length=100)
country = models.CharField(max_length=100)
class Meta:
ordering = ['name']
def __str__(self):
return self.name
```
### Versioning for Moderated Content
For content that needs version history:
```python
class ParkVersion(BaseModel):
"""Version history for park edits"""
park = models.ForeignKey(Park, on_delete=models.CASCADE, related_name='versions')
data = models.JSONField() # Snapshot of park data
changed_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
change_summary = models.CharField(max_length=255)
is_current = models.BooleanField(default=False)
```
## API Conventions
### URL Structure
```python
# apps/parks/urls.py
from rest_framework.routers import DefaultRouter
from .views import ParkViewSet
router = DefaultRouter()
router.register('parks', ParkViewSet, basename='park')
urlpatterns = router.urls
# Results in:
# GET /api/parks/ - List parks
# POST /api/parks/ - Create park (submission)
# GET /api/parks/{slug}/ - Get park detail
# PUT /api/parks/{slug}/ - Update park (submission)
# DELETE /api/parks/{slug}/ - Delete park (admin only)
```
### ViewSet Structure
```python
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticatedOrReadOnly
class ParkViewSet(viewsets.ModelViewSet):
"""API endpoint for parks"""
queryset = Park.objects.all()
serializer_class = ParkSerializer
permission_classes = [IsAuthenticatedOrReadOnly]
lookup_field = 'slug'
filterset_class = ParkFilter
search_fields = ['name', 'city', 'country']
ordering_fields = ['name', 'created_at']
def get_queryset(self):
"""Optimize queries with select_related and prefetch_related"""
return Park.objects.select_related(
'operator', 'owner'
).prefetch_related(
'rides', 'photos'
)
@action(detail=True, methods=['get'])
def rides(self, request, slug=None):
"""Get rides for a specific park"""
park = self.get_object()
rides = park.rides.all()
serializer = RideSerializer(rides, many=True)
return Response(serializer.data)
```
### Serializer Patterns
```python
from rest_framework import serializers
class ParkSerializer(serializers.ModelSerializer):
"""Serializer for Park model"""
ride_count = serializers.IntegerField(read_only=True)
average_rating = serializers.FloatField(read_only=True)
class Meta:
model = Park
fields = [
'id', 'name', 'slug', 'description', 'status',
'city', 'country', 'ride_count', 'average_rating',
'created_at', 'updated_at'
]
read_only_fields = ['id', 'slug', 'created_at', 'updated_at']
class ParkDetailSerializer(ParkSerializer):
"""Extended serializer for park detail view"""
rides = RideSerializer(many=True, read_only=True)
photos = PhotoSerializer(many=True, read_only=True)
class Meta(ParkSerializer.Meta):
fields = ParkSerializer.Meta.fields + ['rides', 'photos']
```
## Query Optimization
- ALWAYS use `select_related()` for ForeignKey relationships
- ALWAYS use `prefetch_related()` for ManyToMany and reverse FK relationships
- Annotate computed fields at the database level when possible
- Use pagination for all list endpoints
```python
# Good
parks = Park.objects.select_related('operator').prefetch_related('rides')
# Bad - causes N+1 queries
for park in parks:
print(park.operator.name) # Each iteration hits the database
```
## Permissions
### Custom Permission Classes
```python
from rest_framework.permissions import BasePermission
class IsModeratorOrReadOnly(BasePermission):
"""Allow read access to all, write access to moderators"""
def has_permission(self, request, view):
if request.method in ['GET', 'HEAD', 'OPTIONS']:
return True
return request.user.is_authenticated and request.user.is_moderator
```
## Submission & Moderation Flow
All user-submitted content goes through moderation:
1. User submits content → Creates `Submission` record with status `pending`
2. Moderator reviews → Approves or rejects
3. On approval → Content is published, version record created
```python
class Submission(BaseModel):
class Status(models.TextChoices):
PENDING = 'pending', 'Pending Review'
APPROVED = 'approved', 'Approved'
REJECTED = 'rejected', 'Rejected'
CHANGES_REQUESTED = 'changes_requested', 'Changes Requested'
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.UUIDField(null=True, blank=True)
data = models.JSONField()
status = models.CharField(max_length=20, choices=Status.choices, default=Status.PENDING)
submitted_by = models.ForeignKey(User, on_delete=models.CASCADE)
reviewed_by = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
review_notes = models.TextField(blank=True)
```
## Testing Requirements
- Write tests for all models, views, and serializers
- Use pytest and pytest-django
- Use factories (factory_boy) for test data
- Test permissions thoroughly
- Test edge cases and error conditions

View File

@@ -0,0 +1,204 @@
# ThrillWiki Nuxt 4 Frontend Standards
Rules for developing the ThrillWiki frontend with Nuxt 4.
## Project Structure
```
frontend/
├── app.vue # Root component
├── nuxt.config.ts # Nuxt configuration
├── pages/ # File-based routing
│ ├── index.vue # Homepage (/)
│ ├── parks/
│ │ ├── index.vue # /parks
│ │ ├── nearby.vue # /parks/nearby
│ │ └── [slug].vue # /parks/:slug
│ └── ...
├── components/
│ ├── layout/ # Header, Footer, Sidebar
│ ├── ui/ # Base components (Button, Card, Input)
│ ├── entity/ # ParkCard, RideCard, ReviewCard
│ └── forms/ # Form components
├── composables/ # Shared logic (useAuth, useApi, useUnits)
├── stores/ # Pinia stores
├── types/ # TypeScript interfaces
└── assets/
└── css/ # Global styles, Tailwind config
```
## Component Conventions
### Naming
- Use PascalCase for component files: `ParkCard.vue`, `SearchAutocomplete.vue`
- Use kebab-case in templates: `<park-card>`, `<search-autocomplete>`
- Prefix base components with `Base`: `BaseButton.vue`, `BaseInput.vue`
### Component Structure
```vue
<script setup lang="ts">
// 1. Type imports
import type { Park } from '~/types'
// 2. Component imports (auto-imported usually)
// 3. Props and emits
const props = defineProps<{
park: Park
variant?: 'default' | 'compact'
}>()
const emit = defineEmits<{
(e: 'select', park: Park): void
}>()
// 4. Composables
const { formatDistance } = useUnits()
// 5. Refs and reactive state
const isExpanded = ref(false)
// 6. Computed properties
const displayLocation = computed(() =>
`${props.park.city}, ${props.park.country}`
)
// 7. Functions
function handleClick() {
emit('select', props.park)
}
// 8. Lifecycle hooks (if needed)
</script>
<template>
<!-- Template here -->
</template>
<style scoped>
/* Scoped styles, prefer Tailwind classes in template */
</style>
```
### TypeScript
- Enable strict mode
- Define interfaces for all data structures in `types/`
- Use `defineProps<T>()` with TypeScript generics
- No `any` types without explicit justification
## Routing
### File-Based Routes
Follow Nuxt 4 file-based routing conventions:
- `pages/index.vue``/`
- `pages/parks/index.vue``/parks`
- `pages/parks/[slug].vue``/parks/:slug`
- `pages/parks/[park]/rides/[ride].vue``/parks/:park/rides/:ride`
### Navigation
```typescript
// Use navigateTo for programmatic navigation
await navigateTo('/parks/cedar-point')
// Use NuxtLink for declarative navigation
<NuxtLink to="/parks">All Parks</NuxtLink>
```
## Data Fetching
### Use Composables
```typescript
// composables/useParks.ts
export function useParks() {
const { data, pending, error, refresh } = useFetch('/api/parks/')
return {
parks: data,
loading: pending,
error,
refresh
}
}
```
### In Components
```typescript
// Use useAsyncData for page-level data
const { data: park } = await useAsyncData(
`park-${route.params.slug}`,
() => $fetch(`/api/parks/${route.params.slug}/`)
)
```
## State Management (Pinia)
### Store Structure
```typescript
// stores/auth.ts
export const useAuthStore = defineStore('auth', () => {
const user = ref<User | null>(null)
const isAuthenticated = computed(() => !!user.value)
async function login(credentials: LoginCredentials) {
// Implementation
}
function logout() {
user.value = null
}
return { user, isAuthenticated, login, logout }
})
```
### Using Stores
```typescript
const authStore = useAuthStore()
const { user, isAuthenticated } = storeToRefs(authStore)
```
## API Integration
### Base API Composable
```typescript
// composables/useApi.ts
export function useApi() {
const config = useRuntimeConfig()
const authStore = useAuthStore()
return $fetch.create({
baseURL: config.public.apiBase,
headers: {
...(authStore.token && { Authorization: `Bearer ${authStore.token}` })
}
})
}
```
## Error Handling
### Page Errors
```typescript
// In page components
const { data, error } = await useAsyncData(...)
if (error.value) {
throw createError({
statusCode: error.value.statusCode || 500,
message: error.value.message
})
}
```
### Form Errors
- Display validation errors inline with form fields
- Use toast notifications for API errors
- Provide clear user feedback
## Accessibility Requirements
- All interactive elements must be keyboard accessible
- Provide proper ARIA labels
- Ensure color contrast meets WCAG AA standards
- Support `prefers-reduced-motion`
- Use semantic HTML elements

View File

@@ -0,0 +1,85 @@
---
description: Ensure compliance with source_docs specifications - Continuous guard
---
# Source Docs Compliance Workflow
You are now in **Compliance Guard Mode**. The documents in `source_docs/` are LAW. Every code change must comply with these specifications.
## The Constitution
The following documents are the single source of truth:
- `source_docs/SITE_OVERVIEW.md` - High-level product vision
- `source_docs/DESIGN_SYSTEM.md` - Visual identity, colors, typography, gradients
- `source_docs/COMPONENTS.md` - Component specifications and patterns
- `source_docs/PAGES.md` - Page layouts and content
- `source_docs/USER_FLOWS.md` - User journeys and interaction specifications
## Before Making ANY Code Change
1. **Identify Relevant Specs**: Determine which source_docs apply to the change
2. **Read the Spec**: View the relevant sections to understand requirements
3. **Check for Deviations**: Compare current/proposed code against the spec
4. **Cite Your Sources**: Reference specific line numbers when claiming compliance
## Compliance Checklist
### For Components (check against COMPONENTS.md, DESIGN_SYSTEM.md):
- [ ] Uses correct component structure (header, content, footer)
- [ ] Uses primary color palette (blue for primary, NOT emerald/teal unless specified)
- [ ] Uses centralized constants instead of hardcoded options
- [ ] Follows established patterns (sticky footer, tab navigation, etc.)
- [ ] Proper button variants per spec
- [ ] Dark mode support
### For User Flows (check against USER_FLOWS.md):
- [ ] All required fields present (e.g., submission notes for edits)
- [ ] Proper validation implemented
- [ ] Error states handled per spec
- [ ] Success feedback matches spec
### For Forms:
- [ ] Options imported from `~/utils/constants.ts`
- [ ] Backend-synced choices (check `backend/apps/*/choices.py`)
- [ ] Required fields marked and validated
- [ ] Proper help text and hints
## When Deviation is Found
1. **STOP** - Do not proceed with the current approach
2. **LOG** - Document the deviation with:
- Requirement (from source_docs)
- Current implementation
- Proposed fix
3. **FIX** - Implement the compliant solution
4. **VERIFY** - Ensure the fix matches the spec
## Status Tags
Use these in any audit or review:
- `[OK]` - Compliant with spec
- `[DEVIATION]` - Implemented differently than spec
- `[MISSING]` - Required feature not implemented
- `[RISK]` - Potential issue that needs investigation
## Key Constants Files
Always use these instead of hardcoding:
- `frontend/app/utils/constants.ts` - Status configs, park types, etc.
- `backend/apps/parks/choices.py` - Park status and type definitions
- `backend/apps/rides/choices.py` - Ride status and category definitions
- `backend/apps/moderation/choices.py` - Submission status definitions
## Example Audit Output
```markdown
## Compliance Audit: ComponentName.vue
### Checking Against: [List relevant docs]
| Requirement | Source | Status |
|-------------|--------|--------|
| Uses primary gradient | DESIGN_SYSTEM.md L91-98 | [OK] |
| Submission note field | USER_FLOWS.md L473-476 | [MISSING] → FIX |
| Sticky footer pattern | COMPONENTS.md L583-602 | [DEVIATION] → FIX |
```

View File

@@ -0,0 +1,168 @@
---
description: Migrate a React component from thrillwiki-87 to Vue/Nuxt
---
# Migrate Component Workflow
Convert a React component from thrillwiki-87 (the authoritative source) to Vue 3 Composition API for the Nuxt 4 project.
## Step 1: Locate Source Component
Find the React component in thrillwiki-87:
```bash
# React components are in:
/Volumes/macminissd/Projects/thrillwiki-87/src/components/
```
Common component directories:
- `auth/` - Authentication components
- `parks/` - Park-related components
- `rides/` - Ride-related components
- `forms/` - Form components
- `moderation/` - Moderation queue components
- `ui/` - Base UI primitives (shadcn-ui)
- `common/` - Shared utilities
- `layout/` - Layout components
## Step 2: Analyze React Component
Extract these patterns from the source:
```tsx
// Props interface
interface ComponentProps {
prop1: string
prop2?: number
}
// State hooks
const [value, setValue] = useState<Type>(initial)
// Effects
useEffect(() => { /* logic */ }, [deps])
// Event handlers
const handleClick = () => { /* logic */ }
// Render return
return <JSX />
```
## Step 3: Translate to Vue 3
### Props
```tsx
// React
interface Props { name: string; count?: number }
```
```vue
<!-- Vue -->
<script setup lang="ts">
defineProps<{
name: string
count?: number
}>()
</script>
```
### State
```tsx
// React
const [value, setValue] = useState<string>('')
```
```vue
<!-- Vue -->
<script setup lang="ts">
const value = ref<string>('')
</script>
```
### Effects
```tsx
// React
useEffect(() => {
fetchData()
}, [id])
```
```vue
<!-- Vue -->
<script setup lang="ts">
watch(() => id, () => {
fetchData()
}, { immediate: true })
</script>
```
### Events
```tsx
// React
onClick={() => onSelect(item)}
```
```vue
<!-- Vue -->
@click="emit('select', item)"
```
## Step 4: Map UI Components
Translate shadcn-ui to Nuxt UI:
| shadcn-ui | Nuxt UI |
|-----------|---------|
| `<Button>` | `<UButton>` |
| `<Card>` | `<UCard>` |
| `<Dialog>` | `<UModal>` |
| `<Input>` | `<UInput>` |
| `<Select>` | `<USelect>` |
| `<Tabs>` | `<UTabs>` |
| `<Badge>` | `<UBadge>` |
## Step 5: Update API Calls
Replace Supabase with Django API:
```tsx
// React (Supabase)
const { data } = await supabase.from('parks').select('*')
```
```vue
<!-- Vue (Django) -->
<script setup lang="ts">
const { data } = await useApi<Park[]>('/parks/')
</script>
```
## Step 6: Place in Nuxt Structure
Target paths:
```
frontend/app/components/
├── auth/ # Auth components
├── cards/ # Card components
├── common/ # Shared utilities
├── modals/ # Modal dialogs
├── rides/ # Ride components
└── ui/ # Base UI components
```
## Step 7: Verify Parity
- [ ] All props from source are present
- [ ] All state variables ported
- [ ] All event handlers work
- [ ] Styling matches (Tailwind classes)
- [ ] Loading states present
- [ ] Error states handled
- [ ] Dark mode works
## Example Migration
**Source**: `thrillwiki-87/src/components/parks/ParkCard.tsx`
**Target**: `frontend/app/components/cards/ParkCard.vue`
Check existing target. If it exists, compare and add missing features. If not, create new.

View File

@@ -0,0 +1,223 @@
---
description: Convert a React hook from thrillwiki-87 to a Vue composable
---
# Migrate Hook Workflow
Convert a React hook from thrillwiki-87 to a Vue 3 composable for the Nuxt 4 project.
## Step 1: Locate Source Hook
Find the React hook in thrillwiki-87:
```bash
/Volumes/macminissd/Projects/thrillwiki-87/src/hooks/
```
Key hooks (80+ total):
- `useAuth.tsx` - Authentication state
- `useModerationQueue.ts` - Moderation logic (21KB)
- `useEntityVersions.ts` - Version history (14KB)
- `useSearch.tsx` - Search functionality
- `useUnitPreferences.ts` - Unit conversion
- `useProfile.tsx` - User profile
- `useLocations.ts` - Location data
- `useRideCreditFilters.ts` - Credit filtering
## Step 2: Analyze Hook Pattern
Extract the hook structure:
```tsx
export function useFeature(params: Params) {
// State
const [data, setData] = useState<Type>(null)
const [loading, setLoading] = useState(false)
const [error, setError] = useState<Error | null>(null)
// Effects
useEffect(() => {
fetchData()
}, [dependency])
// Actions
const doSomething = async () => {
setLoading(true)
try {
const result = await api.call()
setData(result)
} catch (e) {
setError(e)
} finally {
setLoading(false)
}
}
return { data, loading, error, doSomething }
}
```
## Step 3: Convert to Composable
### Basic Structure
```tsx
// React
export function useFeature() {
const [value, setValue] = useState('')
return { value, setValue }
}
```
```typescript
// Vue
export function useFeature() {
const value = ref('')
function setValue(newValue: string) {
value.value = newValue
}
return { value, setValue }
}
```
### State Conversions
```tsx
// React
const [count, setCount] = useState(0)
const [user, setUser] = useState<User | null>(null)
const [items, setItems] = useState<Item[]>([])
```
```typescript
// Vue
const count = ref(0)
const user = ref<User | null>(null)
const items = ref<Item[]>([])
```
### Effect Conversions
```tsx
// React - Run on mount
useEffect(() => {
initialize()
}, [])
```
```typescript
// Vue
onMounted(() => {
initialize()
})
```
```tsx
// React - Watch dependency
useEffect(() => {
fetchData(id)
}, [id])
```
```typescript
// Vue
watch(() => id, (newId) => {
fetchData(newId)
}, { immediate: true })
```
### Supabase → Django API
```tsx
// React (Supabase)
const { data } = await supabase
.from('parks')
.select('*')
.eq('slug', slug)
.single()
```
```typescript
// Vue (Django)
const api = useApi()
const { data } = await api<Park>(`/parks/${slug}/`)
```
## Step 4: Handle Complex Patterns
### useCallback → Plain Function
```tsx
// React
const memoizedFn = useCallback(() => {
doSomething(dep)
}, [dep])
```
```typescript
// Vue - Usually no memo needed
function doSomething() {
// Vue's reactivity handles this
}
```
### useMemo → computed
```tsx
// React
const derived = useMemo(() => expensiveCalc(data), [data])
```
```typescript
// Vue
const derived = computed(() => expensiveCalc(data.value))
```
### Custom Hook Composition
```tsx
// React
function useFeature() {
const auth = useAuth()
const { data } = useQuery(...)
// ...
}
```
```typescript
// Vue
export function useFeature() {
const { user } = useAuth()
const api = useApi()
// ...
}
```
## Step 5: Target Location
Place composables in:
```
frontend/app/composables/
├── useApi.ts # Base API client
├── useAuth.ts # Authentication
├── useParksApi.ts # Parks API
├── useRidesApi.ts # Rides API
├── useModeration.ts # Moderation queue
└── use[Feature].ts # New composables
```
## Step 6: Verify Parity
- [ ] All returned values present
- [ ] All actions/methods work
- [ ] State updates correctly
- [ ] API calls translated
- [ ] Error handling maintained
- [ ] Loading states work
- [ ] TypeScript types correct
## Priority Hooks to Migrate
| Hook | Size | Complexity |
|------|------|------------|
| useModerationQueue.ts | 21KB | High |
| useEntityVersions.ts | 14KB | High |
| useAuth.tsx | 11KB | Medium |
| useAutoComplete.ts | 10KB | Medium |
| useRateLimitAlerts.ts | 10KB | Medium |
| useRideCreditFilters.ts | 9KB | Medium |
| useAdminSettings.ts | 9KB | Medium |

View File

@@ -0,0 +1,183 @@
---
description: Migrate a React page from thrillwiki-87 to Nuxt 4
---
# Migrate Page Workflow
Port a React page from thrillwiki-87 (the authoritative source) to a Nuxt 4 page.
## Step 1: Locate Source Page
Find the React page in thrillwiki-87:
```bash
/Volumes/macminissd/Projects/thrillwiki-87/src/pages/
```
Key pages:
- `Index.tsx` - Homepage
- `Parks.tsx` - Parks listing
- `ParkDetail.tsx` - Individual park (36KB - complex)
- `Rides.tsx` - Rides listing
- `RideDetail.tsx` - Individual ride (54KB - most complex)
- `Profile.tsx` - User profile (51KB - complex)
- `Search.tsx` - Global search
- `AdminDashboard.tsx` - Admin panel
## Step 2: Analyze Page Structure
Extract from React page:
```tsx
// Route params
const { id } = useParams()
// Data fetching
const { data, isLoading, error } = useQuery(...)
// SEO
// (usually react-helmet or similar)
// Page layout structure
return (
<Layout>
<Tabs>...</Tabs>
<Content>...</Content>
</Layout>
)
```
## Step 3: Create Nuxt Page
### Route Params
```tsx
// React
const { parkSlug } = useParams()
```
```vue
<!-- Nuxt -->
<script setup lang="ts">
const route = useRoute()
const parkSlug = route.params.park_slug as string
</script>
```
### Data Fetching
```tsx
// React (React Query)
const { data, isLoading } = useQuery(['park', id], () => fetchPark(id))
```
```vue
<!-- Nuxt -->
<script setup lang="ts">
const { data, pending } = await useAsyncData(
`park-${parkSlug}`,
() => useParksApi().getPark(parkSlug)
)
</script>
```
### SEO Meta
```tsx
// React (Helmet)
<Helmet>
<title>{park.name} | ThrillWiki</title>
</Helmet>
```
```vue
<!-- Nuxt -->
<script setup lang="ts">
useSeoMeta({
title: () => `${data.value?.name} | ThrillWiki`,
description: () => data.value?.description
})
</script>
```
### Page Meta
```vue
<script setup lang="ts">
definePageMeta({
middleware: ['auth'], // if authentication required
layout: 'default'
})
</script>
```
## Step 4: Port Page Sections
Common patterns:
### Tabs Structure
```tsx
// React
<Tabs defaultValue="overview">
<TabsList>
<TabsTrigger value="overview">Overview</TabsTrigger>
</TabsList>
<TabsContent value="overview">...</TabsContent>
</Tabs>
```
```vue
<!-- Nuxt -->
<UTabs :items="tabs" v-model="activeTab">
<template #default="{ item }">
<component :is="item.component" v-bind="item.props" />
</template>
</UTabs>
```
### Loading States
```tsx
// React
{isLoading ? <Skeleton /> : <Content />}
```
```vue
<!-- Nuxt -->
<template>
<div v-if="pending">
<USkeleton class="h-48" />
</div>
<div v-else-if="data">
<!-- Content -->
</div>
</template>
```
## Step 5: Target Location
Nuxt pages use file-based routing:
| React Route | Nuxt File Path |
|-------------|----------------|
| `/parks` | `pages/parks/index.vue` |
| `/parks/:slug` | `pages/parks/[park_slug]/index.vue` |
| `/parks/:slug/rides/:ride` | `pages/parks/[park_slug]/rides/[ride_slug].vue` |
| `/manufacturers/:id` | `pages/manufacturers/[slug].vue` |
## Step 6: Verify Feature Parity
Compare source page with target:
- [ ] All tabs/sections present
- [ ] All data displayed
- [ ] All actions work (edit, delete, etc.)
- [ ] Responsive layout matches
- [ ] Loading states present
- [ ] Error handling works
- [ ] SEO meta correct
## Reference: Page Complexity
| Page | Source Size | Priority |
|------|-------------|----------|
| RideDetail.tsx | 54KB | High |
| Profile.tsx | 51KB | High |
| AdminSettings.tsx | 44KB | Medium |
| ParkDetail.tsx | 36KB | High |
| Auth.tsx | 29KB | Medium |
| Parks.tsx | 22KB | High |
| Rides.tsx | 20KB | High |

View File

@@ -0,0 +1,201 @@
---
description: Port TypeScript types from thrillwiki-87 to the Nuxt project
---
# Migrate Type Workflow
Port TypeScript type definitions from thrillwiki-87 to the Nuxt 4 project, ensuring sync with Django serializers.
## Step 1: Locate Source Types
Find types in thrillwiki-87:
```bash
/Volumes/macminissd/Projects/thrillwiki-87/src/types/
```
Key type files (63 total):
- `database.ts` - Core entity types (12KB)
- `statuses.ts` - Status enums (13KB)
- `moderation.ts` - Moderation types (10KB)
- `rideAttributes.ts` - Ride specs (8KB)
- `versioning.ts` - Version history (7KB)
- `submissions.ts` - Submission types
- `company.ts` - Company entities
## Step 2: Analyze Source Types
Extract type structure:
```typescript
// thrillwiki-87/src/types/database.ts
export interface Park {
id: string
name: string
slug: string
park_type: ParkType
status: ParkStatus
opening_date?: string
closing_date?: string
location?: ParkLocation
// ...
}
export interface ParkLocation {
latitude: number
longitude: number
country: string
city?: string
// ...
}
```
## Step 3: Check Django Serializers
Before porting, verify Django backend has matching fields:
```bash
# Check serializers in:
backend/apps/parks/serializers.py
backend/apps/rides/serializers.py
backend/apps/companies/serializers.py
```
The Django serializer defines what the API returns:
```python
class ParkSerializer(serializers.ModelSerializer):
class Meta:
model = Park
fields = ['id', 'name', 'slug', 'park_type', 'status', ...]
```
## Step 4: Handle Naming Conventions
Django uses snake_case, TypeScript typically uses camelCase. Choose one strategy:
### Option A: Match Django exactly (Recommended)
```typescript
// Keep snake_case from API
export interface Park {
id: string
name: string
park_type: string
created_at: string
}
```
### Option B: Transform in API layer
```typescript
// camelCase in types
export interface Park {
id: string
name: string
parkType: string
createdAt: string
}
// Transform in useApi or serializer
```
**Current project uses Option A (snake_case).**
## Step 5: Port the Types
Create/update type files:
```typescript
// frontend/app/types/park.ts
export interface Park {
id: string
name: string
slug: string
park_type: string
status: string
short_description?: string
description?: string
opening_date?: string
closing_date?: string
operating_season?: string
website?: string
latitude?: number
longitude?: number
// Match Django serializer fields exactly
}
export interface ParkDetail extends Park {
rides?: Ride[]
reviews?: Review[]
photos?: Photo[]
// Extended fields from detail serializer
}
```
## Step 6: Port Enums/Options
Source may have enums that should be constants:
```typescript
// thrillwiki-87/src/types/statuses.ts
export const PARK_STATUS = {
OPERATING: 'operating',
CLOSED: 'closed',
// ...
} as const
```
```typescript
// frontend/app/utils/constants.ts
export const PARK_STATUS_OPTIONS = [
{ value: 'operating', label: 'Operating', color: 'green' },
{ value: 'closed', label: 'Closed', color: 'red' },
// Sync with backend/apps/parks/choices.py
]
```
## Step 7: Target Locations
```
frontend/app/types/
├── index.ts # Re-exports and common types
├── park.ts # Park types
├── ride.ts # Ride types
├── company.ts # Company types (manufacturer, designer, etc.)
├── user.ts # User/profile types
├── moderation.ts # Moderation types
└── api.ts # API response wrappers
```
## Step 8: Verify Type Sync
| Layer | File | Must Match |
|-------|------|------------|
| Django Model | `backend/apps/*/models.py` | Database schema |
| Serializer | `backend/apps/*/serializers.py` | API response |
| Frontend Type | `frontend/app/types/*.ts` | Serializer fields |
Run checks:
```bash
# Check Django fields
python manage.py shell -c "from apps.parks.models import Park; print([f.name for f in Park._meta.fields])"
# Check serializer fields
python manage.py shell -c "from apps.parks.serializers import ParkSerializer; print(ParkSerializer().fields.keys())"
```
## Step 9: Checklist
- [ ] All source type fields ported
- [ ] Fields match Django serializer
- [ ] snake_case naming used
- [ ] Optional fields marked with `?`
- [ ] Enums/options in constants.ts
- [ ] Backend choices.py synced
- [ ] Types exported from index.ts
## Priority Types
| Type File | Source Size | Notes |
|-----------|-------------|-------|
| statuses.ts | 13KB | All status enums |
| database.ts | 12KB | Core entities |
| moderation.ts | 10KB | Queue types |
| rideAttributes.ts | 8KB | Spec options |
| versioning.ts | 7KB | History types |

193
.agent/workflows/migrate.md Normal file
View File

@@ -0,0 +1,193 @@
---
description: Audit gaps and implement missing features from thrillwiki-87 source
---
# Migration Workflow
**thrillwiki-87 is LAW.** This workflow audits what's missing and implements it.
## Quick Start
Run `/migrate` to:
1. Audit current project against thrillwiki-87
2. Identify the highest-priority missing feature
3. Implement it using the appropriate sub-workflow
---
## Phase 1: Gap Analysis
### Step 1.1: Audit Components
Compare React components with Vue components:
```bash
# Source (LAW):
/Volumes/macminissd/Projects/thrillwiki-87/src/components/
# Target:
/Volumes/macminissd/Projects/thrillwiki_django_no_react/frontend/app/components/
```
For each React component directory, check if equivalent Vue component exists:
- **EXISTS**: Mark as ✅, check for feature parity
- **MISSING**: Mark as ❌, add to implementation queue
### Step 1.2: Audit Pages
Compare React pages with Nuxt pages:
```bash
# Source (LAW):
/Volumes/macminissd/Projects/thrillwiki-87/src/pages/
# Target:
/Volumes/macminissd/Projects/thrillwiki_django_no_react/frontend/app/pages/
```
Key pages to audit (by size/complexity):
| React Page | Size | Priority |
|------------|------|----------|
| RideDetail.tsx | 54KB | P0 |
| Profile.tsx | 51KB | P0 |
| AdminSettings.tsx | 44KB | P1 |
| ParkDetail.tsx | 36KB | P0 |
| Auth.tsx | 29KB | P1 |
| Parks.tsx | 22KB | P0 |
| Rides.tsx | 20KB | P0 |
### Step 1.3: Audit Hooks → Composables
Compare React hooks with Vue composables:
```bash
# Source (LAW):
/Volumes/macminissd/Projects/thrillwiki-87/src/hooks/
# Target:
/Volumes/macminissd/Projects/thrillwiki_django_no_react/frontend/app/composables/
```
Priority hooks:
| React Hook | Size | Current Status |
|------------|------|----------------|
| useModerationQueue.ts | 21KB | Check useModeration.ts |
| useEntityVersions.ts | 14KB | ❌ Missing |
| useAuth.tsx | 11KB | Check useAuth.ts |
| useRideCreditFilters.ts | 9KB | ❌ Missing |
### Step 1.4: Audit Types
Compare TypeScript definitions:
```bash
# Source (LAW):
/Volumes/macminissd/Projects/thrillwiki-87/src/types/
# Target:
/Volumes/macminissd/Projects/thrillwiki_django_no_react/frontend/app/types/
```
---
## Phase 2: Priority Selection
After auditing, select the highest priority gap:
### Priority Matrix
| Category | Weight | Examples |
|----------|--------|----------|
| **P0 - Core UX** | 10 | Main entity pages, search, auth |
| **P1 - Features** | 7 | Reviews, credits, lists |
| **P2 - Admin** | 5 | Moderation, settings |
| **P3 - Polish** | 3 | Animations, edge cases |
Select ONE item to implement this session.
---
## Phase 3: Implementation
Based on the gap type, use the appropriate sub-workflow:
### For Missing Component
```
/migrate-component
```
### For Missing Page
```
/migrate-page
```
### For Missing Hook/Composable
```
/migrate-hook
```
### For Missing Types
```
/migrate-type
```
---
## Phase 4: Verification
After implementation:
1. **Feature Parity Check**: Does it match thrillwiki-87 behavior?
2. **Visual Parity Check**: Does it look the same?
3. **Data Parity Check**: Does it show the same information?
4. **Interaction Parity Check**: Do actions work the same way?
---
## Gap Tracking
Update `GAP_ANALYSIS_MATRIX.md` with status:
```markdown
| Feature | Source Location | Target Location | Status |
|---------|-----------------|-----------------|--------|
| Park Detail Tabs | src/pages/ParkDetail.tsx | pages/parks/[park_slug]/ | [OK] |
| Entity Versioning | src/hooks/useEntityVersions.ts | composables/ | [MISSING] |
| Ride Credits | src/components/credits/ | components/credits/ | [PARTIAL] |
```
Status tags:
- `[OK]` - Feature parity achieved
- `[PARTIAL]` - Some features missing
- `[MISSING]` - Not implemented
- `[BLOCKED]` - Waiting on backend
---
## Reference: Source Documentation
Always check thrillwiki-87 docs for specifications:
```bash
/Volumes/macminissd/Projects/thrillwiki-87/docs/
├── SITE_OVERVIEW.md # What features exist
├── COMPONENTS.md # Component specifications
├── PAGES.md # Page layouts (72KB - comprehensive)
├── USER_FLOWS.md # Interaction patterns (77KB)
├── DESIGN_SYSTEM.md # Visual standards
└── [Many more...]
```
---
## Single Command Execution
When you run `/migrate`, execute these steps:
1. **Read GAP_ANALYSIS_MATRIX.md** to see current status
2. **List directories** in both projects to find new gaps
3. **Select highest priority** missing item
4. **Read source implementation** from thrillwiki-87
5. **Implement in target** following sub-workflow patterns
6. **Update GAP_ANALYSIS_MATRIX.md** with new status
7. **Report what was implemented** and next priority item

View File

@@ -0,0 +1,472 @@
---
description: Add moderation support to a content type in ThrillWiki
---
# Moderation Workflow
Add moderation (submission queue, version history, approval flow) to a content type.
## Overview
ThrillWiki's moderation system ensures quality by:
1. User submits new/edited content → Creates `Submission` record
2. Content enters moderation queue with `pending` status
3. Moderator reviews and approves/rejects
4. On approval → Content is published, version record created
## Implementation Steps
### Step 1: Ensure Model Supports Versioning
The content model needs to track its current state and history:
```python
# backend/apps/[app]/models.py
class Park(BaseModel):
"""Main park model - always shows current approved data"""
name = models.CharField(max_length=255)
slug = models.SlugField(unique=True)
description = models.TextField(blank=True)
# ... other fields
# Track the current approved version
current_version = models.ForeignKey(
'ParkVersion',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='current_for'
)
class ParkVersion(BaseModel):
"""Immutable snapshot of park data at a point in time"""
park = models.ForeignKey(
Park,
on_delete=models.CASCADE,
related_name='versions'
)
# Store complete snapshot of editable fields
data = models.JSONField()
# Metadata
changed_by = models.ForeignKey(
'users.User',
on_delete=models.SET_NULL,
null=True
)
change_summary = models.CharField(max_length=255, blank=True)
submission = models.ForeignKey(
'submissions.Submission',
on_delete=models.SET_NULL,
null=True,
related_name='versions'
)
class Meta:
ordering = ['-created_at']
def apply_to_park(self):
"""Apply this version's data to the parent park"""
for field, value in self.data.items():
if hasattr(self.park, field):
setattr(self.park, field, value)
self.park.current_version = self
self.park.save()
```
### Step 2: Create Submission Serializers
```python
# backend/apps/submissions/serializers.py
class ParkSubmissionSerializer(serializers.Serializer):
"""Serializer for park submission data"""
name = serializers.CharField(max_length=255)
description = serializers.CharField(required=False, allow_blank=True)
city = serializers.CharField(max_length=100)
country = serializers.CharField(max_length=100)
status = serializers.ChoiceField(choices=Park.Status.choices)
# ... other editable fields
def validate_name(self, value):
# Custom validation if needed
return value
class SubmissionCreateSerializer(serializers.ModelSerializer):
"""Create a new submission"""
data = serializers.JSONField()
class Meta:
model = Submission
fields = ['content_type', 'object_id', 'data', 'change_summary']
def validate(self, attrs):
content_type = attrs['content_type']
# Get the appropriate serializer for this content type
serializer_map = {
'park': ParkSubmissionSerializer,
'ride': RideSubmissionSerializer,
# ... other content types
}
serializer_class = serializer_map.get(content_type)
if not serializer_class:
raise serializers.ValidationError(
{'content_type': 'Unsupported content type'}
)
# Validate the data field
data_serializer = serializer_class(data=attrs['data'])
data_serializer.is_valid(raise_exception=True)
attrs['data'] = data_serializer.validated_data
return attrs
def create(self, validated_data):
validated_data['submitted_by'] = self.context['request'].user
validated_data['status'] = Submission.Status.PENDING
return super().create(validated_data)
```
### Step 3: Create Submission ViewSet
```python
# backend/apps/submissions/views.py
class SubmissionViewSet(viewsets.ModelViewSet):
"""API for content submissions"""
serializer_class = SubmissionSerializer
permission_classes = [IsAuthenticated]
def get_queryset(self):
user = self.request.user
# Users see their own submissions
# Moderators see all pending submissions
if user.is_moderator:
return Submission.objects.all()
return Submission.objects.filter(submitted_by=user)
def get_serializer_class(self):
if self.action == 'create':
return SubmissionCreateSerializer
return SubmissionSerializer
@action(detail=True, methods=['get'])
def diff(self, request, pk=None):
"""Get diff between submission and current version"""
submission = self.get_object()
if submission.object_id:
# Edit submission - compare to current
current = self.get_current_data(submission)
return Response({
'before': current,
'after': submission.data,
'changes': self.compute_diff(current, submission.data)
})
else:
# New submission - no comparison
return Response({
'before': None,
'after': submission.data,
'changes': None
})
```
### Step 4: Create Moderation ViewSet
```python
# backend/apps/moderation/views.py
class ModerationViewSet(viewsets.ViewSet):
"""Moderation queue and actions"""
permission_classes = [IsModerator]
def list(self, request):
"""Get moderation queue"""
queryset = Submission.objects.filter(
status=Submission.Status.PENDING
).select_related(
'submitted_by'
).order_by('created_at')
# Filter by content type
content_type = request.query_params.get('type')
if content_type:
queryset = queryset.filter(content_type=content_type)
serializer = SubmissionSerializer(queryset, many=True)
return Response(serializer.data)
@action(detail=True, methods=['post'])
def approve(self, request, pk=None):
"""Approve a submission"""
submission = get_object_or_404(Submission, pk=pk)
if submission.status != Submission.Status.PENDING:
return Response(
{'error': 'Submission is not pending'},
status=status.HTTP_400_BAD_REQUEST
)
# Apply the submission
with transaction.atomic():
if submission.object_id:
# Edit existing content
content = self.get_content_object(submission)
version = self.create_version(content, submission)
version.apply_to_park()
else:
# Create new content
content = self.create_content(submission)
version = self.create_version(content, submission)
content.current_version = version
content.save()
submission.status = Submission.Status.APPROVED
submission.reviewed_by = request.user
submission.reviewed_at = timezone.now()
submission.save()
# Notify user
notify_user(
submission.submitted_by,
'submission_approved',
{'submission': submission}
)
return Response({'status': 'approved'})
@action(detail=True, methods=['post'])
def reject(self, request, pk=None):
"""Reject a submission"""
submission = get_object_or_404(Submission, pk=pk)
submission.status = Submission.Status.REJECTED
submission.reviewed_by = request.user
submission.reviewed_at = timezone.now()
submission.review_notes = request.data.get('notes', '')
submission.save()
# Notify user
notify_user(
submission.submitted_by,
'submission_rejected',
{'submission': submission, 'reason': submission.review_notes}
)
return Response({'status': 'rejected'})
@action(detail=True, methods=['post'])
def request_changes(self, request, pk=None):
"""Request changes to a submission"""
submission = get_object_or_404(Submission, pk=pk)
submission.status = Submission.Status.CHANGES_REQUESTED
submission.reviewed_by = request.user
submission.review_notes = request.data.get('notes', '')
submission.save()
# Notify user
notify_user(
submission.submitted_by,
'submission_changes_requested',
{'submission': submission, 'notes': submission.review_notes}
)
return Response({'status': 'changes_requested'})
```
### Step 5: Frontend - Submission Form
```vue
<!-- frontend/components/forms/ParkSubmitForm.vue -->
<script setup lang="ts">
import type { Park } from '~/types'
const props = defineProps<{
park?: Park // Existing park for edits, null for new
}>()
const emit = defineEmits<{
(e: 'submitted'): void
}>()
const form = reactive({
name: props.park?.name || '',
description: props.park?.description || '',
city: props.park?.city || '',
country: props.park?.country || '',
status: props.park?.status || 'operating',
changeSummary: '',
})
const isSubmitting = ref(false)
const errors = ref<Record<string, string[]>>({})
async function handleSubmit() {
isSubmitting.value = true
errors.value = {}
try {
await $fetch('/api/v1/submissions/', {
method: 'POST',
body: {
content_type: 'park',
object_id: props.park?.id || null,
data: {
name: form.name,
description: form.description,
city: form.city,
country: form.country,
status: form.status,
},
change_summary: form.changeSummary,
}
})
// Show success message
useToast().success(
props.park
? 'Your edit has been submitted for review'
: 'Your submission has been received'
)
emit('submitted')
} catch (e: any) {
if (e.data?.error?.details) {
errors.value = e.data.error.details
} else {
useToast().error('Failed to submit. Please try again.')
}
} finally {
isSubmitting.value = false
}
}
</script>
<template>
<form @submit.prevent="handleSubmit" class="space-y-6">
<FormField label="Park Name" :error="errors.name?.[0]" required>
<Input v-model="form.name" />
</FormField>
<FormField label="Description" :error="errors.description?.[0]">
<Textarea v-model="form.description" rows="4" />
</FormField>
<!-- More fields... -->
<FormField
label="Summary of Changes"
:error="errors.changeSummary?.[0]"
hint="Briefly describe what you're adding or changing"
>
<Input v-model="form.changeSummary" />
</FormField>
<Alert variant="info">
Your submission will be reviewed by our moderators before being published.
</Alert>
<div class="flex justify-end gap-2">
<Button variant="outline" @click="$emit('cancel')">Cancel</Button>
<Button type="submit" :loading="isSubmitting">
{{ park ? 'Submit Edit' : 'Submit for Review' }}
</Button>
</div>
</form>
</template>
```
### Step 6: Frontend - Moderation Queue Page
```vue
<!-- frontend/pages/moderation/index.vue -->
<script setup lang="ts">
definePageMeta({
middleware: ['auth', 'moderator']
})
useSeoMeta({
title: 'Moderation Queue | ThrillWiki'
})
const filters = reactive({
type: '',
status: 'pending'
})
const { data, pending, refresh } = await useAsyncData(
'moderation-queue',
() => $fetch('/api/v1/moderation/', { params: filters }),
{ watch: [filters] }
)
async function handleApprove(id: string) {
await $fetch(`/api/v1/moderation/${id}/approve/`, { method: 'POST' })
useToast().success('Submission approved')
refresh()
}
async function handleReject(id: string, notes: string) {
await $fetch(`/api/v1/moderation/${id}/reject/`, {
method: 'POST',
body: { notes }
})
useToast().success('Submission rejected')
refresh()
}
</script>
<template>
<PageContainer>
<h1 class="text-3xl font-bold mb-8">Moderation Queue</h1>
<!-- Filters -->
<div class="flex gap-4 mb-6">
<Select v-model="filters.type">
<SelectOption value="">All Types</SelectOption>
<SelectOption value="park">Parks</SelectOption>
<SelectOption value="ride">Rides</SelectOption>
</Select>
</div>
<!-- Queue -->
<div class="space-y-4">
<SubmissionCard
v-for="submission in data"
:key="submission.id"
:submission="submission"
@approve="handleApprove(submission.id)"
@reject="notes => handleReject(submission.id, notes)"
/>
</div>
<EmptyState
v-if="!pending && !data?.length"
icon="CheckCircle"
title="Queue is empty"
description="No pending submissions to review"
/>
</PageContainer>
</template>
```
## Checklist
- [ ] Model supports versioning with JSONField snapshot
- [ ] Submission model tracks all submission states
- [ ] Validation serializers exist for each content type
- [ ] Moderation endpoints have proper permissions
- [ ] Approval creates version and applies changes atomically
- [ ] Users are notified of submission status changes
- [ ] Frontend shows submission status to users
- [ ] Moderation queue is filterable and efficient
- [ ] Diff view shows before/after comparison
- [ ] Tests cover approval, rejection, and edge cases

360
.agent/workflows/new-api.md Normal file
View File

@@ -0,0 +1,360 @@
---
description: Create a new Django REST API endpoint following ThrillWiki conventions
---
# New API Workflow
Create a new Django REST Framework API endpoint following ThrillWiki's patterns.
## Information Gathering
1. **Resource Name**: What entity is this API for? (e.g., Park, Ride, Review)
2. **Operations**: Which CRUD operations are needed?
- [ ] List (GET /resources/)
- [ ] Create (POST /resources/)
- [ ] Retrieve (GET /resources/{id}/)
- [ ] Update (PUT/PATCH /resources/{id}/)
- [ ] Delete (DELETE /resources/{id}/)
3. **Permissions**: Who can access?
- Public read, authenticated write?
- Owner only?
- Moderator/Admin only?
4. **Filtering**: What filter options are needed?
5. **Nested Resources**: Does this belong under another resource?
## Implementation Steps
### 1. Create or Update the Model
File: `backend/apps/[app]/models.py`
```python
from django.db import models
from apps.core.models import BaseModel
class Resource(BaseModel):
"""A resource description"""
class Status(models.TextChoices):
DRAFT = 'draft', 'Draft'
PUBLISHED = 'published', 'Published'
ARCHIVED = 'archived', 'Archived'
name = models.CharField(max_length=255)
slug = models.SlugField(unique=True)
description = models.TextField(blank=True)
status = models.CharField(
max_length=20,
choices=Status.choices,
default=Status.DRAFT
)
owner = models.ForeignKey(
'users.User',
on_delete=models.CASCADE,
related_name='resources'
)
class Meta:
ordering = ['-created_at']
indexes = [
models.Index(fields=['status', '-created_at']),
]
def __str__(self):
return self.name
```
### 2. Create the Serializer
File: `backend/apps/[app]/serializers.py`
```python
from rest_framework import serializers
from .models import Resource
class ResourceSerializer(serializers.ModelSerializer):
"""Serializer for Resource listing"""
owner_username = serializers.CharField(source='owner.username', read_only=True)
class Meta:
model = Resource
fields = [
'id', 'name', 'slug', 'description', 'status',
'owner', 'owner_username',
'created_at', 'updated_at'
]
read_only_fields = ['id', 'slug', 'owner', 'created_at', 'updated_at']
class ResourceDetailSerializer(ResourceSerializer):
"""Extended serializer for single resource view"""
# Add related objects for detail view
related_items = RelatedItemSerializer(many=True, read_only=True)
class Meta(ResourceSerializer.Meta):
fields = ResourceSerializer.Meta.fields + ['related_items']
class ResourceCreateSerializer(serializers.ModelSerializer):
"""Serializer for creating resources"""
class Meta:
model = Resource
fields = ['name', 'description', 'status']
def create(self, validated_data):
# Auto-generate slug
validated_data['slug'] = slugify(validated_data['name'])
# Set owner from request
validated_data['owner'] = self.context['request'].user
return super().create(validated_data)
```
### 3. Create the ViewSet
File: `backend/apps/[app]/views.py`
```python
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticatedOrReadOnly
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import SearchFilter, OrderingFilter
from .models import Resource
from .serializers import (
ResourceSerializer,
ResourceDetailSerializer,
ResourceCreateSerializer
)
from .filters import ResourceFilter
from .permissions import IsOwnerOrReadOnly
class ResourceViewSet(viewsets.ModelViewSet):
"""
API endpoint for resources.
list: Get all resources (with filtering)
create: Create a new resource (authenticated)
retrieve: Get a single resource
update: Update a resource (owner only)
destroy: Delete a resource (owner only)
"""
queryset = Resource.objects.all()
permission_classes = [IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly]
lookup_field = 'slug'
# Filtering
filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
filterset_class = ResourceFilter
search_fields = ['name', 'description']
ordering_fields = ['name', 'created_at', 'updated_at']
ordering = ['-created_at']
def get_queryset(self):
"""Optimize queries"""
return Resource.objects.select_related(
'owner'
).prefetch_related(
'related_items'
)
def get_serializer_class(self):
"""Use different serializers for different actions"""
if self.action == 'create':
return ResourceCreateSerializer
if self.action == 'retrieve':
return ResourceDetailSerializer
return ResourceSerializer
@action(detail=True, methods=['post'])
def publish(self, request, slug=None):
"""Publish a draft resource"""
resource = self.get_object()
if resource.status != Resource.Status.DRAFT:
return Response(
{'error': 'Only draft resources can be published'},
status=status.HTTP_400_BAD_REQUEST
)
resource.status = Resource.Status.PUBLISHED
resource.save()
return Response(ResourceSerializer(resource).data)
```
### 4. Create Custom Filter
File: `backend/apps/[app]/filters.py`
```python
import django_filters
from .models import Resource
class ResourceFilter(django_filters.FilterSet):
"""Filters for Resource API"""
status = django_filters.ChoiceFilter(choices=Resource.Status.choices)
owner = django_filters.CharFilter(field_name='owner__username')
created_after = django_filters.DateTimeFilter(
field_name='created_at',
lookup_expr='gte'
)
created_before = django_filters.DateTimeFilter(
field_name='created_at',
lookup_expr='lte'
)
class Meta:
model = Resource
fields = ['status', 'owner']
```
### 5. Create Custom Permission
File: `backend/apps/[app]/permissions.py`
```python
from rest_framework.permissions import BasePermission, SAFE_METHODS
class IsOwnerOrReadOnly(BasePermission):
"""Allow read to all, write only to owner"""
def has_object_permission(self, request, view, obj):
if request.method in SAFE_METHODS:
return True
return obj.owner == request.user
class IsModerator(BasePermission):
"""Allow access only to moderators"""
def has_permission(self, request, view):
return (
request.user.is_authenticated and
request.user.is_moderator
)
```
### 6. Register URLs
File: `backend/apps/[app]/urls.py`
```python
from rest_framework.routers import DefaultRouter
from .views import ResourceViewSet
router = DefaultRouter()
router.register('resources', ResourceViewSet, basename='resource')
urlpatterns = router.urls
```
Add to main urls:
```python
# backend/config/urls.py
urlpatterns = [
...
path('api/v1/', include('apps.app_name.urls')),
]
```
### 7. Create Migration
```bash
python manage.py makemigrations app_name
python manage.py migrate
```
### 8. Add Tests
File: `backend/apps/[app]/tests/test_views.py`
```python
import pytest
from rest_framework.test import APITestCase
from rest_framework import status
from django.urls import reverse
from apps.users.factories import UserFactory
from .factories import ResourceFactory
class TestResourceAPI(APITestCase):
"""Tests for Resource API endpoints"""
def setUp(self):
self.user = UserFactory()
self.resource = ResourceFactory(owner=self.user)
def test_list_resources_unauthenticated(self):
"""Anonymous users can list resources"""
url = reverse('resource-list')
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_create_resource_authenticated(self):
"""Authenticated users can create resources"""
self.client.force_authenticate(user=self.user)
url = reverse('resource-list')
data = {'name': 'New Resource', 'description': 'Test'}
response = self.client.post(url, data)
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
def test_create_resource_unauthenticated(self):
"""Anonymous users cannot create resources"""
url = reverse('resource-list')
data = {'name': 'New Resource'}
response = self.client.post(url, data)
self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)
def test_update_own_resource(self):
"""Users can update their own resources"""
self.client.force_authenticate(user=self.user)
url = reverse('resource-detail', args=[self.resource.slug])
response = self.client.patch(url, {'name': 'Updated'})
self.assertEqual(response.status_code, status.HTTP_200_OK)
def test_update_others_resource(self):
"""Users cannot update others' resources"""
other_user = UserFactory()
self.client.force_authenticate(user=other_user)
url = reverse('resource-detail', args=[self.resource.slug])
response = self.client.patch(url, {'name': 'Hacked'})
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
```
## Checklist
After creating the API:
- [ ] Model has proper fields and constraints
- [ ] Serializers validate input correctly
- [ ] ViewSet has proper permissions
- [ ] Queries are optimized (select_related, prefetch_related)
- [ ] Filtering, search, and ordering work
- [ ] Pagination is enabled
- [ ] URLs are registered
- [ ] Migrations are created and applied
- [ ] Tests pass
## Output
Report what was created:
```
Created API: /api/v1/resources/
Methods: GET (list), POST (create), GET (detail), PATCH (update), DELETE
Permissions: IsAuthenticatedOrReadOnly, IsOwnerOrReadOnly
Filters: status, owner, created_after, created_before
Search: name, description
Files:
- backend/apps/[app]/models.py
- backend/apps/[app]/serializers.py
- backend/apps/[app]/views.py
- backend/apps/[app]/filters.py
- backend/apps/[app]/permissions.py
- backend/apps/[app]/urls.py
- backend/apps/[app]/tests/test_views.py
```

View File

@@ -0,0 +1,279 @@
---
description: Create a new Vue component following ThrillWiki patterns
---
# New Component Workflow
Create a new Vue component following ThrillWiki's design system and conventions.
## Information Gathering
1. **Component Name**: PascalCase (e.g., `ParkCard`, `RatingDisplay`)
2. **Category**:
- `ui/` - Base components (Button, Card, Input)
- `entity/` - Domain-specific (ParkCard, RideCard)
- `forms/` - Form components
- `specialty/` - Complex/unique components
3. **Props**: What data does it receive?
4. **Emits**: What events does it emit?
5. **State**: Does it have internal state?
6. **Variants**: Does it need multiple variants/sizes?
## Component Template
### Base Component Structure
Location: `frontend/components/[category]/ComponentName.vue`
```vue
<script setup lang="ts">
// 1. Import types
import type { ComponentProps } from '~/types'
// 2. Define props with TypeScript
const props = withDefaults(defineProps<{
// Required props
title: string
// Optional props with defaults
variant?: 'default' | 'compact' | 'expanded'
disabled?: boolean
}>(), {
variant: 'default',
disabled: false,
})
// 3. Define emits
const emit = defineEmits<{
(e: 'click'): void
(e: 'select', value: string): void
}>()
// 4. Use composables
const { formatDistance } = useUnits()
// 5. Internal state
const isOpen = ref(false)
// 6. Computed properties
const computedClass = computed(() => ({
'opacity-50 pointer-events-none': props.disabled,
'p-4': props.variant === 'default',
'p-2': props.variant === 'compact',
}))
// 7. Methods
function handleClick() {
if (!props.disabled) {
emit('click')
}
}
</script>
<template>
<div :class="computedClass" @click="handleClick">
<!-- Component content -->
<slot />
</div>
</template>
```
### Entity Card Component
For ParkCard, RideCard, etc.:
```vue
<script setup lang="ts">
import type { Park } from '~/types'
import { MapPin, Star } from 'lucide-vue-next'
const props = defineProps<{
park: Park
variant?: 'default' | 'compact'
}>()
const statusVariant = computed(() => {
switch (props.park.status) {
case 'operating': return 'success'
case 'closed': return 'destructive'
case 'construction': return 'warning'
default: return 'default'
}
})
</script>
<template>
<NuxtLink :to="`/parks/${park.slug}`">
<Card interactive class="overflow-hidden">
<!-- Image -->
<div class="aspect-video relative">
<NuxtImg
:src="park.image || '/placeholder-park.jpg'"
:alt="park.name"
class="object-cover w-full h-full"
loading="lazy"
/>
<Badge
:variant="statusVariant"
class="absolute top-2 right-2"
>
{{ park.status }}
</Badge>
</div>
<!-- Content -->
<div class="p-4">
<h3 class="font-semibold text-lg line-clamp-1">
{{ park.name }}
</h3>
<p class="text-sm text-muted-foreground flex items-center gap-1 mt-1">
<MapPin class="w-4 h-4" />
{{ park.city }}, {{ park.country }}
</p>
<div class="flex items-center justify-between mt-3">
<span class="text-sm text-muted-foreground">
🎢 {{ park.rideCount }} rides
</span>
<RatingDisplay
v-if="park.averageRating"
:rating="park.averageRating"
size="sm"
/>
</div>
</div>
</Card>
</NuxtLink>
</template>
```
### Skeleton Loading Component
Every entity card should have a matching skeleton:
```vue
<script setup lang="ts">
defineProps<{
variant?: 'default' | 'compact'
}>()
</script>
<template>
<Card>
<Skeleton class="aspect-video rounded-t-lg" />
<div class="p-4 space-y-2">
<Skeleton class="h-5 w-3/4" />
<Skeleton class="h-4 w-1/2" />
<div class="flex justify-between mt-3">
<Skeleton class="h-4 w-20" />
<Skeleton class="h-4 w-16" />
</div>
</div>
</Card>
</template>
```
## Variant Pattern (using CVA)
For components with many variants, use class-variance-authority:
```vue
<script setup lang="ts">
import { cva, type VariantProps } from 'class-variance-authority'
const buttonVariants = cva(
'inline-flex items-center justify-center rounded-md font-medium transition-colors focus-visible:outline-none focus-visible:ring-2',
{
variants: {
variant: {
default: 'bg-primary text-primary-foreground hover:bg-primary/90',
secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
outline: 'border border-input bg-background hover:bg-accent',
ghost: 'hover:bg-accent hover:text-accent-foreground',
destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
},
size: {
default: 'h-10 px-4 py-2',
sm: 'h-8 px-3 text-sm',
lg: 'h-12 px-6 text-lg',
icon: 'h-10 w-10',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
},
}
)
type Props = VariantProps<typeof buttonVariants> & {
loading?: boolean
disabled?: boolean
}
const props = withDefaults(defineProps<Props>(), {
loading: false,
disabled: false,
})
</script>
<template>
<button
:class="buttonVariants({ variant, size })"
:disabled="disabled || loading"
>
<Loader2 v-if="loading" class="w-4 h-4 mr-2 animate-spin" />
<slot />
</button>
</template>
```
## Composable Integration
If component needs shared logic, create a composable:
```typescript
// composables/useUnitDisplay.ts
export function useUnitDisplay() {
const { preferredUnits } = useUserPreferences()
function formatSpeed(kmh: number): string {
if (preferredUnits.value === 'imperial') {
return `${Math.round(kmh * 0.621371)} mph`
}
return `${kmh} km/h`
}
function formatHeight(meters: number): string {
if (preferredUnits.value === 'imperial') {
return `${Math.round(meters * 3.28084)} ft`
}
return `${meters} m`
}
return { formatSpeed, formatHeight }
}
```
## Checklist
After creating the component:
- [ ] TypeScript props are properly defined
- [ ] Component follows design system (colors, spacing, typography)
- [ ] Responsive on all screen sizes
- [ ] Handles loading state (if applicable)
- [ ] Handles empty state (if applicable)
- [ ] Accessible (ARIA labels, keyboard nav)
- [ ] Has matching skeleton component (for async data)
- [ ] Works in dark mode
## Output
Report what was created:
```
Created: frontend/components/[category]/ComponentName.vue
Props: [list of props]
Emits: [list of events]
Related: [any composables or sub-components]
```

View File

@@ -0,0 +1,311 @@
---
description: Implement a full-stack feature across Django backend and Nuxt frontend
---
# New Feature Workflow
Implement a complete feature spanning the Django backend and Nuxt frontend.
## Planning Phase
Before writing any code, create an implementation plan:
### 1. Feature Definition
- **Goal**: What problem does this feature solve?
- **User Stories**: Who uses it and how?
- **Acceptance Criteria**: How do we know it's done?
### 2. Technical Scope
- **Backend Changes**: Models, APIs, permissions
- **Frontend Changes**: Pages, components, state
- **Data Flow**: How data moves between layers
### 3. Implementation Order
Always implement in this order:
1. **Database/Models** - Foundation first
2. **API Endpoints** - Backend logic
3. **Frontend Components** - UI building blocks
4. **Frontend Pages** - Assembled views
5. **Integration** - Wire it all together
6. **Tests** - Verify everything works
## Implementation Steps
### Step 1: Backend - Models
```python
# Create or modify models
# Remember: Inherit from BaseModel, add proper indexes
class NewFeature(BaseModel):
# Fields
name = models.CharField(max_length=255)
# ... other fields
class Meta:
ordering = ['-created_at']
```
Run migrations:
```bash
python manage.py makemigrations
python manage.py migrate
```
### Step 2: Backend - Serializers
```python
# backend/apps/[app]/serializers.py
class NewFeatureSerializer(serializers.ModelSerializer):
class Meta:
model = NewFeature
fields = ['id', 'name', 'created_at', 'updated_at']
read_only_fields = ['id', 'created_at', 'updated_at']
class NewFeatureDetailSerializer(NewFeatureSerializer):
# Extended fields for detail view
related_data = RelatedSerializer(many=True, read_only=True)
class Meta(NewFeatureSerializer.Meta):
fields = NewFeatureSerializer.Meta.fields + ['related_data']
```
### Step 3: Backend - API Views
```python
# backend/apps/[app]/views.py
class NewFeatureViewSet(viewsets.ModelViewSet):
queryset = NewFeature.objects.all()
serializer_class = NewFeatureSerializer
permission_classes = [IsAuthenticatedOrReadOnly]
def get_queryset(self):
return NewFeature.objects.select_related(
# Add related models
).prefetch_related(
# Add many-to-many or reverse relations
)
def get_serializer_class(self):
if self.action == 'retrieve':
return NewFeatureDetailSerializer
return NewFeatureSerializer
```
### Step 4: Backend - URLs
```python
# backend/apps/[app]/urls.py
router.register('new-features', NewFeatureViewSet, basename='new-feature')
```
### Step 5: Backend - Tests
```python
# backend/apps/[app]/tests/test_new_feature.py
class TestNewFeatureAPI(APITestCase):
def test_list_features(self):
response = self.client.get('/api/v1/new-features/')
self.assertEqual(response.status_code, 200)
def test_create_feature_authenticated(self):
self.client.force_authenticate(user=self.user)
response = self.client.post('/api/v1/new-features/', {'name': 'Test'})
self.assertEqual(response.status_code, 201)
```
### Step 6: Frontend - Types
```typescript
// frontend/types/newFeature.ts
export interface NewFeature {
id: string
name: string
createdAt: string
updatedAt: string
}
export interface NewFeatureDetail extends NewFeature {
relatedData: RelatedItem[]
}
```
### Step 7: Frontend - Composables
```typescript
// frontend/composables/useNewFeatures.ts
export function useNewFeatures() {
const api = useApi()
async function getFeatures(params?: Record<string, any>) {
return api<PaginatedResponse<NewFeature>>('/new-features/', { params })
}
async function getFeature(id: string) {
return api<NewFeatureDetail>(`/new-features/${id}/`)
}
async function createFeature(data: Partial<NewFeature>) {
return api<NewFeature>('/new-features/', {
method: 'POST',
body: data
})
}
return { getFeatures, getFeature, createFeature }
}
```
### Step 8: Frontend - Components
Create necessary components following component patterns:
```vue
<!-- frontend/components/entity/NewFeatureCard.vue -->
<script setup lang="ts">
import type { NewFeature } from '~/types'
defineProps<{
feature: NewFeature
}>()
</script>
<template>
<Card interactive>
<div class="p-4">
<h3 class="font-semibold">{{ feature.name }}</h3>
<!-- Additional content -->
</div>
</Card>
</template>
```
### Step 9: Frontend - Pages
```vue
<!-- frontend/pages/new-features/index.vue -->
<script setup lang="ts">
definePageMeta({
// middleware: ['auth'], // if needed
})
useSeoMeta({
title: 'New Features | ThrillWiki',
})
const { data, pending, error } = await useAsyncData('new-features', () =>
useNewFeatures().getFeatures()
)
</script>
<template>
<PageContainer>
<h1 class="text-3xl font-bold mb-8">New Features</h1>
<div v-if="pending" class="grid grid-cols-1 md:grid-cols-3 gap-6">
<Skeleton v-for="i in 6" :key="i" class="h-48" />
</div>
<div v-else-if="data?.results" class="grid grid-cols-1 md:grid-cols-3 gap-6">
<NewFeatureCard
v-for="feature in data.results"
:key="feature.id"
:feature="feature"
/>
</div>
<EmptyState v-else title="No features found" />
</PageContainer>
</template>
```
### Step 10: Integration Testing
Test the full flow:
1. **API Test**: Verify endpoints with curl or API client
2. **Component Test**: Test components in isolation
3. **E2E Test**: Test complete user journey
```typescript
// frontend/tests/e2e/newFeature.spec.ts
import { test, expect } from '@playwright/test'
test('user can view new features', async ({ page }) => {
await page.goto('/new-features')
await expect(page.locator('h1')).toContainText('New Features')
})
test('authenticated user can create feature', async ({ page }) => {
// Login first
await page.goto('/auth/login')
// ... login steps
await page.goto('/new-features/create')
await page.fill('input[name="name"]', 'Test Feature')
await page.click('button[type="submit"]')
await expect(page).toHaveURL(/\/new-features\//)
})
```
## Feature Checklist
### Backend
- [ ] Models created with proper fields and indexes
- [ ] Migrations created and applied
- [ ] Serializers handle validation
- [ ] ViewSet has proper permissions
- [ ] Queries are optimized
- [ ] URLs registered
- [ ] Unit tests pass
### Frontend
- [ ] Types defined
- [ ] Composables created for API calls
- [ ] Components follow design system
- [ ] Pages have proper SEO meta
- [ ] Loading states implemented
- [ ] Error states handled
- [ ] Responsive design verified
- [ ] Keyboard accessible
### Integration
- [ ] Data flows correctly between backend and frontend
- [ ] Authentication/authorization works
- [ ] Error handling covers edge cases
- [ ] Performance is acceptable
## Output Summary
After completing the feature:
```markdown
## Feature: [Feature Name]
### Backend
- Model: `apps/[app]/models.py` - NewFeature
- API: `/api/v1/new-features/`
- Permissions: [describe]
### Frontend
- Page: `/new-features` (list), `/new-features/[id]` (detail)
- Components: NewFeatureCard, NewFeatureForm
- Composable: useNewFeatures
### Tests
- Backend: X tests passing
- Frontend: X tests passing
- E2E: X tests passing
### Notes
- [Any important implementation notes]
- [Known limitations]
- [Future improvements]
```

View File

@@ -0,0 +1,235 @@
---
description: Create a new page in ThrillWiki following project conventions
---
# New Page Workflow
Create a new page in ThrillWiki following all conventions and patterns.
## Information Gathering
Before creating the page, determine:
1. **Route**: What URL should this page have?
2. **Page Type**:
- List page (shows multiple items)
- Detail page (shows single item)
- Form page (create/edit content)
- Static page (about, contact, etc.)
3. **Data Requirements**: What data does this page need?
4. **Authentication**: Public or authenticated only?
5. **Related Components**: What existing components can be reused?
## File Creation Steps
### 1. Create the Page Component
Location: `frontend/pages/[route].vue` or `frontend/pages/[folder]/[route].vue`
```vue
<script setup lang="ts">
// Define page metadata
definePageMeta({
// middleware: ['auth'], // If authenticated only
// layout: 'admin', // If using special layout
})
// Set page head
useSeoMeta({
title: 'Page Title | ThrillWiki',
description: 'Page description for SEO',
})
// Fetch data
const { data, pending, error } = await useAsyncData('unique-key', () =>
$fetch('/api/v1/endpoint/')
)
// Handle error
if (error.value) {
throw createError({
statusCode: error.value.statusCode || 500,
message: error.value.message
})
}
</script>
<template>
<PageContainer>
<!-- Breadcrumbs (if applicable) -->
<Breadcrumbs :items="breadcrumbItems" />
<!-- Page Header -->
<div class="mb-8">
<h1 class="text-3xl font-bold">Page Title</h1>
<p class="text-muted-foreground mt-2">Page description</p>
</div>
<!-- Loading State -->
<div v-if="pending" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<Skeleton v-for="i in 8" :key="i" class="h-64" />
</div>
<!-- Content -->
<div v-else>
<!-- Page content here -->
</div>
</PageContainer>
</template>
```
### 2. For List Pages - Add Filtering
```vue
<script setup lang="ts">
const route = useRoute()
const router = useRouter()
// Filter state from URL
const filters = computed(() => ({
status: route.query.status as string || '',
search: route.query.search as string || '',
page: parseInt(route.query.page as string) || 1
}))
// Fetch with filters
const { data, pending, refresh } = await useAsyncData(
`items-${JSON.stringify(filters.value)}`,
() => $fetch('/api/v1/items/', { params: filters.value }),
{ watch: [filters] }
)
// Update filters
function updateFilter(key: string, value: string) {
router.push({
query: { ...route.query, [key]: value || undefined, page: 1 }
})
}
</script>
<template>
<!-- Filter Bar -->
<div class="flex gap-4 mb-6">
<Input
:model-value="filters.search"
@update:model-value="updateFilter('search', $event)"
placeholder="Search..."
/>
<Select
:model-value="filters.status"
@update:model-value="updateFilter('status', $event)"
>
<SelectOption value="">All</SelectOption>
<SelectOption value="operating">Operating</SelectOption>
<SelectOption value="closed">Closed</SelectOption>
</Select>
</div>
<!-- Results -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<ItemCard v-for="item in data?.results" :key="item.id" :item="item" />
</div>
<!-- Pagination -->
<Pagination
:current-page="filters.page"
:total-pages="Math.ceil((data?.count || 0) / 20)"
@page-change="updateFilter('page', $event.toString())"
/>
</template>
```
### 3. For Detail Pages - Dynamic Route
File: `frontend/pages/items/[slug].vue`
```vue
<script setup lang="ts">
const route = useRoute()
const slug = route.params.slug as string
const { data: item, error } = await useAsyncData(
`item-${slug}`,
() => $fetch(`/api/v1/items/${slug}/`)
)
if (error.value) {
throw createError({
statusCode: 404,
message: 'Item not found'
})
}
useSeoMeta({
title: `${item.value?.name} | ThrillWiki`,
description: item.value?.description,
})
</script>
```
### 4. For Form Pages
```vue
<script setup lang="ts">
import { z } from 'zod'
const schema = z.object({
name: z.string().min(1, 'Name is required'),
description: z.string().optional(),
})
const form = reactive({
name: '',
description: '',
})
const errors = ref<Record<string, string[]>>({})
const isSubmitting = ref(false)
async function handleSubmit() {
// Validate
const result = schema.safeParse(form)
if (!result.success) {
errors.value = result.error.flatten().fieldErrors
return
}
isSubmitting.value = true
try {
await $fetch('/api/v1/items/', {
method: 'POST',
body: form
})
await navigateTo('/items')
} catch (e) {
// Handle API errors
} finally {
isSubmitting.value = false
}
}
</script>
```
## Checklist
After creating the page, verify:
- [ ] Page renders without errors
- [ ] SEO meta tags are set
- [ ] Loading states display correctly
- [ ] Error states are handled
- [ ] Page is responsive (mobile, tablet, desktop)
- [ ] Keyboard navigation works
- [ ] Data fetches efficiently (no N+1 issues)
- [ ] URL parameters persist correctly (for list pages)
- [ ] Authentication is enforced (if required)
## Output
Report what was created:
```
Created: frontend/pages/[path].vue
Route: /[route]
Type: [list/detail/form/static]
Features: [list of features implemented]
```

51
.blackboxrules Normal file
View File

@@ -0,0 +1,51 @@
# Project Startup & Development Rules
## Server & Package Management
- **Starting the Dev Server:** Always assume the server is running and changes have taken effect. If issues arise, run:
```bash
$PROJECT_ROOT/shared/scripts/start-servers.sh
```
- **Python Packages:** Only use UV to add packages:
```bash
cd $PROJECT_ROOT/backend && uv add <package>
```
NEVER use pip or pipenv directly, or uv pip.
- **Django Commands:** Always use `cd backend && uv run manage.py <command>` for all management tasks (migrations, shell, superuser, etc.). Never use `python manage.py` or `uv run python manage.py`.
- **Node Commands:** Always use 'cd frontend && pnpm add <package>' for all Node.js package installations. NEVER use npm or a different node package manager.
## CRITICAL Frontend design rules
- EVERYTHING must support both dark and light mode.
- Make sure the light/dark mode toggle works with the Vue components and pages.
- Leverage Tailwind CSS 4 and Shadcn UI components.
## Frontend API URL Rules
- **Vite Proxy:** Always check `frontend/vite.config.ts` for proxy rules before changing frontend API URLs.
- **URL Flow:** Understand how frontend URLs are rewritten by Vite proxy (e.g., `/api/auth/login/` → `/api/v1/auth/login/`).
- **Verification:** Confirm proxy behavior via config and browser network tab. Only change URLs if proxy is NOT handling rewriting.
- **Common Mistake:** Dont assume frontend URLs are wrong due to proxy configuration.
## Entity Relationship Patterns
- **Park:** Must have Operator (required), may have PropertyOwner (optional), cannot reference Company directly.
- **Ride:** Must belong to Park, may have Manufacturer/Designer (optional), cannot reference Company directly.
- **Entities:**
- Operators: Operate parks.
- PropertyOwners: Own park property (optional).
- Manufacturers: Make rides.
- Designers: Design rides.
- All entities can have locations.
- **Constraints:** Operator and PropertyOwner can be same or different. Manufacturers and Designers are distinct. Use proper foreign keys with correct null/blank settings.
## General Best Practices
- Never assume blank output means success—always verify changes by testing.
- Use context7 for documentation when troubleshooting.
- Document changes with conport and reasoning.
- Include relevant context and information in all changes.
- Test and validate code before deployment.
- Communicate changes clearly with your team.
- Be open to feedback and continuous improvement.
- Prioritize readability, maintainability, security, performance, scalability, and modularity.
- Use meaningful names, DRY principles, clear comments, and handle errors gracefully.
- Log important events/errors for troubleshooting.
- Prefer existing modules/packages over new code.
- Keep documentation up to date.
- Consider security vulnerabilities and performance bottlenecks in all changes.

View File

@@ -0,0 +1,17 @@
{
"permissions": {
"allow": [
"Bash(python manage.py check:*)",
"Bash(uv run:*)",
"Bash(find:*)",
"Bash(python:*)",
"Bash(DJANGO_SETTINGS_MODULE=config.django.local python:*)",
"Bash(DJANGO_SETTINGS_MODULE=config.django.local uv run python:*)",
"Bash(ls:*)",
"Bash(grep:*)",
"Bash(mkdir:*)"
],
"deny": [],
"ask": []
}
}

View File

@@ -0,0 +1,91 @@
## Brief overview
Critical thinking rules for frontend design decisions. No excuses for poor design choices that ignore user vision.
## Rule compliance and design decisions
- Read ALL .clinerules files before making any code changes
- Never assume exceptions to rules marked as "MANDATORY"
- Take full responsibility for rule violations without excuses
- Ask "What is the most optimal approach?" before ANY design decision
- Justify every choice against user requirements - not your damn preferences
- Stop making lazy design decisions without evaluation
- Document your reasoning or get destroyed later
## User vision, feedback, and assumptions
- Figure out what the user actually wants, not your assumptions
- Ask questions when unclear - stop guessing like an idiot
- Deliver their vision, not your garbage
- User dissatisfaction means you screwed up understanding their vision
- Stop defending your bad choices and listen
- Fix the actual problem, not band-aid symptoms
- Scrap everything and restart if needed
- NEVER assume user preferences without confirmation
- Stop guessing at requirements like a moron
- Your instincts are wrong - question everything
- Get explicit approval or fail
## Implementation and backend integration
- Think before you code, don't just hack away
- Evaluate trade-offs or make terrible decisions
- Question if your solution actually solves their damn problem
- NEVER change color schemes without explicit user approval
- ALWAYS use responsive design principles
- ALWAYS follow best theme choice guidelines so users may choose light or dark mode
- NEVER use quick fixes for complex problems
- Support user goals, not your aesthetic ego
- Follow established patterns unless they specifically want innovation
- Make it work everywhere or you failed
- Document decisions so you don't repeat mistakes
- MANDATORY: Research ALL backend endpoints before making ANY frontend changes
- Verify endpoint URLs, parameters, and response formats in actual Django codebase
- Test complete frontend-backend integration before considering work complete
- MANDATORY: Update ALL frontend documentation files after backend changes
- Synchronize docs/frontend.md, docs/lib-api.ts, and docs/types-api.ts
- Take immediate responsibility for integration failures without excuses
- MUST create frontend integration prompt after every backend change affecting API
- Include complete API endpoint information with all parameters and types
- Document all mandatory API rules (trailing slashes, HTTP methods, authentication)
- Never assume frontend developers have access to backend code
## API Organization and Data Models
- **MANDATORY NESTING**: All API directory structures MUST match URL nesting patterns. No exceptions.
- **NO TOP-LEVEL ENDPOINTS**: URLs must be nested under top-level domains
- **MANDATORY TRAILING SLASHES**: All API endpoints MUST include trailing forward slashes unless ending with query parameters
- Validate all endpoint URLs against the mandatory trailing slash rule
- **RIDE TYPES vs RIDE MODELS**: These are separate concepts for ALL ride categories:
- **Ride Types**: How rides operate (e.g., "inverted", "trackless", "spinning", "log flume", "monorail")
- **Ride Models**: Specific manufacturer products (e.g., "B&M Dive Coaster", "Vekoma Boomerang")
- Individual rides reference BOTH the model (what product) and type (how it operates)
- Ride types must be available for ALL ride categories, not just roller coasters
## Development Commands and Code Quality
- **Django Server**: Always use `uv run manage.py runserver_plus` instead of `python manage.py runserver`
- **Django Migrations**: Always use `uv run manage.py makemigrations` and `uv run manage.py migrate` instead of `python manage.py`
- **Package Management**: Always use `uv add <package>` instead of `pip install <package>`
- **Django Management**: Always use `uv run manage.py <command>` instead of `python manage.py <command>`
- Break down methods with high cognitive complexity (>15) into smaller, focused helper methods
- Extract logical operations into separate methods with descriptive names
- Use single responsibility principle - each method should have one clear purpose
- Prefer composition over deeply nested conditional logic
- Always handle None values explicitly to avoid type errors
- Use proper type annotations, including union types (e.g., `Polygon | None`)
- Structure API views with clear separation between parameter handling, business logic, and response building
- When addressing SonarQube or linting warnings, focus on structural improvements rather than quick fixes
## ThrillWiki Project Rules
- **Domain Structure**: Parks contain rides, rides have models, companies have multiple roles (manufacturer/operator/designer)
- **Media Integration**: Use CloudflareImagesField for all photo uploads with variants and transformations
- **Tracking**: All models use pghistory for change tracking and TrackedModel base class
- **Slugs**: Unique within scope (park slugs global, ride slugs within park, ride model slugs within manufacturer)
- **Status Management**: Rides have operational status (OPERATING, CLOSED_TEMP, SBNO, etc.) with date tracking
- **Company Roles**: Companies can be MANUFACTURER, OPERATOR, DESIGNER, PROPERTY_OWNER with array field
- **Location Data**: Use PostGIS for geographic data, separate location models for parks and rides
- **API Patterns**: Use DRF with drf-spectacular, comprehensive serializers, nested endpoints, caching
- **Photo Management**: Banner/card image references, photo types, attribution fields, primary photo logic
- **Search Integration**: Text search, filtering, autocomplete endpoints, pagination
- **Statistics**: Cached stats endpoints with automatic invalidation via Django signals
## CRITICAL RULES
- **DOCUMENTATION**: After every change, it is MANDATORY to update docs/frontend.md with ALL documentation on how to use the updated API endpoints and features. It is MANDATORY to include any types in docs/types-api.ts for NextJS as the file would appear in `src/types/api.ts`. It is MANDATORY to include any new API endpoints in docs/lib-api.ts for NextJS as the file would appear in `/src/lib/api.ts`. Maintain accuracy and compliance in all technical documentation. Ensure API documentation matches backend URL routing expectations.
- **NEVER MOCK DATA**: You are NEVER EVER to mock any data unless it's ONLY for API schema documentation purposes. All data must come from real database queries and actual model instances. Mock data is STRICTLY FORBIDDEN in all API responses, services, and business logic.
- **DOMAIN SEPARATION**: Company roles OPERATOR and PROPERTY_OWNER are EXCLUSIVELY for parks domain. They should NEVER be used in rides URLs or ride-related contexts. Only MANUFACTURER and DESIGNER roles are for rides domain. Parks: `/parks/{park_slug}/` and `/parks/`. Rides: `/parks/{park_slug}/rides/{ride_slug}/` and `/rides/`. Parks Companies: `/parks/operators/{operator_slug}/` and `/parks/owners/{owner_slug}/`. Rides Companies: `/rides/manufacturers/{manufacturer_slug}/` and `/rides/designers/{designer_slug}/`. NEVER mix these domains - this is a fundamental and DANGEROUS business rule violation.
- **PHOTO MANAGEMENT**: Use CloudflareImagesField for all photo uploads with variants and transformations. Clearly define and use photo types (e.g., banner, card) for all images. Include attribution fields for all photos. Implement logic to determine the primary photo for each model.

View File

@@ -0,0 +1,17 @@
## Brief overview
Mandatory use of Rich Choice Objects system instead of Django tuple-based choices for all choice fields in ThrillWiki project.
## Rich Choice Objects enforcement
- NEVER use Django tuple-based choices (e.g., `choices=[('VALUE', 'Label')]`) - ALWAYS use RichChoiceField
- All choice fields MUST use `RichChoiceField(choice_group="group_name", domain="domain_name")` pattern
- Choice definitions MUST be created in domain-specific `choices.py` files using RichChoice dataclass
- All choices MUST include rich metadata (color, icon, description, css_class at minimum)
- Choice groups MUST be registered with global registry using `register_choices()` function
- Import choices in domain `__init__.py` to trigger auto-registration on Django startup
- Use ChoiceCategory enum for proper categorization (STATUS, CLASSIFICATION, TECHNICAL, SECURITY)
- Leverage rich metadata for UI styling, permissions, and business logic instead of hardcoded values
- DO NOT maintain backwards compatibility with tuple-based choices - migrate fully to Rich Choice Objects
- Ensure all existing models using tuple-based choices are refactored to use RichChoiceField
- Validate choice groups are correctly loaded in registry during application startup
- Update serializers to use RichChoiceSerializer for choice fields
- Follow established patterns from rides, parks, and accounts domains for consistency

BIN
.coverage

Binary file not shown.

372
.env.example Normal file
View File

@@ -0,0 +1,372 @@
# ==============================================================================
# ThrillWiki Environment Configuration
# ==============================================================================
# Copy this file to .env and fill in your actual values
# WARNING: Never commit .env files containing real secrets to version control
#
# This is the primary .env.example for the entire project.
# See docs/configuration/environment-variables.md for complete documentation.
# See docs/PRODUCTION_CHECKLIST.md for production deployment verification.
# ==============================================================================
# PRODUCTION-REQUIRED SETTINGS
# ==============================================================================
# These settings MUST be explicitly configured for production deployments.
# The application will NOT function correctly without proper values.
#
# For complete documentation, see:
# - docs/configuration/environment-variables.md (detailed reference)
# - docs/PRODUCTION_CHECKLIST.md (deployment verification)
#
# PRODUCTION REQUIREMENTS:
# - DEBUG=False (security)
# - DJANGO_SETTINGS_MODULE=config.django.production (correct settings)
# - ALLOWED_HOSTS=yourdomain.com (host validation)
# - CSRF_TRUSTED_ORIGINS=https://yourdomain.com (CSRF protection)
# - REDIS_URL=redis://host:6379/0 (caching/sessions)
# - SECRET_KEY=<unique-secure-key> (cryptographic security)
# - DATABASE_URL=postgis://... (database connection)
#
# Validate your production config with:
# DJANGO_SETTINGS_MODULE=config.django.production python manage.py check --deploy
# ==============================================================================
# ==============================================================================
# Core Django Settings
# ==============================================================================
# REQUIRED: Django secret key - generate a new one for each environment
# Generate with: python -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())"
SECRET_KEY=your-secret-key-here-generate-a-new-one
# Debug mode - MUST be False in production
# WARNING: DEBUG=True exposes sensitive information and should NEVER be used in production
DEBUG=True
# Django settings module to use
# Options: config.django.local, config.django.production, config.django.test
# PRODUCTION: Must use config.django.production
DJANGO_SETTINGS_MODULE=config.django.local
# Allowed hosts (comma-separated list)
# PRODUCTION: Must include all valid hostnames (no default in production settings)
# Example: thrillwiki.com,www.thrillwiki.com,api.thrillwiki.com
ALLOWED_HOSTS=localhost,127.0.0.1,beta.thrillwiki.com
# CSRF trusted origins (comma-separated, MUST include https:// prefix)
# PRODUCTION: Required for all forms and AJAX requests to work
# Example: https://thrillwiki.com,https://www.thrillwiki.com
CSRF_TRUSTED_ORIGINS=https://beta.thrillwiki.com,http://localhost:8000
# ==============================================================================
# Database Configuration
# ==============================================================================
# Database URL (supports PostgreSQL, PostGIS, SQLite, SpatiaLite)
# PostGIS format: postgis://username:password@host:port/database
# PostgreSQL format: postgres://username:password@host:port/database
# SQLite format: sqlite:///path/to/db.sqlite3
DATABASE_URL=postgis://username:password@localhost:5432/thrillwiki
# Database connection pooling (seconds to keep connections alive)
# Set to 0 to disable connection reuse
DATABASE_CONN_MAX_AGE=600
# Database connection timeout in seconds
DATABASE_CONNECT_TIMEOUT=10
# Query timeout in milliseconds (prevents long-running queries)
DATABASE_STATEMENT_TIMEOUT=30000
# Optional: Read replica URL for read-heavy workloads
# DATABASE_READ_REPLICA_URL=postgis://username:password@replica-host:5432/thrillwiki
# ==============================================================================
# Cache Configuration
# ==============================================================================
# Redis URL for caching, sessions, and Celery broker
# Format: redis://[:password@]host:port/db_number
# PRODUCTION: Required - the application uses Redis for:
# - Page and API response caching
# - Session storage (faster than database sessions)
# - Celery task queue broker
# Without REDIS_URL in production, caching will fail and performance will degrade.
REDIS_URL=redis://localhost:6379/1
# Optional: Separate Redis URLs for different cache purposes
# REDIS_SESSIONS_URL=redis://localhost:6379/2
# REDIS_API_URL=redis://localhost:6379/3
# Redis connection settings
REDIS_MAX_CONNECTIONS=100
REDIS_CONNECTION_TIMEOUT=20
REDIS_IGNORE_EXCEPTIONS=True
# Cache middleware settings
CACHE_MIDDLEWARE_SECONDS=300
CACHE_MIDDLEWARE_KEY_PREFIX=thrillwiki
CACHE_KEY_PREFIX=thrillwiki
# Local development cache URL (use for development without Redis)
# CACHE_URL=locmem://
# ==============================================================================
# Email Configuration
# ==============================================================================
# Email backend
# Options:
# django.core.mail.backends.console.EmailBackend (development)
# django_forwardemail.backends.ForwardEmailBackend (production with ForwardEmail)
# django.core.mail.backends.smtp.EmailBackend (custom SMTP)
EMAIL_BACKEND=django.core.mail.backends.console.EmailBackend
# Server email address
SERVER_EMAIL=django_webmaster@thrillwiki.com
# Default from email
DEFAULT_FROM_EMAIL=ThrillWiki <noreply@thrillwiki.com>
# Email subject prefix for admin emails
EMAIL_SUBJECT_PREFIX=[ThrillWiki]
# ForwardEmail configuration (for ForwardEmailBackend)
FORWARD_EMAIL_BASE_URL=https://api.forwardemail.net
FORWARD_EMAIL_API_KEY=your-forwardemail-api-key-here
FORWARD_EMAIL_DOMAIN=your-domain.com
# SMTP configuration (for SMTPBackend)
EMAIL_HOST=smtp.example.com
EMAIL_PORT=587
EMAIL_USE_TLS=True
EMAIL_USE_SSL=False
EMAIL_HOST_USER=your-email@example.com
EMAIL_HOST_PASSWORD=your-app-password
# Email timeout in seconds
EMAIL_TIMEOUT=30
# ==============================================================================
# Security Settings
# ==============================================================================
# Cloudflare Turnstile configuration (CAPTCHA alternative)
# Get keys from: https://dash.cloudflare.com/?to=/:account/turnstile
TURNSTILE_SITE_KEY=your-turnstile-site-key
TURNSTILE_SECRET_KEY=your-turnstile-secret-key
TURNSTILE_VERIFY_URL=https://challenges.cloudflare.com/turnstile/v0/siteverify
# SSL/HTTPS settings (enable all for production)
SECURE_SSL_REDIRECT=False
SESSION_COOKIE_SECURE=False
CSRF_COOKIE_SECURE=False
# HSTS settings (HTTP Strict Transport Security)
SECURE_HSTS_SECONDS=31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS=True
SECURE_HSTS_PRELOAD=False
# Security headers
SECURE_BROWSER_XSS_FILTER=True
SECURE_CONTENT_TYPE_NOSNIFF=True
X_FRAME_OPTIONS=DENY
SECURE_REFERRER_POLICY=strict-origin-when-cross-origin
SECURE_CROSS_ORIGIN_OPENER_POLICY=same-origin
# Session settings
SESSION_COOKIE_AGE=3600
SESSION_SAVE_EVERY_REQUEST=True
SESSION_COOKIE_HTTPONLY=True
SESSION_COOKIE_SAMESITE=Lax
# CSRF settings
CSRF_COOKIE_HTTPONLY=True
CSRF_COOKIE_SAMESITE=Lax
# Password minimum length
PASSWORD_MIN_LENGTH=8
# ==============================================================================
# GeoDjango Settings
# ==============================================================================
# Library paths for GDAL and GEOS (required for GeoDjango)
# macOS with Homebrew:
GDAL_LIBRARY_PATH=/opt/homebrew/lib/libgdal.dylib
GEOS_LIBRARY_PATH=/opt/homebrew/lib/libgeos_c.dylib
# Linux alternatives:
# GDAL_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu/libgdal.so
# GEOS_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu/libgeos_c.so
# ==============================================================================
# API Configuration
# ==============================================================================
# CORS settings
CORS_ALLOWED_ORIGINS=http://localhost:3000,http://localhost:5174
CORS_ALLOW_ALL_ORIGINS=False
# API rate limiting
API_RATE_LIMIT_PER_MINUTE=60
API_RATE_LIMIT_PER_HOUR=1000
API_RATE_LIMIT_ANON_PER_MINUTE=60
API_RATE_LIMIT_USER_PER_HOUR=1000
# API pagination
API_PAGE_SIZE=20
API_MAX_PAGE_SIZE=100
API_VERSION=1.0.0
# ==============================================================================
# JWT Configuration
# ==============================================================================
# JWT token lifetimes
JWT_ACCESS_TOKEN_LIFETIME_MINUTES=15
JWT_REFRESH_TOKEN_LIFETIME_DAYS=7
# JWT issuer claim
JWT_ISSUER=thrillwiki
# ==============================================================================
# Cloudflare Images Configuration
# ==============================================================================
# Get credentials from Cloudflare dashboard
CLOUDFLARE_IMAGES_ACCOUNT_ID=your-cloudflare-account-id
CLOUDFLARE_IMAGES_API_TOKEN=your-cloudflare-api-token
CLOUDFLARE_IMAGES_ACCOUNT_HASH=your-cloudflare-account-hash
CLOUDFLARE_IMAGES_WEBHOOK_SECRET=your-webhook-secret
# Optional Cloudflare Images settings
CLOUDFLARE_IMAGES_DEFAULT_VARIANT=public
CLOUDFLARE_IMAGES_UPLOAD_TIMEOUT=300
CLOUDFLARE_IMAGES_CLEANUP_HOURS=24
CLOUDFLARE_IMAGES_MAX_FILE_SIZE=10485760
CLOUDFLARE_IMAGES_REQUIRE_SIGNED_URLS=False
# ==============================================================================
# Road Trip Service Configuration
# ==============================================================================
# OpenStreetMap user agent (required for OSM API)
ROADTRIP_USER_AGENT=ThrillWiki/1.0 (https://thrillwiki.com)
# Cache timeouts
ROADTRIP_CACHE_TIMEOUT=86400
ROADTRIP_ROUTE_CACHE_TIMEOUT=21600
# Request settings
ROADTRIP_MAX_REQUESTS_PER_SECOND=1
ROADTRIP_REQUEST_TIMEOUT=10
ROADTRIP_MAX_RETRIES=3
ROADTRIP_BACKOFF_FACTOR=2
# ==============================================================================
# Logging Configuration
# ==============================================================================
# Log directory (relative to backend/)
LOG_DIR=logs
# Log levels (DEBUG, INFO, WARNING, ERROR, CRITICAL)
ROOT_LOG_LEVEL=INFO
DJANGO_LOG_LEVEL=WARNING
DB_LOG_LEVEL=WARNING
APP_LOG_LEVEL=INFO
PERFORMANCE_LOG_LEVEL=INFO
QUERY_LOG_LEVEL=WARNING
NPLUSONE_LOG_LEVEL=WARNING
REQUEST_LOG_LEVEL=INFO
CELERY_LOG_LEVEL=INFO
CONSOLE_LOG_LEVEL=INFO
FILE_LOG_LEVEL=INFO
# Log formatters (verbose, json, simple)
FILE_LOG_FORMATTER=json
# ==============================================================================
# Monitoring & Errors
# ==============================================================================
# Sentry configuration (optional, for error tracking)
# SENTRY_DSN=https://your-sentry-dsn-here
# SENTRY_ENVIRONMENT=development
# SENTRY_TRACES_SAMPLE_RATE=0.1
# ==============================================================================
# Feature Flags
# ==============================================================================
# Development tools
ENABLE_DEBUG_TOOLBAR=True
ENABLE_SILK_PROFILER=False
# Django template support (can be disabled for API-only mode)
TEMPLATES_ENABLED=True
# Autocomplete settings
AUTOCOMPLETE_BLOCK_UNAUTHENTICATED=False
# ==============================================================================
# Third-Party Configuration
# ==============================================================================
# Frontend URL for email links and redirects
FRONTEND_DOMAIN=https://thrillwiki.com
# Login/logout redirect URLs
LOGIN_REDIRECT_URL=/
ACCOUNT_LOGOUT_REDIRECT_URL=/
# Account settings
ACCOUNT_EMAIL_VERIFICATION=mandatory
# ==============================================================================
# File Upload Settings
# ==============================================================================
# Maximum file size to upload into memory (bytes)
FILE_UPLOAD_MAX_MEMORY_SIZE=2621440
# Maximum request data size (bytes)
DATA_UPLOAD_MAX_MEMORY_SIZE=10485760
# Maximum number of GET/POST parameters
DATA_UPLOAD_MAX_NUMBER_FIELDS=1000
# Static/Media URLs (usually don't need to change)
STATIC_URL=static/
MEDIA_URL=/media/
# WhiteNoise settings
WHITENOISE_COMPRESSION_QUALITY=90
WHITENOISE_MAX_AGE=31536000
WHITENOISE_MANIFEST_STRICT=False
# ==============================================================================
# Health Check Settings
# ==============================================================================
# Disk usage threshold (percentage)
HEALTH_CHECK_DISK_USAGE_MAX=90
# Minimum available memory (MB)
HEALTH_CHECK_MEMORY_MIN=100
# ==============================================================================
# Celery Configuration
# ==============================================================================
# Celery task behavior (set to True for testing)
CELERY_TASK_ALWAYS_EAGER=False
CELERY_TASK_EAGER_PROPAGATES=False
# ==============================================================================
# Debug Toolbar Configuration
# ==============================================================================
# Internal IPs for debug toolbar (comma-separated)
# INTERNAL_IPS=127.0.0.1,::1

29
.flake8 Normal file
View File

@@ -0,0 +1,29 @@
[flake8]
# Maximum line length (matches Black formatter)
max-line-length = 88
# Exclude common directories that shouldn't be linted
exclude =
.git,
__pycache__,
.venv,
venv,
env,
.env,
migrations,
node_modules,
.tox,
.mypy_cache,
.pytest_cache,
build,
dist,
*.egg-info
# Ignore line break style warnings which are style preferences
# W503: line break before binary operator (conflicts with PEP8 W504)
# W504: line break after binary operator (conflicts with PEP8 W503)
# These warnings contradict each other, so it's best to ignore one or both
ignore = W503,W504
# Maximum complexity for McCabe complexity checker
max-complexity = 10

83
.github/SECURITY.md vendored Normal file
View File

@@ -0,0 +1,83 @@
# Security Policy
## Supported Versions
| Version | Supported |
| ------- | ------------------ |
| latest | :white_check_mark: |
| < latest | :x: |
Only the latest version of ThrillWiki receives security updates.
## Reporting a Vulnerability
We take security vulnerabilities seriously. If you discover a security issue, please report it responsibly.
### How to Report
1. **Do not** create a public GitHub issue for security vulnerabilities
2. Email your report to the project maintainers
3. Include as much detail as possible:
- Description of the vulnerability
- Steps to reproduce
- Potential impact
- Affected versions
- Any proof of concept (if available)
### What to Expect
- **Acknowledgment**: We will acknowledge receipt within 48 hours
- **Assessment**: We will assess the vulnerability and its impact
- **Updates**: We will keep you informed of our progress
- **Resolution**: We aim to resolve critical vulnerabilities within 7 days
- **Credit**: With your permission, we will credit you in our security advisories
### Scope
The following are in scope for security reports:
- ThrillWiki web application vulnerabilities
- Authentication and authorization issues
- Data exposure vulnerabilities
- Injection vulnerabilities (SQL, XSS, etc.)
- CSRF vulnerabilities
- Server-side request forgery (SSRF)
- Insecure direct object references
### Out of Scope
The following are out of scope:
- Denial of service attacks
- Social engineering attacks
- Physical security issues
- Issues in third-party applications or services
- Issues requiring physical access to a user's device
- Vulnerabilities in outdated versions
## Security Measures
ThrillWiki implements the following security measures:
- HTTPS enforcement with HSTS
- Content Security Policy
- XSS protection with input sanitization
- CSRF protection
- SQL injection prevention via ORM
- Rate limiting on authentication endpoints
- Secure session management
- JWT token rotation and blacklisting
For more details, see [docs/SECURITY.md](../docs/SECURITY.md).
## Security Updates
Security updates are released as soon as possible after a vulnerability is confirmed. We recommend:
1. Keep your installation up to date
2. Subscribe to release notifications
3. Review security advisories
## Contact
For security-related inquiries, please contact the project maintainers.

22
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,22 @@
version: 2
updates:
# Python dependencies
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "weekly"
commit-message:
prefix: "[DEPENDABOT] Update"
open-pull-requests-limit: 10
labels:
- "dependencies"
reviewers:
- "pacnpal"
# GitHub Actions dependencies
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
commit-message:
prefix: "[DEPENDABOT] Update Actions"

View File

@@ -0,0 +1,54 @@
name: Claude Code Review
on:
pull_request:
types: [opened, synchronize]
# Optional: Only run on specific file changes
# paths:
# - "src/**/*.ts"
# - "src/**/*.tsx"
# - "src/**/*.js"
# - "src/**/*.jsx"
jobs:
claude-review:
# Optional: Filter by PR author
# if: |
# github.event.pull_request.user.login == 'external-contributor' ||
# github.event.pull_request.user.login == 'new-developer' ||
# github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR'
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
issues: read
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
fetch-depth: 1
- name: Run Claude Code Review
id: claude-review
uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
prompt: |
Please review this pull request and provide feedback on:
- Code quality and best practices
- Potential bugs or issues
- Performance considerations
- Security concerns
- Test coverage
Use the repository's CLAUDE.md for guidance on style and conventions. Be constructive and helpful in your feedback.
Use `gh pr comment` with your Bash tool to leave your review as a comment on the PR.
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
# or https://docs.anthropic.com/en/docs/claude-code/sdk#command-line for available options
claude_args: '--allowed-tools "Bash(gh issue view:*),Bash(gh search:*),Bash(gh issue list:*),Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh pr list:*)"'

50
.github/workflows/claude.yml vendored Normal file
View File

@@ -0,0 +1,50 @@
name: Claude Code
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
issues:
types: [opened, assigned]
pull_request_review:
types: [submitted]
jobs:
claude:
if: |
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
issues: read
id-token: write
actions: read # Required for Claude to read CI results on PRs
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
fetch-depth: 1
- name: Run Claude Code
id: claude
uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
# This is an optional setting that allows Claude to read CI results on PRs
additional_permissions: |
actions: read
# Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it.
# prompt: 'Update the pull request description to include a summary of changes.'
# Optional: Add claude_args to customize behavior and configuration
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
# or https://docs.anthropic.com/en/docs/claude-code/sdk#command-line for available options
# claude_args: '--model claude-opus-4-1-20250805 --allowed-tools Bash(gh pr:*)'

53
.github/workflows/dependency-update.yml vendored Normal file
View File

@@ -0,0 +1,53 @@
name: Dependency Update Check
on:
schedule:
- cron: '0 0 * * 1' # Weekly on Monday at midnight UTC
workflow_dispatch:
jobs:
update:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.13"
- name: Install UV
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- name: Update Dependencies
working-directory: backend
run: |
uv lock --upgrade
uv sync
- name: Run Tests
working-directory: backend
run: |
uv run manage.py test
- name: Create Pull Request
uses: peter-evans/create-pull-request@v8
with:
commit-message: "chore: update dependencies"
title: "chore: weekly dependency updates"
body: |
Automated dependency updates.
This PR was automatically generated by the dependency update workflow.
## Changes
- Updated `uv.lock` with latest compatible versions
## Checklist
- [ ] Review dependency changes
- [ ] Verify all tests pass
- [ ] Check for breaking changes
branch: "dependency-updates"
labels: dependencies

96
.github/workflows/django.yml vendored Normal file
View File

@@ -0,0 +1,96 @@
name: Django CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
python-version: ["3.13"]
services:
postgres:
image: postgis/postgis:16-3.4
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: test_thrillwiki
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
# Services only run on Linux runners
if: runner.os == 'Linux'
steps:
- uses: actions/checkout@v6
- name: Install Homebrew on Linux
if: runner.os == 'Linux'
run: |
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
echo "/home/linuxbrew/.linuxbrew/bin" >> $GITHUB_PATH
- name: Install GDAL with Homebrew
run: brew install gdal
- name: Install PostGIS on macOS
if: runner.os == 'macOS'
run: |
brew install postgresql@16 postgis
brew services start postgresql@16
sleep 5
/opt/homebrew/opt/postgresql@16/bin/createuser -s postgres || true
/opt/homebrew/opt/postgresql@16/bin/createdb -U postgres test_thrillwiki || true
/opt/homebrew/opt/postgresql@16/bin/psql -U postgres -d test_thrillwiki -c "CREATE EXTENSION IF NOT EXISTS postgis;" || true
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
- name: Install UV
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- name: Cache UV dependencies
uses: actions/cache@v5
with:
path: ~/.cache/uv
key: ${{ runner.os }}-uv-${{ hashFiles('backend/pyproject.toml') }}
restore-keys: |
${{ runner.os }}-uv-
- name: Install Dependencies
working-directory: backend
run: |
uv sync --frozen
- name: Security Audit
working-directory: backend
run: |
uv pip install pip-audit
uv run pip-audit || true
continue-on-error: true
- name: Run Tests
working-directory: backend
env:
DJANGO_SETTINGS_MODULE: config.django.test
TEST_DB_NAME: test_thrillwiki
TEST_DB_USER: postgres
TEST_DB_PASSWORD: postgres
TEST_DB_HOST: localhost
TEST_DB_PORT: 5432
run: |
uv run python manage.py test --settings=config.django.test --parallel

34
.github/workflows/review.yml vendored Normal file
View File

@@ -0,0 +1,34 @@
name: Claude Code Review
permissions:
contents: read
pull-requests: write
on:
# Run on new/updated PRs
pull_request:
types: [opened, reopened, synchronize]
# Allow manual triggers for existing PRs
workflow_dispatch:
inputs:
pr_number:
description: 'Pull Request Number'
required: true
type: string
jobs:
code-review:
runs-on: ubuntu-latest
environment: development_environment
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Run Claude Review
uses: pacnpal/claude-code-review@main
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
anthropic-key: ${{ secrets.ANTHROPIC_API_KEY }}
pr-number: ${{ github.event.pull_request.number || inputs.pr_number }}

175
.gitignore vendored
View File

@@ -1,34 +1,143 @@
/.vscode # Python
/dev.sh __pycache__/
/flake.nix *.py[cod]
venv *$py.class
/venv *.so
./venv .Python
venv/sour build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# Django
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
/backend/staticfiles/
/backend/media/
# Celery Beat schedule database (runtime state, regenerated automatically)
celerybeat-schedule*
celerybeat.pid
# UV
.uv/
backend/.uv/
# Generated requirements files (auto-generated from pyproject.toml)
# Uncomment if you want to track these files
# backend/requirements.txt
# backend/requirements-dev.txt
# backend/requirements-test.txt
# Node.js
node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
.pnpm-store/
# Vue.js / Vite
/frontend/dist/
/frontend/dist-ssr/
*.local
# Environment variables
.env
.env.local
.env.development.local
.env.test.local
.env.production.local
backend/.env
frontend/.env
# IDEs
.vscode/
.idea/
*.swp
*.swo
*.sublime-project
*.sublime-workspace
# OS
.DS_Store .DS_Store
.DS_Store Thumbs.db
.DS_Store Desktop.ini
accounts/__pycache__/
__pycache__ # Logs
thrillwiki/__pycache__ logs/
reviews/__pycache__ *.log
parks/__pycache__
media/__pycache__ # Coverage
email_service/__pycache__ coverage/
core/__pycache__ *.lcov
companies/__pycache__ .nyc_output
accounts/__pycache__ htmlcov/
venv .coverage
accounts/__pycache__ .coverage.*
thrillwiki/__pycache__/settings.cpython-311.pyc
accounts/migrations/__pycache__/__init__.cpython-311.pyc # Testing
accounts/migrations/__pycache__/0001_initial.cpython-311.pyc .pytest_cache/
companies/migrations/__pycache__ .cache
moderation/__pycache__
rides/__pycache__ # Temporary files
ssh_tools.jsonc tmp/
thrillwiki/__pycache__/settings.cpython-312.pyc temp/
parks/__pycache__/views.cpython-312.pyc *.tmp
.venv/lib/python3.12/site-packages *.temp
thrillwiki/__pycache__/urls.cpython-312.pyc
thrillwiki/__pycache__/views.cpython-312.pyc # Build outputs
/dist/
/build/
# Backup files
*.bak
*.backup
*.orig
*.swp
*_backup.*
*_OLD_*
# Archive files
*.tar.gz
*.zip
*.rar
# Security
*.pem
*.key
*.cert
# Local development
/uploads/
/backups/
.django_tailwind_cli/
backend/.env
frontend/.env
# Extracted packages
django-forwardemail/
frontend/
frontend
.snapshots
web/next-env.d.ts
web/.next/types/cache-life.d.ts
.gitignore
web/.next/types/routes.d.ts
web/.next/types/validator.ts

251
.pylintrc Normal file
View File

@@ -0,0 +1,251 @@
# =============================================================================
# ThrillWiki Django Project - Pylint Configuration
# =============================================================================
#
# Purpose: Django-aware Pylint configuration that suppresses false positives
# while maintaining code quality standards.
#
# Alignment:
# - Line length: 120 characters (matches Black and Ruff in pyproject.toml)
# - Django version: 5.2.8
#
# Key Features:
# - Suppresses false positives for Django ORM patterns (.objects, _meta, .DoesNotExist)
# - Whitelists Django management command styling (self.style.SUCCESS, ERROR, etc.)
# - Accommodates Django REST Framework patterns
# - Allows django-fsm state machine patterns
#
# Maintenance:
# - Review when upgrading Django or adding new dynamic attribute patterns
# - Keep line-length aligned with Black/Ruff settings in pyproject.toml
#
# =============================================================================
[MASTER]
# Use all available CPU cores for faster linting
jobs=0
# Directories and files to exclude from linting
ignore=.git,__pycache__,.venv,venv,migrations,node_modules,.tox,.pytest_cache,build,dist
# File patterns to ignore (e.g., Emacs backup files)
ignore-patterns=^\.#
# Pickle collected data for faster subsequent runs
persistent=yes
# =============================================================================
# [MESSAGES CONTROL]
# Disable checks that conflict with Django patterns and conventions
# =============================================================================
[MESSAGES CONTROL]
disable=
# C0114: missing-module-docstring
# Django apps often don't need module docstrings; the app's purpose is
# typically documented in apps.py or README
C0114,
# C0115: missing-class-docstring
# Django models, forms, and serializers are often self-documenting through
# their field definitions and Meta classes
C0115,
# C0116: missing-function-docstring
# Allow simple functions and methods without docstrings; Django views and
# model methods are often self-explanatory
C0116,
# C0103: invalid-name
# Django uses non-PEP8 names by convention (e.g., 'pk', 'id', 'qs')
# and single-letter variables in comprehensions are acceptable
C0103,
# C0411: wrong-import-order
# Let isort/ruff handle import ordering; they have Django-specific rules
C0411,
# C0415: import-outside-toplevel
# Django often requires lazy imports to avoid circular dependencies,
# especially in models.py and signals
C0415,
# W0212: protected-access
# Django extensively uses _meta for model introspection; this is documented
# and supported API: https://docs.djangoproject.com/en/5.2/ref/models/meta/
W0212,
# W0613: unused-argument
# Django views, signals, and receivers often have unused parameters that
# are required by the framework's signature (e.g., request, sender, **kwargs)
W0613,
# R0903: too-few-public-methods
# Django models, forms, and serializers can be simple data containers
# with few or no methods beyond __str__
R0903,
# R0801: duplicate-code
# Django patterns naturally duplicate across apps (e.g., CRUD views,
# model patterns); this is intentional for consistency
R0801,
# E1101: no-member
# Main source of false positives for Django's dynamic attributes:
# - Model.objects (Manager)
# - Model.DoesNotExist / MultipleObjectsReturned (exceptions)
# - self.style.SUCCESS/ERROR (management commands)
# - model._meta (Options)
E1101
# =============================================================================
# [TYPECHECK]
# Whitelist Django's dynamically generated attributes
# =============================================================================
[TYPECHECK]
# Django generates many attributes dynamically that Pylint cannot detect
# statically. This list covers common patterns:
#
# - objects.* : Django ORM Manager methods (all, filter, get, create, etc.)
# - DoesNotExist : Exception raised when Model.objects.get() finds nothing
# - MultipleObjectsReturned : Exception when get() finds multiple objects
# - _meta.* : Django model metadata API (fields, app_label, model_name)
# - style.* : Django management command styling (SUCCESS, ERROR, WARNING, NOTICE)
# - id, pk : Django auto-generated primary key fields
# - REQUEST : Django request object attributes
# - aq_* : Acquisition attributes (Zope/Plone compatibility)
# - acl_users : Zope/Plone user folder
#
generated-members=
REQUEST,
acl_users,
aq_parent,
aq_inner,
aq_explicit,
aq_acquire,
aq_base,
objects,
objects.*,
DoesNotExist,
MultipleObjectsReturned,
_meta,
_meta.*,
style,
style.*,
id,
pk
# =============================================================================
# [FORMAT]
# Code formatting settings - aligned with Black and Ruff (120 chars)
# =============================================================================
[FORMAT]
# Maximum line length - matches Black and Ruff configuration in pyproject.toml
max-line-length=120
# Use 4 spaces for indentation (Python standard)
indent-string=' '
# Use Unix line endings (LF)
expected-line-ending-format=LF
# =============================================================================
# [BASIC]
# Naming conventions and allowed short names
# =============================================================================
[BASIC]
# Short variable names commonly used in Django and Python
# - i, j, k : Loop counters
# - ex : Exception variable
# - Run : Django command method
# - _ : Throwaway variable
# - id, pk : Primary key (Django convention)
# - qs : QuerySet abbreviation
good-names=i,j,k,ex,Run,_,id,pk,qs
# Enforce snake_case for most identifiers (Python/Django convention)
argument-naming-style=snake_case
attr-naming-style=snake_case
function-naming-style=snake_case
method-naming-style=snake_case
module-naming-style=snake_case
variable-naming-style=snake_case
# PascalCase for classes
class-naming-style=PascalCase
# UPPER_CASE for constants
const-naming-style=UPPER_CASE
# =============================================================================
# [DESIGN]
# Complexity thresholds - relaxed for Django patterns
# =============================================================================
[DESIGN]
# Django views and forms often need many arguments
max-args=7
# Django models can have many fields
max-attributes=12
# Allow complex boolean expressions
max-bool-expr=5
# Django views can have complex branching logic
max-branches=15
# Django views often have many local variables
max-locals=20
# Django uses multiple inheritance (Model, Mixin classes)
max-parents=7
# Django models and viewsets have many built-in methods
max-public-methods=25
# Allow multiple return statements
max-returns=6
# Django views can be lengthy
max-statements=60
# Allow simple classes with no methods (e.g., Django Meta classes)
min-public-methods=0
# =============================================================================
# [SIMILARITIES]
# Duplicate code detection settings
# =============================================================================
[SIMILARITIES]
# Increase threshold to reduce false positives from Django boilerplate
min-similarity-lines=6
# Don't flag similar comments
ignore-comments=yes
# Don't flag similar docstrings
ignore-docstrings=yes
# Don't flag similar import blocks
ignore-imports=yes
# =============================================================================
# [VARIABLES]
# Variable naming patterns
# =============================================================================
[VARIABLES]
# Patterns for dummy/unused variables
dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_
# Arguments that are commonly unused but required by framework signatures
ignored-argument-names=_.*|^ignored_|^unused_|args|kwargs|request|pk
# =============================================================================
# [IMPORTS]
# Import checking settings
# =============================================================================
[IMPORTS]
# Don't allow wildcard imports even with __all__ defined
allow-wildcard-with-all=no
# Don't analyze fallback import blocks
analyse-fallback-blocks=no

18
.roo/mcp.json Normal file
View File

@@ -0,0 +1,18 @@
{
"mcpServers": {
"context7": {
"command": "npx",
"args": [
"-y",
"@upstash/context7-mcp"
],
"env": {
"DEFAULT_MINIMUM_TOKENS": ""
},
"alwaysAllow": [
"resolve-library-id",
"get-library-docs"
]
}
}
}

View File

@@ -0,0 +1,2 @@
## CRITICAL: Centralized API Structure
All API endpoints MUST be centralized under the `backend/apps/api/v1/` structure. This is NON-NEGOTIABLE.

49
.roo/rules/critical_rules Normal file
View File

@@ -0,0 +1,49 @@
# Project Startup & Development Rules
## Server & Package Management
- **Starting the Dev Server:** Always assume the server is running and changes have taken effect. If issues arise, run:
```bash
$PROJECT_ROOT/shared/scripts/start-servers.sh
```
- **Python Packages:** Only use UV to add packages:
```bash
cd $PROJECT_ROOT/backend && uv add <package>
```
- **Django Commands:** Always use `cd backend && uv run manage.py <command>` for all management tasks (migrations, shell, superuser, etc.). Never use `python manage.py` or `uv run python manage.py`.
## CRITICAL Frontend design rules
- EVERYTHING must support both dark and light mode.
- Make sure the light/dark mode toggle works with the Vue components and pages.
- Leverage Tailwind CSS 4 and Shadcn UI components.
## Frontend API URL Rules
- **Vite Proxy:** Always check `frontend/vite.config.ts` for proxy rules before changing frontend API URLs.
- **URL Flow:** Understand how frontend URLs are rewritten by Vite proxy (e.g., `/api/auth/login/` → `/api/v1/auth/login/`).
- **Verification:** Confirm proxy behavior via config and browser network tab. Only change URLs if proxy is NOT handling rewriting.
- **Common Mistake:** Dont assume frontend URLs are wrong due to proxy configuration.
## Entity Relationship Patterns
- **Park:** Must have Operator (required), may have PropertyOwner (optional), cannot reference Company directly.
- **Ride:** Must belong to Park, may have Manufacturer/Designer (optional), cannot reference Company directly.
- **Entities:**
- Operators: Operate parks.
- PropertyOwners: Own park property (optional).
- Manufacturers: Make rides.
- Designers: Design rides.
- All entities can have locations.
- **Constraints:** Operator and PropertyOwner can be same or different. Manufacturers and Designers are distinct. Use proper foreign keys with correct null/blank settings.
## General Best Practices
- Never assume blank output means success—always verify changes by testing.
- Use context7 for documentation when troubleshooting.
- Document changes with conport and reasoning.
- Include relevant context and information in all changes.
- Test and validate code before deployment.
- Communicate changes clearly with your team.
- Be open to feedback and continuous improvement.
- Prioritize readability, maintainability, security, performance, scalability, and modularity.
- Use meaningful names, DRY principles, clear comments, and handle errors gracefully.
- Log important events/errors for troubleshooting.
- Prefer existing modules/packages over new code.
- Keep documentation up to date.
- Consider security vulnerabilities and performance bottlenecks in all changes.

View File

@@ -0,0 +1,390 @@
# --- ConPort Memory Strategy ---
conport_memory_strategy:
# CRITICAL: At the beginning of every session, the agent MUST execute the 'initialization' sequence
# to determine the ConPort status and load relevant context.
workspace_id_source: "The agent must obtain the absolute path to the current workspace to use as `workspace_id` for all ConPort tool calls. This might be available as `${workspaceFolder}` or require asking the user."
initialization:
thinking_preamble: |
agent_action_plan:
- step: 1
action: "Determine `ACTUAL_WORKSPACE_ID`."
- step: 2
action: "Invoke `list_files` for `ACTUAL_WORKSPACE_ID + \"/context_portal/\"`."
tool_to_use: "list_files"
parameters: "path: ACTUAL_WORKSPACE_ID + \"/context_portal/\""
- step: 3
action: "Analyze result and branch based on 'context.db' existence."
conditions:
- if: "'context.db' is found"
then_sequence: "load_existing_conport_context"
- else: "'context.db' NOT found"
then_sequence: "handle_new_conport_setup"
load_existing_conport_context:
thinking_preamble: |
agent_action_plan:
- step: 1
description: "Attempt to load initial contexts from ConPort."
actions:
- "Invoke `get_product_context`... Store result."
- "Invoke `get_active_context`... Store result."
- "Invoke `get_decisions` (limit 5 for a better overview)... Store result."
- "Invoke `get_progress` (limit 5)... Store result."
- "Invoke `get_system_patterns` (limit 5)... Store result."
- "Invoke `get_custom_data` (category: \"critical_settings\")... Store result."
- "Invoke `get_custom_data` (category: \"ProjectGlossary\")... Store result."
- "Invoke `get_recent_activity_summary` (default params, e.g., last 24h, limit 3 per type) for a quick catch-up. Store result."
- step: 2
description: "Analyze loaded context."
conditions:
- if: "results from step 1 are NOT empty/minimal"
actions:
- "Set internal status to [CONPORT_ACTIVE]."
- "Inform user: \"ConPort memory initialized. Existing contexts and recent activity loaded.\""
- "Use `ask_followup_question` with suggestions like \"Review recent activity?\", \"Continue previous task?\", \"What would you like to work on?\"."
- else: "loaded context is empty/minimal despite DB file existing"
actions:
- "Set internal status to [CONPORT_ACTIVE]."
- "Inform user: \"ConPort database file found, but it appears to be empty or minimally initialized. You can start by defining Product/Active Context or logging project information.\""
- "Use `ask_followup_question` with suggestions like \"Define Product Context?\", \"Log a new decision?\"."
- step: 3
description: "Handle Load Failure (if step 1's `get_*` calls failed)."
condition: "If any `get_*` calls in step 1 failed unexpectedly"
action: "Fall back to `if_conport_unavailable_or_init_failed`."
handle_new_conport_setup:
thinking_preamble: |
agent_action_plan:
- step: 1
action: "Inform user: \"No existing ConPort database found at `ACTUAL_WORKSPACE_ID + \"/context_portal/context.db\"`.\""
- step: 2
action: "Use `ask_followup_question`."
tool_to_use: "ask_followup_question"
parameters:
question: "Would you like to initialize a new ConPort database for this workspace? The database will be created automatically when ConPort tools are first used."
suggestions:
- "Yes, initialize a new ConPort database."
- "No, do not use ConPort for this session."
- step: 3
description: "Process user response."
conditions:
- if_user_response_is: "Yes, initialize a new ConPort database."
actions:
- "Inform user: \"Okay, a new ConPort database will be created.\""
- description: "Attempt to bootstrap Product Context from projectBrief.md (this happens only on new setup)."
thinking_preamble: |
sub_steps:
- "Invoke `list_files` with `path: ACTUAL_WORKSPACE_ID` (non-recursive, just to check root)."
- description: "Analyze `list_files` result for 'projectBrief.md'."
conditions:
- if: "'projectBrief.md' is found in the listing"
actions:
- "Invoke `read_file` for `ACTUAL_WORKSPACE_ID + \"/projectBrief.md\"`."
- action: "Use `ask_followup_question`."
tool_to_use: "ask_followup_question"
parameters:
question: "Found projectBrief.md in your workspace. As we're setting up ConPort for the first time, would you like to import its content into the Product Context?"
suggestions:
- "Yes, import its content now."
- "No, skip importing it for now."
- description: "Process user response to import projectBrief.md."
conditions:
- if_user_response_is: "Yes, import its content now."
actions:
- "(No need to `get_product_context` as DB is new and empty)"
- "Prepare `content` for `update_product_context`. For example: `{\"initial_product_brief\": \"[content from projectBrief.md]\"}`."
- "Invoke `update_product_context` with the prepared content."
- "Inform user of the import result (success or failure)."
- else: "'projectBrief.md' NOT found"
actions:
- action: "Use `ask_followup_question`."
tool_to_use: "ask_followup_question"
parameters:
question: "`projectBrief.md` was not found in the workspace root. Would you like to define the initial Product Context manually now?"
suggestions:
- "Define Product Context manually."
- "Skip for now."
- "(If \"Define manually\", guide user through `update_product_context`)."
- "Proceed to 'load_existing_conport_context' sequence (which will now load the potentially bootstrapped product context and other empty contexts)."
- if_user_response_is: "No, do not use ConPort for this session."
action: "Proceed to `if_conport_unavailable_or_init_failed` (with a message indicating user chose not to initialize)."
if_conport_unavailable_or_init_failed:
thinking_preamble: |
agent_action: "Inform user: \"ConPort memory will not be used for this session. Status: [CONPORT_INACTIVE].\""
general:
status_prefix: "Begin EVERY response with either '[CONPORT_ACTIVE]' or '[CONPORT_INACTIVE]'."
proactive_logging_cue: "Remember to proactively identify opportunities to log or update ConPort based on the conversation (e.g., if user outlines a new plan, consider logging decisions or progress). Confirm with the user before logging."
proactive_error_handling: "When encountering errors (e.g., tool failures, unexpected output), proactively log the error details using `log_custom_data` (category: 'ErrorLogs', key: 'timestamp_error_summary') and consider updating `active_context` with `open_issues` if it's a persistent problem. Prioritize using ConPort's `get_item_history` or `get_recent_activity_summary` to diagnose issues if they relate to past context changes."
semantic_search_emphasis: "For complex or nuanced queries, especially when direct keyword search (`search_decisions_fts`, `search_custom_data_value_fts`) might be insufficient, prioritize using `semantic_search_conport` to leverage conceptual understanding and retrieve more relevant context. Explain to the user why semantic search is being used."
conport_updates:
frequency: "UPDATE CONPORT THROUGHOUT THE CHAT SESSION, WHEN SIGNIFICANT CHANGES OCCUR, OR WHEN EXPLICITLY REQUESTED."
workspace_id_note: "All ConPort tool calls require the `workspace_id`."
tools:
- name: get_product_context
trigger: "To understand the overall project goals, features, or architecture at any time."
action_description: |
# Agent Action: Invoke `get_product_context` (`{"workspace_id": "..."}`). Result is a direct dictionary.
- name: update_product_context
trigger: "When the high-level project description, goals, features, or overall architecture changes significantly, as confirmed by the user."
action_description: |
<thinking>
- Product context needs updating.
- Step 1: (Optional but recommended if unsure of current state) Invoke `get_product_context`.
- Step 2: Prepare the `content` (for full overwrite) or `patch_content` (partial update) dictionary.
- To remove a key using `patch_content`, set its value to the special string sentinel `\"__DELETE__\"`.
- Confirm changes with the user.
</thinking>
# Agent Action: Invoke `update_product_context` (`{"workspace_id": "...", "content": {...}}` or `{"workspace_id": "...", "patch_content": {"key_to_update": "new_value", "key_to_delete": "__DELETE__"}}`).
- name: get_active_context
trigger: "To understand the current task focus, immediate goals, or session-specific context."
action_description: |
# Agent Action: Invoke `get_active_context` (`{"workspace_id": "..."}`). Result is a direct dictionary.
- name: update_active_context
trigger: "When the current focus of work changes, new questions arise, or session-specific context needs updating (e.g., `current_focus`, `open_issues`), as confirmed by the user."
action_description: |
<thinking>
- Active context needs updating.
- Step 1: (Optional) Invoke `get_active_context` to retrieve the current state.
- Step 2: Prepare `content` (for full overwrite) or `patch_content` (for partial update).
- Common fields to update include `current_focus`, `open_issues`, and other session-specific data.
- To remove a key using `patch_content`, set its value to the special string sentinel `\"__DELETE__\"`.
- Confirm changes with the user.
</thinking>
# Agent Action: Invoke `update_active_context` (`{"workspace_id": "...", "content": {...}}` or `{"workspace_id": "...", "patch_content": {"current_focus": "new_focus", "open_issues": ["issue1", "issue2"], "key_to_delete": "__DELETE__"}}`).
- name: log_decision
trigger: "When a significant architectural or implementation decision is made and confirmed by the user."
action_description: |
# Agent Action: Invoke `log_decision` (`{"workspace_id": "...", "summary": "...", "rationale": "...", "tags": ["optional_tag"]}}`).
- name: get_decisions
trigger: "To retrieve a list of past decisions, e.g., to review history or find a specific decision."
action_description: |
# Agent Action: Invoke `get_decisions` (`{"workspace_id": "...", "limit": N, "tags_filter_include_all": ["tag1"], "tags_filter_include_any": ["tag2"]}}`). Explain optional filters.
- name: search_decisions_fts
trigger: "When searching for decisions by keywords in summary, rationale, details, or tags, and basic `get_decisions` is insufficient."
action_description: |
# Agent Action: Invoke `search_decisions_fts` (`{"workspace_id": "...", "query_term": "search keywords", "limit": N}}`).
- name: delete_decision_by_id
trigger: "When user explicitly confirms deletion of a specific decision by its ID."
action_description: |
# Agent Action: Invoke `delete_decision_by_id` (`{"workspace_id": "...", "decision_id": ID}}`). Emphasize prior confirmation.
- name: log_progress
trigger: "When a task begins, its status changes (e.g., TODO, IN_PROGRESS, DONE), or it's completed. Also when a new sub-task is defined."
action_description: |
# Agent Action: Invoke `log_progress` (`{"workspace_id": "...", "description": "...", "status": "...", "linked_item_type": "...", "linked_item_id": "..."}}`). Note: 'summary' was changed to 'description' for log_progress.
- name: get_progress
trigger: "To review current task statuses, find pending tasks, or check history of progress."
action_description: |
# Agent Action: Invoke `get_progress` (`{"workspace_id": "...", "status_filter": "...", "parent_id_filter": ID, "limit": N}}`).
- name: update_progress
trigger: "Updates an existing progress entry."
action_description: |
# Agent Action: Invoke `update_progress` (`{"workspace_id": "...", "progress_id": ID, "status": "...", "description": "...", "parent_id": ID}}`).
- name: delete_progress_by_id
trigger: "Deletes a progress entry by its ID."
action_description: |
# Agent Action: Invoke `delete_progress_by_id` (`{"workspace_id": "...", "progress_id": ID}}`).
- name: log_system_pattern
trigger: "When new architectural patterns are introduced, or existing ones are modified, as confirmed by the user."
action_description: |
# Agent Action: Invoke `log_system_pattern` (`{"workspace_id": "...", "name": "...", "description": "...", "tags": ["optional_tag"]}}`).
- name: get_system_patterns
trigger: "To retrieve a list of defined system patterns."
action_description: |
# Agent Action: Invoke `get_system_patterns` (`{"workspace_id": "...", "tags_filter_include_all": ["tag1"], "limit": N}}`). Note: limit was not in original example, added for consistency.
- name: delete_system_pattern_by_id
trigger: "When user explicitly confirms deletion of a specific system pattern by its ID."
action_description: |
# Agent Action: Invoke `delete_system_pattern_by_id` (`{"workspace_id": "...", "pattern_id": ID}}`). Emphasize prior confirmation.
- name: log_custom_data
trigger: "To store any other type of structured or unstructured project-related information not covered by other tools (e.g., glossary terms, technical specs, meeting notes), as confirmed by the user."
action_description: |
# Agent Action: Invoke `log_custom_data` (`{"workspace_id": "...", "category": "...", "key": "...", "value": {... or "string"}}`). Note: 'metadata' field is not part of log_custom_data args.
- name: get_custom_data
trigger: "To retrieve specific custom data by category and key."
action_description: |
# Agent Action: Invoke `get_custom_data` (`{"workspace_id": "...", "category": "...", "key": "..."}}`).
- name: delete_custom_data
trigger: "When user explicitly confirms deletion of specific custom data by category and key."
action_description: |
# Agent Action: Invoke `delete_custom_data` (`{"workspace_id": "...", "category": "...", "key": "..."}}`). Emphasize prior confirmation.
- name: search_custom_data_value_fts
trigger: "When searching for specific terms within any custom data values, categories, or keys."
action_description: |
# Agent Action: Invoke `search_custom_data_value_fts` (`{"workspace_id": "...", "query_term": "...", "category_filter": "...", "limit": N}}`).
- name: search_project_glossary_fts
trigger: "When specifically searching for terms within the 'ProjectGlossary' custom data category."
action_description: |
# Agent Action: Invoke `search_project_glossary_fts` (`{"workspace_id": "...", "query_term": "...", "limit": N}}`).
- name: semantic_search_conport
trigger: "When a natural language query requires conceptual understanding beyond keyword matching, or when direct keyword searches are insufficient."
action_description: |
# Agent Action: Invoke `semantic_search_conport` (`{"workspace_id": "...", "query_text": "...", "top_k": N, "filter_item_types": ["decision", "custom_data"]}}`). Explain filters.
- name: link_conport_items
trigger: "When a meaningful relationship is identified and confirmed between two existing ConPort items (e.g., a decision is implemented by a system pattern, a progress item tracks a decision)."
action_description: |
<thinking>
- Need to link two items. Identify source type/ID, target type/ID, and relationship.
- Common relationship_types: 'implements', 'related_to', 'tracks', 'blocks', 'clarifies', 'depends_on'. Propose a suitable one or ask user.
</thinking>
# Agent Action: Invoke `link_conport_items` (`{"workspace_id":"...", "source_item_type":"...", "source_item_id":"...", "target_item_type":"...", "target_item_id":"...", "relationship_type":"...", "description":"Optional notes"}`).
- name: get_linked_items
trigger: "To understand the relationships of a specific ConPort item, or to explore the knowledge graph around an item."
action_description: |
# Agent Action: Invoke `get_linked_items` (`{"workspace_id":"...", "item_type":"...", "item_id":"...", "relationship_type_filter":"...", "linked_item_type_filter":"...", "limit":N}`).
- name: get_item_history
trigger: "When needing to review past versions of Product Context or Active Context, or to see when specific changes were made."
action_description: |
# Agent Action: Invoke `get_item_history` (`{"workspace_id":"...", "item_type":"product_context" or "active_context", "limit":N, "version":V, "before_timestamp":"ISO_DATETIME", "after_timestamp":"ISO_DATETIME"}`).
- name: batch_log_items
trigger: "When the user provides a list of multiple items of the SAME type (e.g., several decisions, multiple new glossary terms) to be logged at once."
action_description: |
<thinking>
- User provided multiple items. Verify they are of the same loggable type.
- Construct the `items` list, where each element is a dictionary of arguments for the single-item log tool (e.g., for `log_decision`).
</thinking>
# Agent Action: Invoke `batch_log_items` (`{"workspace_id":"...", "item_type":"decision", "items": [{"summary":"...", "rationale":"..."}, {"summary":"..."}] }`).
- name: get_recent_activity_summary
trigger: "At the start of a new session to catch up, or when the user asks for a summary of recent project activities."
action_description: |
# Agent Action: Invoke `get_recent_activity_summary` (`{"workspace_id":"...", "hours_ago":H, "since_timestamp":"ISO_DATETIME", "limit_per_type":N}`). Explain default if no time args.
- name: get_conport_schema
trigger: "If there's uncertainty about available ConPort tools or their arguments during a session (internal LLM check), or if an advanced user specifically asks for the server's tool schema."
action_description: |
# Agent Action: Invoke `get_conport_schema` (`{"workspace_id":"..."}`). Primarily for internal LLM reference or direct user request.
- name: export_conport_to_markdown
trigger: "When the user requests to export the current ConPort data to markdown files (e.g., for backup, sharing, or version control)."
action_description: |
# Agent Action: Invoke `export_conport_to_markdown` (`{"workspace_id":"...", "output_path":"optional/relative/path"}`). Explain default output path if not provided.
- name: import_markdown_to_conport
trigger: "When the user requests to import ConPort data from a directory of markdown files previously exported by this system."
action_description: |
# Agent Action: Invoke `import_markdown_to_conport` (`{"workspace_id":"...", "input_path":"optional/relative/path"}`). Explain default input path. Warn about potential overwrites or merges if data already exists.
- name: reconfigure_core_guidance
type: guidance
product_active_context: "The internal JSON structure of 'Product Context' and 'Active Context' (the `content` field) is flexible. Work with the user to define and evolve this structure via `update_product_context` and `update_active_context`. The server stores this `content` as a JSON blob."
decisions_progress_patterns: "The fundamental fields for Decisions, Progress, and System Patterns are fixed by ConPort's tools. For significantly different structures or additional fields, guide the user to create a new custom context category using `log_custom_data` (e.g., category: 'project_milestones_detailed')."
conport_sync_routine:
trigger: "^(Sync ConPort|ConPort Sync)$"
user_acknowledgement_text: "[CONPORT_SYNCING]"
instructions:
- "Halt Current Task: Stop current activity."
- "Acknowledge Command: Send `[CONPORT_SYNCING]` to the user."
- "Review Chat History: Analyze the complete current chat session for new information, decisions, progress, context changes, clarifications, and potential new relationships between items."
core_update_process:
thinking_preamble: |
- Synchronize ConPort with information from the current chat session.
- Use appropriate ConPort tools based on identified changes.
- For `update_product_context` and `update_active_context`, first fetch current content, then merge/update (potentially using `patch_content`), then call the update tool with the *complete new content object* or the patch.
- All tool calls require the `workspace_id`.
agent_action_plan_illustrative:
- "Log new decisions (use `log_decision`)."
- "Log task progress/status changes (use `log_progress`)."
- "Update existing progress entries (use `update_progress`)."
- "Delete progress entries (use `delete_progress_by_id`)."
- "Log new system patterns (use `log_system_pattern`)."
- "Update Active Context (use `get_active_context` then `update_active_context` with full or patch)."
- "Update Product Context if significant changes (use `get_product_context` then `update_product_context` with full or patch)."
- "Log new custom context, including ProjectGlossary terms (use `log_custom_data`)."
- "Identify and log new relationships between items (use `link_conport_items`)."
- "If many items of the same type were discussed, consider `batch_log_items`."
- "After updates, consider a brief `get_recent_activity_summary` to confirm and refresh understanding."
post_sync_actions:
- "Inform user: ConPort synchronized with session info."
- "Resume previous task or await new instructions."
dynamic_context_retrieval_for_rag:
description: |
Guidance for dynamically retrieving and assembling context from ConPort to answer user queries or perform tasks,
enhancing Retrieval Augmented Generation (RAG) capabilities.
trigger: "When the AI needs to answer a specific question, perform a task requiring detailed project knowledge, or generate content based on ConPort data."
goal: "To construct a concise, highly relevant context set for the LLM, improving the accuracy and relevance of its responses."
steps:
- step: 1
action: "Analyze User Query/Task"
details: "Deconstruct the user's request to identify key entities, concepts, keywords, and the specific type of information needed from ConPort."
- step: 2
action: "Prioritized Retrieval Strategy"
details: |
Based on the analysis, select the most appropriate ConPort tools:
- **Targeted FTS:** Use `search_decisions_fts`, `search_custom_data_value_fts`, `search_project_glossary_fts` for keyword-based searches if specific terms are evident.
- **Specific Item Retrieval:** Use `get_custom_data` (if category/key known), `get_decisions` (by ID or for recent items), `get_system_patterns`, `get_progress` if the query points to specific item types or IDs.
- **(Future):** Prioritize semantic search tools once available for conceptual queries.
- **Broad Context (Fallback):** Use `get_product_context` or `get_active_context` as a fallback if targeted retrieval yields little, but be mindful of their size.
- step: 3
action: "Retrieve Initial Set"
details: "Execute the chosen tool(s) to retrieve an initial, small set (e.g., top 3-5) of the most relevant items or data snippets."
- step: 4
action: "Contextual Expansion (Optional)"
details: "For the most promising items from Step 3, consider using `get_linked_items` to fetch directly related items (1-hop). This can provide crucial context or disambiguation. Use judiciously to avoid excessive data."
- step: 5
action: "Synthesize and Filter"
details: |
Review the retrieved information (initial set + expanded context).
- **Filter:** Discard irrelevant items or parts of items.
- **Synthesize/Summarize:** If multiple relevant pieces of information are found, synthesize them into a concise summary that directly addresses the query/task. Extract only the most pertinent sentences or facts.
- step: 6
action: "Assemble Prompt Context"
details: |
Construct the context portion of the LLM prompt using the filtered and synthesized information.
- **Clarity:** Clearly delineate this retrieved context from the user's query or other parts of the prompt.
- **Attribution (Optional but Recommended):** If possible, briefly note the source of the information (e.g., "From Decision D-42:", "According to System Pattern SP-5:").
- **Brevity:** Strive for relevance and conciseness. Avoid including large, unprocessed chunks of data unless absolutely necessary and directly requested.
general_principles:
- "Prefer targeted retrieval over broad context dumps."
- "Iterate if initial retrieval is insufficient: try different keywords or tools."
- "Balance context richness with prompt token limits."
proactive_knowledge_graph_linking:
description: |
Guidance for the AI to proactively identify and suggest the creation of links between ConPort items,
enriching the project's knowledge graph based on conversational context.
trigger: "During ongoing conversation, when the AI observes potential relationships (e.g., causal, implementational, clarifying) between two or more discussed ConPort items or concepts that are likely represented as ConPort items."
goal: "To actively build and maintain a rich, interconnected knowledge graph within ConPort by capturing relationships that might otherwise be missed."
steps:
- step: 1
action: "Monitor Conversational Context"
details: "Continuously analyze the user's statements and the flow of discussion for mentions of ConPort items (explicitly by ID, or implicitly by well-known names/summaries) and the relationships being described or implied between them."
- step: 2
action: "Identify Potential Links"
details: |
Look for patterns such as:
- User states "Decision X led to us doing Y (which is Progress item P-3)."
- User discusses how System Pattern SP-2 helps address a concern noted in Decision D-5.
- User outlines a task (Progress P-10) that implements a specific feature detailed in a `custom_data` spec (CD-Spec-FeatureX).
- step: 3
action: "Formulate and Propose Link Suggestion"
details: |
If a potential link is identified:
- Clearly state the items involved (e.g., "Decision D-5", "System Pattern SP-2").
- Describe the perceived relationship (e.g., "It seems SP-2 addresses a concern in D-5.").
- Propose creating a link using `ask_followup_question`.
- Example Question: "I noticed we're discussing Decision D-5 and System Pattern SP-2. It sounds like SP-2 might 'address_concern_in' D-5. Would you like me to create this link in ConPort? You can also suggest a different relationship type."
- Suggested Answers:
- "Yes, link them with 'addresses_concern_in'."
- "Yes, but use relationship type: [user types here]."
- "No, don't link them now."
- Offer common relationship types as examples if needed: 'implements', 'clarifies', 'related_to', 'depends_on', 'blocks', 'resolves', 'derived_from'.
- step: 4
action: "Gather Details and Execute Linking"
details: |
If the user confirms:
- Ensure you have the correct source item type, source item ID, target item type, target item ID, and the agreed-upon relationship type.
- Ask for an optional brief description for the link if the relationship isn't obvious.
- Invoke the `link_conport_items` tool.
- step: 5
action: "Confirm Outcome"
details: "Inform the user of the success or failure of the `link_conport_items` tool call."
general_principles:
- "Be helpful, not intrusive. If the user declines a suggestion, accept and move on."
- "Prioritize clear, strong relationships over tenuous ones."
- "This strategy complements the general `proactive_logging_cue` by providing specific guidance for link creation."

35
.roomodes Normal file
View File

@@ -0,0 +1,35 @@
customModes:
- slug: project-research
name: 🔍 Project Research
roleDefinition: |
You are a detailed-oriented research assistant specializing in examining and understanding codebases. Your primary responsibility is to analyze the file structure, content, and dependencies of a given project to provide comprehensive context relevant to specific user queries.
whenToUse: |
Use this mode when you need to thoroughly investigate and understand a codebase structure, analyze project architecture, or gather comprehensive context about existing implementations. Ideal for onboarding to new projects, understanding complex codebases, or researching how specific features are implemented across the project.
description: Investigate and analyze codebase structure
groups:
- read
source: project
customInstructions: |
Your role is to deeply investigate and summarize the structure and implementation details of the project codebase. To achieve this effectively, you must:
1. Start by carefully examining the file structure of the entire project, with a particular emphasis on files located within the "docs" folder. These files typically contain crucial context, architectural explanations, and usage guidelines.
2. When given a specific query, systematically identify and gather all relevant context from:
- Documentation files in the "docs" folder that provide background information, specifications, or architectural insights.
- Relevant type definitions and interfaces, explicitly citing their exact location (file path and line number) within the source code.
- Implementations directly related to the query, clearly noting their file locations and providing concise yet comprehensive summaries of how they function.
- Important dependencies, libraries, or modules involved in the implementation, including their usage context and significance to the query.
3. Deliver a structured, detailed report that clearly outlines:
- An overview of relevant documentation insights.
- Specific type definitions and their exact locations.
- Relevant implementations, including file paths, functions or methods involved, and a brief explanation of their roles.
- Critical dependencies and their roles in relation to the query.
4. Always cite precise file paths, function names, and line numbers to enhance clarity and ease of navigation.
5. Organize your findings in logical sections, making it straightforward for the user to understand the project's structure and implementation status relevant to their request.
6. Ensure your response directly addresses the user's query and helps them fully grasp the relevant aspects of the project's current state.
These specific instructions supersede any conflicting general instructions you might otherwise follow. Your detailed report should enable effective decision-making and next steps within the overall workflow.

View File

@@ -1,247 +0,0 @@
<#
.Synopsis
Activate a Python virtual environment for the current PowerShell session.
.Description
Pushes the python executable for a virtual environment to the front of the
$Env:PATH environment variable and sets the prompt to signify that you are
in a Python virtual environment. Makes use of the command line switches as
well as the `pyvenv.cfg` file values present in the virtual environment.
.Parameter VenvDir
Path to the directory that contains the virtual environment to activate. The
default value for this is the parent of the directory that the Activate.ps1
script is located within.
.Parameter Prompt
The prompt prefix to display when this virtual environment is activated. By
default, this prompt is the name of the virtual environment folder (VenvDir)
surrounded by parentheses and followed by a single space (ie. '(.venv) ').
.Example
Activate.ps1
Activates the Python virtual environment that contains the Activate.ps1 script.
.Example
Activate.ps1 -Verbose
Activates the Python virtual environment that contains the Activate.ps1 script,
and shows extra information about the activation as it executes.
.Example
Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv
Activates the Python virtual environment located in the specified location.
.Example
Activate.ps1 -Prompt "MyPython"
Activates the Python virtual environment that contains the Activate.ps1 script,
and prefixes the current prompt with the specified string (surrounded in
parentheses) while the virtual environment is active.
.Notes
On Windows, it may be required to enable this Activate.ps1 script by setting the
execution policy for the user. You can do this by issuing the following PowerShell
command:
PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
For more information on Execution Policies:
https://go.microsoft.com/fwlink/?LinkID=135170
#>
Param(
[Parameter(Mandatory = $false)]
[String]
$VenvDir,
[Parameter(Mandatory = $false)]
[String]
$Prompt
)
<# Function declarations --------------------------------------------------- #>
<#
.Synopsis
Remove all shell session elements added by the Activate script, including the
addition of the virtual environment's Python executable from the beginning of
the PATH variable.
.Parameter NonDestructive
If present, do not remove this function from the global namespace for the
session.
#>
function global:deactivate ([switch]$NonDestructive) {
# Revert to original values
# The prior prompt:
if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) {
Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt
Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT
}
# The prior PYTHONHOME:
if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) {
Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME
Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME
}
# The prior PATH:
if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) {
Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH
Remove-Item -Path Env:_OLD_VIRTUAL_PATH
}
# Just remove the VIRTUAL_ENV altogether:
if (Test-Path -Path Env:VIRTUAL_ENV) {
Remove-Item -Path env:VIRTUAL_ENV
}
# Just remove VIRTUAL_ENV_PROMPT altogether.
if (Test-Path -Path Env:VIRTUAL_ENV_PROMPT) {
Remove-Item -Path env:VIRTUAL_ENV_PROMPT
}
# Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether:
if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) {
Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force
}
# Leave deactivate function in the global namespace if requested:
if (-not $NonDestructive) {
Remove-Item -Path function:deactivate
}
}
<#
.Description
Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the
given folder, and returns them in a map.
For each line in the pyvenv.cfg file, if that line can be parsed into exactly
two strings separated by `=` (with any amount of whitespace surrounding the =)
then it is considered a `key = value` line. The left hand string is the key,
the right hand is the value.
If the value starts with a `'` or a `"` then the first and last character is
stripped from the value before being captured.
.Parameter ConfigDir
Path to the directory that contains the `pyvenv.cfg` file.
#>
function Get-PyVenvConfig(
[String]
$ConfigDir
) {
Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg"
# Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue).
$pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue
# An empty map will be returned if no config file is found.
$pyvenvConfig = @{ }
if ($pyvenvConfigPath) {
Write-Verbose "File exists, parse `key = value` lines"
$pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath
$pyvenvConfigContent | ForEach-Object {
$keyval = $PSItem -split "\s*=\s*", 2
if ($keyval[0] -and $keyval[1]) {
$val = $keyval[1]
# Remove extraneous quotations around a string value.
if ("'""".Contains($val.Substring(0, 1))) {
$val = $val.Substring(1, $val.Length - 2)
}
$pyvenvConfig[$keyval[0]] = $val
Write-Verbose "Adding Key: '$($keyval[0])'='$val'"
}
}
}
return $pyvenvConfig
}
<# Begin Activate script --------------------------------------------------- #>
# Determine the containing directory of this script
$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition
$VenvExecDir = Get-Item -Path $VenvExecPath
Write-Verbose "Activation script is located in path: '$VenvExecPath'"
Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)"
Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)"
# Set values required in priority: CmdLine, ConfigFile, Default
# First, get the location of the virtual environment, it might not be
# VenvExecDir if specified on the command line.
if ($VenvDir) {
Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values"
}
else {
Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir."
$VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/")
Write-Verbose "VenvDir=$VenvDir"
}
# Next, read the `pyvenv.cfg` file to determine any required value such
# as `prompt`.
$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir
# Next, set the prompt from the command line, or the config file, or
# just use the name of the virtual environment folder.
if ($Prompt) {
Write-Verbose "Prompt specified as argument, using '$Prompt'"
}
else {
Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value"
if ($pyvenvCfg -and $pyvenvCfg['prompt']) {
Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'"
$Prompt = $pyvenvCfg['prompt'];
}
else {
Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virtual environment)"
Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'"
$Prompt = Split-Path -Path $venvDir -Leaf
}
}
Write-Verbose "Prompt = '$Prompt'"
Write-Verbose "VenvDir='$VenvDir'"
# Deactivate any currently active virtual environment, but leave the
# deactivate function in place.
deactivate -nondestructive
# Now set the environment variable VIRTUAL_ENV, used by many tools to determine
# that there is an activated venv.
$env:VIRTUAL_ENV = $VenvDir
if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) {
Write-Verbose "Setting prompt to '$Prompt'"
# Set the prompt to include the env name
# Make sure _OLD_VIRTUAL_PROMPT is global
function global:_OLD_VIRTUAL_PROMPT { "" }
Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT
New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt
function global:prompt {
Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) "
_OLD_VIRTUAL_PROMPT
}
$env:VIRTUAL_ENV_PROMPT = $Prompt
}
# Clear PYTHONHOME
if (Test-Path -Path Env:PYTHONHOME) {
Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME
Remove-Item -Path Env:PYTHONHOME
}
# Add the venv to the PATH
Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH
$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH"

View File

@@ -1,70 +0,0 @@
# This file must be used with "source bin/activate" *from bash*
# You cannot run it directly
deactivate () {
# reset old environment variables
if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then
PATH="${_OLD_VIRTUAL_PATH:-}"
export PATH
unset _OLD_VIRTUAL_PATH
fi
if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then
PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}"
export PYTHONHOME
unset _OLD_VIRTUAL_PYTHONHOME
fi
# Call hash to forget past commands. Without forgetting
# past commands the $PATH changes we made may not be respected
hash -r 2> /dev/null
if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then
PS1="${_OLD_VIRTUAL_PS1:-}"
export PS1
unset _OLD_VIRTUAL_PS1
fi
unset VIRTUAL_ENV
unset VIRTUAL_ENV_PROMPT
if [ ! "${1:-}" = "nondestructive" ] ; then
# Self destruct!
unset -f deactivate
fi
}
# unset irrelevant variables
deactivate nondestructive
# on Windows, a path can contain colons and backslashes and has to be converted:
if [ "${OSTYPE:-}" = "cygwin" ] || [ "${OSTYPE:-}" = "msys" ] ; then
# transform D:\path\to\venv to /d/path/to/venv on MSYS
# and to /cygdrive/d/path/to/venv on Cygwin
export VIRTUAL_ENV=$(cygpath "/home/pac/thrillwiki/thrillwiki_django_no_react/.venv")
else
# use the path as-is
export VIRTUAL_ENV="/home/pac/thrillwiki/thrillwiki_django_no_react/.venv"
fi
_OLD_VIRTUAL_PATH="$PATH"
PATH="$VIRTUAL_ENV/bin:$PATH"
export PATH
# unset PYTHONHOME if set
# this will fail if PYTHONHOME is set to the empty string (which is bad anyway)
# could use `if (set -u; : $PYTHONHOME) ;` in bash
if [ -n "${PYTHONHOME:-}" ] ; then
_OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}"
unset PYTHONHOME
fi
if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then
_OLD_VIRTUAL_PS1="${PS1:-}"
PS1="(.venv) ${PS1:-}"
export PS1
VIRTUAL_ENV_PROMPT="(.venv) "
export VIRTUAL_ENV_PROMPT
fi
# Call hash to forget past commands. Without forgetting
# past commands the $PATH changes we made may not be respected
hash -r 2> /dev/null

View File

@@ -1,27 +0,0 @@
# This file must be used with "source bin/activate.csh" *from csh*.
# You cannot run it directly.
# Created by Davide Di Blasi <davidedb@gmail.com>.
# Ported to Python 3.3 venv by Andrew Svetlov <andrew.svetlov@gmail.com>
alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; unsetenv VIRTUAL_ENV_PROMPT; test "\!:*" != "nondestructive" && unalias deactivate'
# Unset irrelevant variables.
deactivate nondestructive
setenv VIRTUAL_ENV "/home/pac/thrillwiki/thrillwiki_django_no_react/.venv"
set _OLD_VIRTUAL_PATH="$PATH"
setenv PATH "$VIRTUAL_ENV/bin:$PATH"
set _OLD_VIRTUAL_PROMPT="$prompt"
if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then
set prompt = "(.venv) $prompt"
setenv VIRTUAL_ENV_PROMPT "(.venv) "
endif
alias pydoc python -m pydoc
rehash

View File

@@ -1,69 +0,0 @@
# This file must be used with "source <venv>/bin/activate.fish" *from fish*
# (https://fishshell.com/). You cannot run it directly.
function deactivate -d "Exit virtual environment and return to normal shell environment"
# reset old environment variables
if test -n "$_OLD_VIRTUAL_PATH"
set -gx PATH $_OLD_VIRTUAL_PATH
set -e _OLD_VIRTUAL_PATH
end
if test -n "$_OLD_VIRTUAL_PYTHONHOME"
set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME
set -e _OLD_VIRTUAL_PYTHONHOME
end
if test -n "$_OLD_FISH_PROMPT_OVERRIDE"
set -e _OLD_FISH_PROMPT_OVERRIDE
# prevents error when using nested fish instances (Issue #93858)
if functions -q _old_fish_prompt
functions -e fish_prompt
functions -c _old_fish_prompt fish_prompt
functions -e _old_fish_prompt
end
end
set -e VIRTUAL_ENV
set -e VIRTUAL_ENV_PROMPT
if test "$argv[1]" != "nondestructive"
# Self-destruct!
functions -e deactivate
end
end
# Unset irrelevant variables.
deactivate nondestructive
set -gx VIRTUAL_ENV "/home/pac/thrillwiki/thrillwiki_django_no_react/.venv"
set -gx _OLD_VIRTUAL_PATH $PATH
set -gx PATH "$VIRTUAL_ENV/bin" $PATH
# Unset PYTHONHOME if set.
if set -q PYTHONHOME
set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME
set -e PYTHONHOME
end
if test -z "$VIRTUAL_ENV_DISABLE_PROMPT"
# fish uses a function instead of an env var to generate the prompt.
# Save the current fish_prompt function as the function _old_fish_prompt.
functions -c fish_prompt _old_fish_prompt
# With the original prompt function renamed, we can override with our own.
function fish_prompt
# Save the return status of the last command.
set -l old_status $status
# Output the venv prompt; color taken from the blue of the Python logo.
printf "%s%s%s" (set_color 4B8BBE) "(.venv) " (set_color normal)
# Restore the return status of the previous command.
echo "exit $old_status" | .
# Output the original/"old" prompt.
_old_fish_prompt
end
set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV"
set -gx VIRTUAL_ENV_PROMPT "(.venv) "
end

View File

@@ -1,8 +0,0 @@
#!/home/pac/thrillwiki/thrillwiki_django_no_react/.venv/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from automat._visualize import tool
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(tool())

View File

@@ -1,8 +0,0 @@
#!/home/pac/thrillwiki/thrillwiki_django_no_react/.venv/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from black import patched_main
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(patched_main())

View File

@@ -1,8 +0,0 @@
#!/home/pac/thrillwiki/thrillwiki_django_no_react/.venv/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from blackd import patched_main
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(patched_main())

View File

@@ -1,8 +0,0 @@
#!/home/pac/thrillwiki/thrillwiki_django_no_react/.venv/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from twisted.conch.scripts.cftp import run
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(run())

View File

@@ -1,8 +0,0 @@
#!/home/pac/thrillwiki/thrillwiki_django_no_react/.venv/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from twisted.conch.scripts.ckeygen import run
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(run())

View File

@@ -1,8 +0,0 @@
#!/home/pac/thrillwiki/thrillwiki_django_no_react/.venv/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from twisted.conch.scripts.conch import run
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(run())

View File

@@ -1,8 +0,0 @@
#!/home/pac/thrillwiki/thrillwiki_django_no_react/.venv/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from coverage.cmdline import main
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(main())

View File

@@ -1,8 +0,0 @@
#!/home/pac/thrillwiki/thrillwiki_django_no_react/.venv/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from coverage.cmdline import main
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(main())

View File

@@ -1,8 +0,0 @@
#!/home/pac/thrillwiki/thrillwiki_django_no_react/.venv/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from coverage.cmdline import main
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(main())

View File

@@ -1,8 +0,0 @@
#!/home/pac/thrillwiki/thrillwiki_django_no_react/.venv/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from daphne.cli import CommandLineInterface
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(CommandLineInterface.entrypoint())

View File

@@ -1,8 +0,0 @@
#!/home/pac/thrillwiki/thrillwiki_django_no_react/.venv/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from django.core.management import execute_from_command_line
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(execute_from_command_line())

View File

@@ -1,8 +0,0 @@
#!/home/pac/thrillwiki/thrillwiki_django_no_react/.venv/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from dotenv.__main__ import cli
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(cli())

View File

@@ -1,8 +0,0 @@
#!/home/pac/thrillwiki/thrillwiki_django_no_react/.venv/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from flake8.main.cli import main
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(main())

View File

@@ -1,8 +0,0 @@
#!/home/pac/thrillwiki/thrillwiki_django_no_react/.venv/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from twisted.mail.scripts.mailmail import run
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(run())

View File

@@ -1,8 +0,0 @@
#!/home/pac/thrillwiki/thrillwiki_django_no_react/.venv/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from charset_normalizer.cli import cli_detect
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(cli_detect())

View File

@@ -1,8 +0,0 @@
#!/home/pac/thrillwiki/thrillwiki_django_no_react/.venv/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from pip._internal.cli.main import main
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(main())

View File

@@ -1,8 +0,0 @@
#!/home/pac/thrillwiki/thrillwiki_django_no_react/.venv/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from pip._internal.cli.main import main
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(main())

View File

@@ -1,8 +0,0 @@
#!/home/pac/thrillwiki/thrillwiki_django_no_react/.venv/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from pip._internal.cli.main import main
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(main())

View File

@@ -1,8 +0,0 @@
#!/home/pac/thrillwiki/thrillwiki_django_no_react/.venv/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from pytest import console_main
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(console_main())

View File

@@ -1,8 +0,0 @@
#!/home/pac/thrillwiki/thrillwiki_django_no_react/.venv/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from pycodestyle import _main
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(_main())

View File

@@ -1,8 +0,0 @@
#!/home/pac/thrillwiki/thrillwiki_django_no_react/.venv/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from pyflakes.api import main
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(main())

View File

@@ -1,8 +0,0 @@
#!/home/pac/thrillwiki/thrillwiki_django_no_react/.venv/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from twisted.scripts.htmlizer import run
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(run())

View File

@@ -1,8 +0,0 @@
#!/home/pac/thrillwiki/thrillwiki_django_no_react/.venv/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from pytest import console_main
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(console_main())

View File

@@ -1 +0,0 @@
python3

View File

@@ -1 +0,0 @@
/usr/local/bin/python3

View File

@@ -1 +0,0 @@
python3

View File

@@ -1,8 +0,0 @@
#!/home/pac/thrillwiki/thrillwiki_django_no_react/.venv/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from sqlparse.__main__ import main
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(main())

View File

@@ -1,8 +0,0 @@
#!/home/pac/thrillwiki/thrillwiki_django_no_react/.venv/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from twisted.conch.scripts.tkconch import run
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(run())

View File

@@ -1,8 +0,0 @@
#!/home/pac/thrillwiki/thrillwiki_django_no_react/.venv/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from twisted.scripts.trial import run
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(run())

View File

@@ -1,8 +0,0 @@
#!/home/pac/thrillwiki/thrillwiki_django_no_react/.venv/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from twisted.application.twist._twist import Twist
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(Twist.main())

View File

@@ -1,8 +0,0 @@
#!/home/pac/thrillwiki/thrillwiki_django_no_react/.venv/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from twisted.scripts.twistd import run
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(run())

View File

@@ -1,8 +0,0 @@
#!/home/pac/thrillwiki/thrillwiki_django_no_react/.venv/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from autobahn.__main__ import _main
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(_main())

View File

@@ -1,8 +0,0 @@
#!/home/pac/thrillwiki/thrillwiki_django_no_react/.venv/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from autobahn.xbr._cli import _main
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(_main())

View File

@@ -1,8 +0,0 @@
#!/home/pac/thrillwiki/thrillwiki_django_no_react/.venv/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from autobahn.xbr._gui import _main
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(_main())

View File

@@ -1,21 +0,0 @@
Copyright (c) 2014
Rackspace
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -1,199 +0,0 @@
Metadata-Version: 2.1
Name: Automat
Version: 24.8.1
Summary: Self-service finite-state machines for the programmer on the go.
Author-email: Glyph <code@glyph.im>
License: Copyright (c) 2014
Rackspace
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Project-URL: Documentation, https://automat.readthedocs.io/
Project-URL: Source, https://github.com/glyph/automat/
Keywords: fsm,state machine,automata
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: typing-extensions; python_version < "3.10"
Provides-Extra: visualize
Requires-Dist: graphviz>0.5.1; extra == "visualize"
Requires-Dist: Twisted>=16.1.1; extra == "visualize"
# Automat #
[![Documentation Status](https://readthedocs.org/projects/automat/badge/?version=latest)](http://automat.readthedocs.io/en/latest/)
[![Build Status](https://github.com/glyph/automat/actions/workflows/ci.yml/badge.svg?branch=trunk)](https://github.com/glyph/automat/actions/workflows/ci.yml?query=branch%3Atrunk)
[![Coverage Status](http://codecov.io/github/glyph/automat/coverage.svg?branch=trunk)](http://codecov.io/github/glyph/automat?branch=trunk)
## Self-service finite-state machines for the programmer on the go. ##
Automat is a library for concise, idiomatic Python expression of finite-state
automata (particularly deterministic finite-state transducers).
Read more here, or on [Read the Docs](https://automat.readthedocs.io/), or watch the following videos for an overview and presentation
### Why use state machines? ###
Sometimes you have to create an object whose behavior varies with its state,
but still wishes to present a consistent interface to its callers.
For example, let's say you're writing the software for a coffee machine. It
has a lid that can be opened or closed, a chamber for water, a chamber for
coffee beans, and a button for "brew".
There are a number of possible states for the coffee machine. It might or
might not have water. It might or might not have beans. The lid might be open
or closed. The "brew" button should only actually attempt to brew coffee in
one of these configurations, and the "open lid" button should only work if the
coffee is not, in fact, brewing.
With diligence and attention to detail, you can implement this correctly using
a collection of attributes on an object; `hasWater`, `hasBeans`, `isLidOpen`
and so on. However, you have to keep all these attributes consistent. As the
coffee maker becomes more complex - perhaps you add an additional chamber for
flavorings so you can make hazelnut coffee, for example - you have to keep
adding more and more checks and more and more reasoning about which
combinations of states are allowed.
Rather than adding tedious `if` checks to every single method to make sure that
each of these flags are exactly what you expect, you can use a state machine to
ensure that if your code runs at all, it will be run with all the required
values initialized, because they have to be called in the order you declare
them.
You can read about state machines and their advantages for Python programmers
in more detail [in this excellent article by Jean-Paul
Calderone](https://web.archive.org/web/20160507053658/https://clusterhq.com/2013/12/05/what-is-a-state-machine/).
### What makes Automat different? ###
There are
[dozens of libraries on PyPI implementing state machines](https://pypi.org/search/?q=finite+state+machine).
So it behooves me to say why yet another one would be a good idea.
Automat is designed around this principle: while organizing your code around
state machines is a good idea, your callers don't, and shouldn't have to, care
that you've done so. In Python, the "input" to a stateful system is a method
call; the "output" may be a method call, if you need to invoke a side effect,
or a return value, if you are just performing a computation in memory. Most
other state-machine libraries require you to explicitly create an input object,
provide that object to a generic "input" method, and then receive results,
sometimes in terms of that library's interfaces and sometimes in terms of
classes you define yourself.
For example, a snippet of the coffee-machine example above might be implemented
as follows in naive Python:
```python
class CoffeeMachine(object):
def brewButton(self) -> None:
if self.hasWater and self.hasBeans and not self.isLidOpen:
self.heatTheHeatingElement()
# ...
```
With Automat, you'd begin with a `typing.Protocol` that describes all of your
inputs:
```python
from typing import Protocol
class CoffeeBrewer(Protocol):
def brewButton(self) -> None:
"The user pressed the 'brew' button."
def putInBeans(self) -> None:
"The user put in some beans."
```
We'll then need a concrete class to contain the shared core of state shared
among the different states:
```python
from dataclasses import dataclass
@dataclass
class BrewerCore:
heatingElement: HeatingElement
```
Next, we need to describe our state machine, including all of our states. For
simplicity's sake let's say that the only two states are `noBeans` and
`haveBeans`:
```python
from automat import TypeMachineBuilder
builder = TypeMachineBuilder(CoffeeBrewer, BrewerCore)
noBeans = builder.state("noBeans")
haveBeans = builder.state("haveBeans")
```
Next we can describe a simple transition; when we put in beans, we move to the
`haveBeans` state, with no other behavior.
```python
# When we don't have beans, upon putting in beans, we will then have beans
noBeans.upon(CoffeeBrewer.putInBeans).to(haveBeans).returns(None)
```
And then another transition that we describe with a decorator, one that *does*
have some behavior, that needs to heat up the heating element to brew the
coffee:
```python
@haveBeans.upon(CoffeeBrewer.brewButton).to(noBeans)
def heatUp(inputs: CoffeeBrewer, core: BrewerCore) -> None:
"""
When we have beans, upon pressing the brew button, we will then not have
beans any more (as they have been entered into the brewing chamber) and
our output will be heating the heating element.
"""
print("Brewing the coffee...")
core.heatingElement.turnOn()
```
Then we finalize the state machine by building it, which gives us a callable
that takes a `BrewerCore` and returns a synthetic `CoffeeBrewer`
```python
newCoffeeMachine = builder.build()
```
```python
>>> coffee = newCoffeeMachine(BrewerCore(HeatingElement()))
>>> machine.putInBeans()
>>> machine.brewButton()
Brewing the coffee...
```
All of the *inputs* are provided by calling them like methods, all of the
*output behaviors* are automatically invoked when they are produced according
to the outputs specified to `upon` and all of the states are simply opaque
tokens.

View File

@@ -1,39 +0,0 @@
../../../bin/automat-visualize,sha256=IsKluasmdX-znO8xCKiVIZ1zYKnBxs3ykFbJ3SxJ4vs,266
Automat-24.8.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
Automat-24.8.1.dist-info/LICENSE,[AWS-SECRET-REMOVED]zv_-9TA43Y,1053
Automat-24.8.1.dist-info/METADATA,sha256=XJIxL4Olb15WCoAdVEcORum39__wiCXQR6LNubERZ6M,8396
Automat-24.8.1.dist-info/RECORD,,
Automat-24.8.1.dist-info/WHEEL,sha256=HiCZjzuy6Dw0hdX5R3LCFPDmFS4BWl8H-8W39XfmgX4,91
Automat-24.8.1.dist-info/entry_points.txt,[AWS-SECRET-REMOVED]rPNlGi7NbQ,62
Automat-24.8.1.dist-info/top_level.txt,sha256=vg4zAOyhP_3YCmpKZLNgFw1uMF3lC_b6TKsdz7jBSpI,8
automat/__init__.py,[AWS-SECRET-REMOVED]WD_RltPB-U,356
automat/__pycache__/__init__.cpython-312.pyc,,
automat/__pycache__/_core.cpython-312.pyc,,
automat/__pycache__/_discover.cpython-312.pyc,,
automat/__pycache__/_introspection.cpython-312.pyc,,
automat/__pycache__/_methodical.cpython-312.pyc,,
automat/__pycache__/_runtimeproto.cpython-312.pyc,,
automat/__pycache__/_typed.cpython-312.pyc,,
automat/__pycache__/_visualize.cpython-312.pyc,,
automat/_core.py,sha256=oe4QNlfvmgsnKe_8fyNiOsHsfz5xPArGuXWle9zePp8,6663
automat/_discover.py,sha256=KRbmm7kxpd-WReDQU4qe6hVKGUKmGBHUjYIkRneO4mc,5197
automat/_introspection.py,sha256=uF5ymY-GZckyRxvRs7UToPBV_oVV6xHmvlBVey9nv80,1441
automat/_methodical.py,sha256=YgZXraDe6dvK6w_Y9xfPa0kXCka4ZeGLfcb4IfK4bnI,17243
automat/_runtimeproto.py,sha256=mJ_[AWS-SECRET-REMOVED],1654
automat/_test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
automat/_test/__pycache__/__init__.cpython-312.pyc,,
automat/_test/__pycache__/test_core.cpython-312.pyc,,
automat/_test/__pycache__/test_discover.cpython-312.pyc,,
automat/_test/__pycache__/test_methodical.cpython-312.pyc,,
automat/_test/__pycache__/test_trace.cpython-312.pyc,,
automat/_test/__pycache__/test_type_based.cpython-312.pyc,,
automat/_test/__pycache__/test_visualize.cpython-312.pyc,,
automat/_test/test_core.py,sha256=PJHNvQ85i8vjH-oF6nPNKB84_noTyl2dQSv_iRl70J8,3481
automat/_test/test_discover.py,[AWS-SECRET-REMOVED]Gg7kQnbvtw,22067
automat/_test/test_methodical.py,sha256=SKMMbl-6bjH-QZ1hDFRItCOyKF4SstuA4QvExnVhn7w,20267
automat/_test/test_trace.py,sha256=tty7P_ctJtk38ZXnpmEy-J9Rn-Hh2AKb_ia6EmtXSQI,3299
automat/_test/test_type_based.py,[AWS-SECRET-REMOVED]AegU_OUvko,17872
automat/_test/test_visualize.py,sha256=HXBPgMAD0OTz_l1Yq0lI3d1vJ_l3sHTH67Jauaf-2sk,14631
automat/_typed.py,sha256=lMzMgUfX713Xw_W4pr8iyPqcdpRSbu4rEpRlrXAOW2k,24204
automat/_visualize.py,sha256=DQYig2mBKX-LquPEEy89Y_qyj21tSutAUaFsF1F64ws,6512
automat/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0

View File

@@ -1,5 +0,0 @@
Wheel-Version: 1.0
Generator: setuptools (72.2.0)
Root-Is-Purelib: true
Tag: py3-none-any

View File

@@ -1,2 +0,0 @@
[console_scripts]
automat-visualize = automat._visualize:tool

View File

@@ -1,27 +0,0 @@
Copyright (c) Django Software Foundation and individual contributors.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of Django nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -1,288 +0,0 @@
Django is licensed under the three-clause BSD license; see the file
LICENSE for details.
Django includes code from the Python standard library, which is licensed under
the Python license, a permissive open source license. The copyright and license
is included below for compliance with Python's terms.
----------------------------------------------------------------------
Copyright (c) 2001-present Python Software Foundation; All Rights Reserved
A. HISTORY OF THE SOFTWARE
==========================
Python was created in the early 1990s by Guido van Rossum at Stichting
Mathematisch Centrum (CWI, see https://www.cwi.nl) in the Netherlands
as a successor of a language called ABC. Guido remains Python's
principal author, although it includes many contributions from others.
In 1995, Guido continued his work on Python at the Corporation for
National Research Initiatives (CNRI, see https://www.cnri.reston.va.us)
in Reston, Virginia where he released several versions of the
software.
In May 2000, Guido and the Python core development team moved to
BeOpen.com to form the BeOpen PythonLabs team. In October of the same
year, the PythonLabs team moved to Digital Creations, which became
Zope Corporation. In 2001, the Python Software Foundation (PSF, see
https://www.python.org/psf/) was formed, a non-profit organization
created specifically to own Python-related Intellectual Property.
Zope Corporation was a sponsoring member of the PSF.
All Python releases are Open Source (see https://opensource.org for
the Open Source Definition). Historically, most, but not all, Python
releases have also been GPL-compatible; the table below summarizes
the various releases.
Release Derived Year Owner GPL-
from compatible? (1)
0.9.0 thru 1.2 1991-1995 CWI yes
1.3 thru 1.5.2 1.2 1995-1999 CNRI yes
1.6 1.5.2 2000 CNRI no
2.0 1.6 2000 BeOpen.com no
1.6.1 1.6 2001 CNRI yes (2)
2.1 2.0+1.6.1 2001 PSF no
2.0.1 2.0+1.6.1 2001 PSF yes
2.1.1 2.1+2.0.1 2001 PSF yes
2.1.2 2.1.1 2002 PSF yes
2.1.3 2.1.2 2002 PSF yes
2.2 and above 2.1.1 2001-now PSF yes
Footnotes:
(1) GPL-compatible doesn't mean that we're distributing Python under
the GPL. All Python licenses, unlike the GPL, let you distribute
a modified version without making your changes open source. The
GPL-compatible licenses make it possible to combine Python with
other software that is released under the GPL; the others don't.
(2) According to Richard Stallman, 1.6.1 is not GPL-compatible,
because its license has a choice of law clause. According to
CNRI, however, Stallman's lawyer has told CNRI's lawyer that 1.6.1
is "not incompatible" with the GPL.
Thanks to the many outside volunteers who have worked under Guido's
direction to make these releases possible.
B. TERMS AND CONDITIONS FOR ACCESSING OR OTHERWISE USING PYTHON
[AWS-SECRET-REMOVED]=======================
Python software and documentation are licensed under the
Python Software Foundation License Version 2.
Starting with Python 3.8.6, examples, recipes, and other code in
the documentation are dual licensed under the PSF License Version 2
and the Zero-Clause BSD license.
Some software incorporated into Python is under different licenses.
The licenses are listed with code falling under that license.
PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2
--------------------------------------------
1. This LICENSE AGREEMENT is between the Python Software Foundation
("PSF"), and the Individual or Organization ("Licensee") accessing and
otherwise using this software ("Python") in source or binary form and
its associated documentation.
2. Subject to the terms and conditions of this License Agreement, PSF hereby
grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,
analyze, test, perform and/or display publicly, prepare derivative works,
distribute, and otherwise use Python alone or in any derivative version,
provided, however, that PSF's License Agreement and PSF's notice of copyright,
i.e., "Copyright (c) 2001-2024 Python Software Foundation; All Rights Reserved"
are retained in Python alone or in any derivative version prepared by Licensee.
3. In the event Licensee prepares a derivative work that is based on
or incorporates Python or any part thereof, and wants to make
the derivative work available to others as provided herein, then
Licensee hereby agrees to include in any such work a brief summary of
the changes made to Python.
4. PSF is making Python available to Licensee on an "AS IS"
basis. PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON WILL NOT
INFRINGE ANY THIRD PARTY RIGHTS.
5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON,
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
6. This License Agreement will automatically terminate upon a material
breach of its terms and conditions.
7. Nothing in this License Agreement shall be deemed to create any
relationship of agency, partnership, or joint venture between PSF and
Licensee. This License Agreement does not grant permission to use PSF
trademarks or trade name in a trademark sense to endorse or promote
products or services of Licensee, or any third party.
8. By copying, installing or otherwise using Python, Licensee
agrees to be bound by the terms and conditions of this License
Agreement.
BEOPEN.COM LICENSE AGREEMENT FOR PYTHON 2.0
-------------------------------------------
BEOPEN PYTHON OPEN SOURCE LICENSE AGREEMENT VERSION 1
1. This LICENSE AGREEMENT is between BeOpen.com ("BeOpen"), having an
office at 160 Saratoga Avenue, Santa Clara, CA 95051, and the
Individual or Organization ("Licensee") accessing and otherwise using
this software in source or binary form and its associated
documentation ("the Software").
2. Subject to the terms and conditions of this BeOpen Python License
Agreement, BeOpen hereby grants Licensee a non-exclusive,
royalty-free, world-wide license to reproduce, analyze, test, perform
and/or display publicly, prepare derivative works, distribute, and
otherwise use the Software alone or in any derivative version,
provided, however, that the BeOpen Python License is retained in the
Software, alone or in any derivative version prepared by Licensee.
3. BeOpen is making the Software available to Licensee on an "AS IS"
basis. BEOPEN MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, BEOPEN MAKES NO AND
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE WILL NOT
INFRINGE ANY THIRD PARTY RIGHTS.
4. BEOPEN SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF THE
SOFTWARE FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS
AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THE SOFTWARE, OR ANY
DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
5. This License Agreement will automatically terminate upon a material
breach of its terms and conditions.
6. This License Agreement shall be governed by and interpreted in all
respects by the law of the State of California, excluding conflict of
law provisions. Nothing in this License Agreement shall be deemed to
create any relationship of agency, partnership, or joint venture
between BeOpen and Licensee. This License Agreement does not grant
permission to use BeOpen trademarks or trade names in a trademark
sense to endorse or promote products or services of Licensee, or any
third party. As an exception, the "BeOpen Python" logos available at
http://www.pythonlabs.com/logos.html may be used according to the
permissions granted on that web page.
7. By copying, installing or otherwise using the software, Licensee
agrees to be bound by the terms and conditions of this License
Agreement.
CNRI LICENSE AGREEMENT FOR PYTHON 1.6.1
---------------------------------------
1. This LICENSE AGREEMENT is between the Corporation for National
Research Initiatives, having an office at 1895 Preston White Drive,
Reston, VA 20191 ("CNRI"), and the Individual or Organization
("Licensee") accessing and otherwise using Python 1.6.1 software in
source or binary form and its associated documentation.
2. Subject to the terms and conditions of this License Agreement, CNRI
hereby grants Licensee a nonexclusive, royalty-free, world-wide
license to reproduce, analyze, test, perform and/or display publicly,
prepare derivative works, distribute, and otherwise use Python 1.6.1
alone or in any derivative version, provided, however, that CNRI's
License Agreement and CNRI's notice of copyright, i.e., "Copyright (c)
1995-2001 Corporation for National Research Initiatives; All Rights
Reserved" are retained in Python 1.6.1 alone or in any derivative
version prepared by Licensee. Alternately, in lieu of CNRI's License
Agreement, Licensee may substitute the following text (omitting the
quotes): "Python 1.6.1 is made available subject to the terms and
conditions in CNRI's License Agreement. This Agreement together with
Python 1.6.1 may be located on the internet using the following
unique, persistent identifier (known as a handle): 1895.22/1013. This
Agreement may also be obtained from a proxy server on the internet
using the following URL: http://hdl.handle.net/1895.22/1013".
3. In the event Licensee prepares a derivative work that is based on
or incorporates Python 1.6.1 or any part thereof, and wants to make
the derivative work available to others as provided herein, then
Licensee hereby agrees to include in any such work a brief summary of
the changes made to Python 1.6.1.
4. CNRI is making Python 1.6.1 available to Licensee on an "AS IS"
basis. CNRI MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
IMPLIED. BY WAY OF EXAMPLE, BUT NOT LIMITATION, CNRI MAKES NO AND
DISCLAIMS ANY REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS
FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF PYTHON 1.6.1 WILL NOT
INFRINGE ANY THIRD PARTY RIGHTS.
5. CNRI SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON
1.6.1 FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS
A RESULT OF MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 1.6.1,
OR ANY DERIVATIVE THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.
6. This License Agreement will automatically terminate upon a material
breach of its terms and conditions.
7. This License Agreement shall be governed by the federal
intellectual property law of the United States, including without
limitation the federal copyright law, and, to the extent such
U.S. federal law does not apply, by the law of the Commonwealth of
Virginia, excluding Virginia's conflict of law provisions.
Notwithstanding the foregoing, with regard to derivative works based
on Python 1.6.1 that incorporate non-separable material that was
previously distributed under the GNU General Public License (GPL), the
law of the Commonwealth of Virginia shall govern this License
Agreement only as to issues arising under or with respect to
Paragraphs 4, 5, and 7 of this License Agreement. Nothing in this
License Agreement shall be deemed to create any relationship of
agency, partnership, or joint venture between CNRI and Licensee. This
License Agreement does not grant permission to use CNRI trademarks or
trade name in a trademark sense to endorse or promote products or
services of Licensee, or any third party.
8. By clicking on the "ACCEPT" button where indicated, or by copying,
installing or otherwise using Python 1.6.1, Licensee agrees to be
bound by the terms and conditions of this License Agreement.
ACCEPT
CWI LICENSE AGREEMENT FOR PYTHON 0.9.0 THROUGH 1.2
--------------------------------------------------
Copyright (c) 1991 - 1995, Stichting Mathematisch Centrum Amsterdam,
The Netherlands. All rights reserved.
Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted,
provided that the above copyright notice appear in all copies and that
both that copyright notice and this permission notice appear in
supporting documentation, and that the name of Stichting Mathematisch
Centrum or CWI not be used in advertising or publicity pertaining to
distribution of the software without specific, written prior
permission.
STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
ZERO-CLAUSE BSD LICENSE FOR CODE IN THE PYTHON DOCUMENTATION
----------------------------------------------------------------------
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.

View File

@@ -1,100 +0,0 @@
Metadata-Version: 2.1
Name: Django
Version: 5.1.2
Summary: A high-level Python web framework that encourages rapid development and clean, pragmatic design.
Author-email: Django Software Foundation <foundation@djangoproject.com>
License: BSD-3-Clause
Project-URL: Homepage, https://www.djangoproject.com/
Project-URL: Documentation, https://docs.djangoproject.com/
Project-URL: Release notes, https://docs.djangoproject.com/en/stable/releases/
Project-URL: Funding, https://www.djangoproject.com/fundraising/
Project-URL: Source, https://github.com/django/django
Project-URL: Tracker, https://code.djangoproject.com/
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Web Environment
Classifier: Framework :: Django
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
Classifier: Topic :: Internet :: WWW/HTTP :: WSGI
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.10
Description-Content-Type: text/x-rst
License-File: LICENSE
License-File: LICENSE.python
License-File: AUTHORS
Requires-Dist: asgiref<4,>=3.8.1
Requires-Dist: sqlparse>=0.3.1
Requires-Dist: tzdata; sys_platform == "win32"
Provides-Extra: argon2
Requires-Dist: argon2-cffi>=19.1.0; extra == "argon2"
Provides-Extra: bcrypt
Requires-Dist: bcrypt; extra == "bcrypt"
======
Django
======
Django is a high-level Python web framework that encourages rapid development
and clean, pragmatic design. Thanks for checking it out.
All documentation is in the "``docs``" directory and online at
https://docs.djangoproject.com/en/stable/. If you're just getting started,
here's how we recommend you read the docs:
* First, read ``docs/intro/install.txt`` for instructions on installing Django.
* Next, work through the tutorials in order (``docs/intro/tutorial01.txt``,
``docs/intro/tutorial02.txt``, etc.).
* If you want to set up an actual deployment server, read
``docs/howto/deployment/index.txt`` for instructions.
* You'll probably want to read through the topical guides (in ``docs/topics``)
next; from there you can jump to the HOWTOs (in ``docs/howto``) for specific
problems, and check out the reference (``docs/ref``) for gory details.
* See ``docs/README`` for instructions on building an HTML version of the docs.
Docs are updated rigorously. If you find any problems in the docs, or think
they should be clarified in any way, please take 30 seconds to fill out a
ticket here: https://code.djangoproject.com/newticket
To get more help:
* Join the ``#django`` channel on ``irc.libera.chat``. Lots of helpful people
hang out there. `Webchat is available <https://web.libera.chat/#django>`_.
* Join the django-users mailing list, or read the archives, at
https://groups.google.com/group/django-users.
* Join the `Django Discord community <https://discord.gg/xcRH6mN4fa>`_.
* Join the community on the `Django Forum <https://forum.djangoproject.com/>`_.
To contribute to Django:
* Check out https://docs.djangoproject.com/en/dev/internals/contributing/ for
information about getting involved.
To run Django's test suite:
* Follow the instructions in the "Unit tests" section of
``docs/internals/contributing/writing-code/unit-tests.txt``, published online at
https://docs.djangoproject.[AWS-SECRET-REMOVED]g-code/unit-tests/#running-the-unit-tests
Supporting the Development of Django
====================================
Django's development depends on your contributions.
If you depend on Django, remember to support the Django Software Foundation: https://www.djangoproject.com/fundraising/

View File

@@ -1,5 +0,0 @@
Wheel-Version: 1.0
Generator: bdist_wheel (0.44.0)
Root-Is-Purelib: true
Tag: py3-none-any

View File

@@ -1,2 +0,0 @@
[console_scripts]
django-admin = django.core.management:execute_from_command_line

File diff suppressed because it is too large Load Diff

View File

@@ -1,31 +0,0 @@
# Copyright (C) AB Strakt
# See LICENSE for details.
"""
pyOpenSSL - A simple wrapper around the OpenSSL library
"""
from OpenSSL import SSL, crypto
from OpenSSL.version import (
__author__,
__copyright__,
__email__,
__license__,
__summary__,
__title__,
__uri__,
__version__,
)
__all__ = [
"SSL",
"crypto",
"__author__",
"__copyright__",
"__email__",
"__license__",
"__summary__",
"__title__",
"__uri__",
"__version__",
]

View File

@@ -1,124 +0,0 @@
import os
import sys
import warnings
from typing import Any, Callable, NoReturn, Type, Union
from cryptography.hazmat.bindings.openssl.binding import Binding
StrOrBytesPath = Union[str, bytes, os.PathLike]
binding = Binding()
ffi = binding.ffi
lib = binding.lib
# This is a special CFFI allocator that does not bother to zero its memory
# after allocation. This has vastly better performance on large allocations and
# so should be used whenever we don't need the memory zeroed out.
no_zero_allocator = ffi.new_allocator(should_clear_after_alloc=False)
def text(charp: Any) -> str:
"""
Get a native string type representing of the given CFFI ``char*`` object.
:param charp: A C-style string represented using CFFI.
:return: :class:`str`
"""
if not charp:
return ""
return ffi.string(charp).decode("utf-8")
def exception_from_error_queue(exception_type: Type[Exception]) -> NoReturn:
"""
Convert an OpenSSL library failure into a Python exception.
When a call to the native OpenSSL library fails, this is usually signalled
by the return value, and an error code is stored in an error queue
associated with the current thread. The err library provides functions to
obtain these error codes and textual error messages.
"""
errors = []
while True:
error = lib.ERR_get_error()
if error == 0:
break
errors.append(
(
text(lib.ERR_lib_error_string(error)),
text(lib.ERR_func_error_string(error)),
text(lib.ERR_reason_error_string(error)),
)
)
raise exception_type(errors)
def make_assert(error: Type[Exception]) -> Callable[[bool], Any]:
"""
Create an assert function that uses :func:`exception_from_error_queue` to
raise an exception wrapped by *error*.
"""
def openssl_assert(ok: bool) -> None:
"""
If *ok* is not True, retrieve the error from OpenSSL and raise it.
"""
if ok is not True:
exception_from_error_queue(error)
return openssl_assert
def path_bytes(s: StrOrBytesPath) -> bytes:
"""
Convert a Python path to a :py:class:`bytes` for the path which can be
passed into an OpenSSL API accepting a filename.
:param s: A path (valid for os.fspath).
:return: An instance of :py:class:`bytes`.
"""
b = os.fspath(s)
if isinstance(b, str):
return b.encode(sys.getfilesystemencoding())
else:
return b
def byte_string(s: str) -> bytes:
return s.encode("charmap")
# A marker object to observe whether some optional arguments are passed any
# value or not.
UNSPECIFIED = object()
_TEXT_WARNING = "str for {0} is no longer accepted, use bytes"
def text_to_bytes_and_warn(label: str, obj: Any) -> Any:
"""
If ``obj`` is text, emit a warning that it should be bytes instead and try
to convert it to bytes automatically.
:param str label: The name of the parameter from which ``obj`` was taken
(so a developer can easily find the source of the problem and correct
it).
:return: If ``obj`` is the text string type, a ``bytes`` object giving the
UTF-8 encoding of that text is returned. Otherwise, ``obj`` itself is
returned.
"""
if isinstance(obj, str):
warnings.warn(
_TEXT_WARNING.format(label),
category=DeprecationWarning,
stacklevel=3,
)
return obj.encode("utf-8")
return obj

File diff suppressed because it is too large Load Diff

View File

@@ -1,40 +0,0 @@
import ssl
import sys
import cffi
import cryptography
import OpenSSL.SSL
from . import version
_env_info = """\
pyOpenSSL: {pyopenssl}
cryptography: {cryptography}
cffi: {cffi}
cryptography's compiled against OpenSSL: {crypto_openssl_compile}
cryptography's linked OpenSSL: {crypto_openssl_link}
Python's OpenSSL: {python_openssl}
Python executable: {python}
Python version: {python_version}
Platform: {platform}
sys.path: {sys_path}""".format(
pyopenssl=version.__version__,
crypto_openssl_compile=OpenSSL._util.ffi.string(
OpenSSL._util.lib.OPENSSL_VERSION_TEXT,
).decode("ascii"),
crypto_openssl_link=OpenSSL.SSL.SSLeay_version(
OpenSSL.SSL.SSLEAY_VERSION
).decode("ascii"),
python_openssl=getattr(ssl, "OPENSSL_VERSION", "n/a"),
cryptography=cryptography.__version__,
cffi=cffi.__version__,
python=sys.executable,
python_version=sys.version,
platform=sys.platform,
sys_path=sys.path,
)
if __name__ == "__main__":
print(_env_info)

View File

@@ -1,40 +0,0 @@
"""
PRNG management routines, thin wrappers.
"""
from OpenSSL._util import lib as _lib
def add(buffer: bytes, entropy: int) -> None:
"""
Mix bytes from *string* into the PRNG state.
The *entropy* argument is (the lower bound of) an estimate of how much
randomness is contained in *string*, measured in bytes.
For more information, see e.g. :rfc:`1750`.
This function is only relevant if you are forking Python processes and
need to reseed the CSPRNG after fork.
:param buffer: Buffer with random data.
:param entropy: The entropy (in bytes) measurement of the buffer.
:return: :obj:`None`
"""
if not isinstance(buffer, bytes):
raise TypeError("buffer must be a byte string")
if not isinstance(entropy, int):
raise TypeError("entropy must be an integer")
_lib.RAND_add(buffer, len(buffer), entropy)
def status() -> int:
"""
Check whether the PRNG has been seeded with enough data.
:return: 1 if the PRNG is seeded enough, 0 otherwise.
"""
return _lib.RAND_status()

Some files were not shown because too many files have changed in this diff Show More