# ThrillWiki Frontend API Documentation This document provides comprehensive documentation for frontend developers on how to integrate with the ThrillWiki API endpoints. ## Base URL ``` http://localhost:8000/api/v1/ ``` ## Authentication Most endpoints are publicly accessible. Admin endpoints require authentication. ## Content Discovery Endpoints ### Trending Content Get trending parks and rides based on view counts, ratings, and recency. **Endpoint:** `GET /trending/content/` **Parameters:** - `limit` (optional): Number of trending items to return (default: 20, max: 100) - `timeframe` (optional): Timeframe for trending calculation - "day", "week", "month" (default: "week") **Response Format:** ```json { "trending_rides": [ { "id": 137, "name": "Steel Vengeance", "park": "Cedar Point", "category": "ride", "rating": 4.8, "rank": 1, "views": 15234, "views_change": "+25%", "slug": "steel-vengeance", "date_opened": "2018-05-05", "url": "https://thrillwiki.com/parks/cedar-point/rides/steel-vengeance/", "park_url": "https://thrillwiki.com/parks/cedar-point/", "card_image": "https://media.thrillwiki.com/rides/steel-vengeance-card.jpg" } ], "trending_parks": [ { "id": 1, "name": "Cedar Point", "park": "Cedar Point", "category": "park", "rating": 4.6, "rank": 1, "views": 45678, "views_change": "+12%", "slug": "cedar-point", "date_opened": "1870-01-01", "url": "https://thrillwiki.com/parks/cedar-point/", "card_image": "https://media.thrillwiki.com/parks/cedar-point-card.jpg", "city": "Sandusky", "state": "Ohio", "country": "USA", "primary_company": "Cedar Fair" } ], "latest_reviews": [] } ``` ### New Content Get recently added parks and rides. **Endpoint:** `GET /trending/new/` **Parameters:** - `limit` (optional): Number of new items to return (default: 20, max: 100) - `days` (optional): Number of days to look back for new content (default: 30, max: 365) **Response Format:** ```json { "recently_added": [ { "id": 137, "name": "Steel Vengeance", "park": "Cedar Point", "category": "ride", "date_added": "2018-05-05", "date_opened": "2018-05-05", "slug": "steel-vengeance", "url": "https://thrillwiki.com/parks/cedar-point/rides/steel-vengeance/", "park_url": "https://thrillwiki.com/parks/cedar-point/", "card_image": "https://media.thrillwiki.com/rides/steel-vengeance-card.jpg" }, { "id": 42, "name": "Dollywood", "park": "Dollywood", "category": "park", "date_added": "2018-05-01", "date_opened": "1986-05-03", "slug": "dollywood", "url": "https://thrillwiki.com/parks/dollywood/", "card_image": "https://media.thrillwiki.com/parks/dollywood-card.jpg", "city": "Pigeon Forge", "state": "Tennessee", "country": "USA", "primary_company": "Dollywood Company" } ], "newly_opened": [ { "id": 136, "name": "Time Traveler", "park": "Silver Dollar City", "category": "ride", "date_added": "2018-04-28", "date_opened": "2018-04-28", "slug": "time-traveler", "url": "https://thrillwiki.com/parks/silver-dollar-city/rides/time-traveler/", "park_url": "https://thrillwiki.com/parks/silver-dollar-city/", "card_image": "https://media.thrillwiki.com/rides/time-traveler-card.jpg" } ], "upcoming": [] } ``` **Key Changes:** - **REMOVED:** `location` field from all trending and new content responses - **ADDED:** `park` field - shows the park name for both parks and rides - **ADDED:** `date_opened` field - shows when the park/ride originally opened ### Trigger Content Calculation (Admin Only) Manually trigger the calculation of trending and new content. **Endpoint:** `POST /trending/calculate/` **Authentication:** Admin access required **Response Format:** ```json { "message": "Trending content calculation completed", "trending_completed": true, "new_content_completed": true, "completion_time": "2025-08-28 16:41:42", "trending_output": "Successfully calculated 50 trending items for all", "new_content_output": "Successfully calculated 50 new items for all" } ``` ## Data Field Descriptions ### Common Fields - `id`: Unique identifier for the item - `name`: Display name of the park or ride - `park`: Name of the park (for rides, this is the parent park; for parks, this is the park itself) - `category`: Type of content ("park" or "ride") - `slug`: URL-friendly identifier - `date_opened`: ISO date string of when the park/ride originally opened (YYYY-MM-DD format) - `url`: Frontend URL for direct navigation to the item's detail page - `card_image`: URL to the card image for display in lists and grids (available for both parks and rides) ### Park-Specific Fields - `city`: City where the park is located (shortened format) - `state`: State/province where the park is located (shortened format) - `country`: Country where the park is located (shortened format) - `primary_company`: Name of the primary operating company for the park ### Ride-Specific Fields - `park_url`: Frontend URL for the ride's parent park ### Trending-Specific Fields - `rating`: Average user rating (0.0 to 10.0) - `rank`: Position in trending list (1-based) - `views`: Current view count - `views_change`: Percentage change in views (e.g., "+25%") ### New Content-Specific Fields - `date_added`: ISO date string of when the item was added to the database (YYYY-MM-DD format) ## Implementation Notes ### Content Categorization The API automatically categorizes new content based on dates: - **Recently Added**: Items added to the database in the last 30 days - **Newly Opened**: Items that opened in the last year - **Upcoming**: Future openings (currently empty, reserved for future use) ### Caching - Trending content is cached for 24 hours - New content is cached for 30 minutes - Use the admin trigger endpoint to force cache refresh ### Error Handling All endpoints return standard HTTP status codes: - `200`: Success - `400`: Bad request (invalid parameters) - `403`: Forbidden (admin endpoints only) - `500`: Internal server error ### Rate Limiting No rate limiting is currently implemented, but it may be added in the future. ## Migration from Previous API Format If you were previously using the API with `location` fields, update your frontend code: **Before:** ```javascript const ride = { name: "Steel Vengeance", location: "Cedar Point", // OLD FIELD category: "ride" }; ``` **After:** ```javascript const ride = { name: "Steel Vengeance", park: "Cedar Point", // NEW FIELD category: "ride", date_opened: "2018-05-05" // NEW FIELD }; ``` ## Backend Architecture Changes The trending system has been migrated from Celery-based async processing to Django management commands for better reliability and simpler deployment: ### Management Commands - `python manage.py calculate_trending` - Calculate trending content - `python manage.py calculate_new_content` - Calculate new content ### Direct Calculation The API now uses direct calculation instead of async tasks, providing immediate results while maintaining performance through caching. ## URL Fields for Frontend Navigation All API responses now include dynamically generated `url` fields that provide direct links to the frontend pages for each entity. These URLs are generated based on the configured `FRONTEND_DOMAIN` setting. ### URL Patterns - **Parks**: `https://domain.com/parks/{park-slug}/` - **Rides**: `https://domain.com/parks/{park-slug}/rides/{ride-slug}/` - **Ride Models**: `https://domain.com/rides/manufacturers/{manufacturer-slug}/{model-slug}/` - **Companies (Operators)**: `https://domain.com/parks/operators/{operator-slug}/` - **Companies (Property Owners)**: `https://domain.com/parks/owners/{owner-slug}/` - **Companies (Manufacturers)**: `https://domain.com/rides/manufacturers/{manufacturer-slug}/` - **Companies (Designers)**: `https://domain.com/rides/designers/{designer-slug}/` ### Domain Separation Rules **CRITICAL**: Company URLs follow strict domain separation: - **Parks Domain**: OPERATOR and PROPERTY_OWNER roles generate URLs under `/parks/` - **Rides Domain**: MANUFACTURER and DESIGNER roles generate URLs under `/rides/` - Companies with multiple roles use their primary role (first in the roles array) for URL generation - URLs are auto-generated when entities are saved and stored in the database ### Example Response with URL Fields ```json { "id": 1, "name": "Steel Vengeance", "slug": "steel-vengeance", "park": { "id": 1, "name": "Cedar Point", "slug": "cedar-point", "url": "https://thrillwiki.com/parks/cedar-point/" }, "url": "https://thrillwiki.com/parks/cedar-point/rides/steel-vengeance/", "manufacturer": { "id": 1, "name": "Rocky Mountain Construction", "slug": "rocky-mountain-construction", "url": "https://thrillwiki.com/rides/manufacturers/rocky-mountain-construction/" } } ``` ## Example Usage ### Fetch Trending Content ```javascript const response = await fetch('/api/v1/trending/content/?limit=10'); const data = await response.json(); // Display trending rides with clickable links data.trending_rides.forEach(ride => { console.log(`${ride.name} at ${ride.park} - opened ${ride.date_opened}`); console.log(`Visit: ${ride.url}`); }); ``` ### Fetch New Content ```javascript const response = await fetch('/api/v1/trending/new/?limit=5&days=7'); const data = await response.json(); // Display newly opened attractions data.newly_opened.forEach(item => { console.log(`${item.name} at ${item.park} - opened ${item.date_opened}`); }); ``` ### Admin: Trigger Calculation ```javascript const response = await fetch('/api/v1/trending/calculate/', { method: 'POST', headers: { 'Authorization': 'Bearer YOUR_ADMIN_TOKEN', 'Content-Type': 'application/json' } }); const result = await response.json(); console.log(result.message); ## Reviews Endpoints ### Latest Reviews Get the latest reviews from both parks and rides across the platform. **Endpoint:** `GET /reviews/latest/` **Parameters:** - `limit` (optional): Number of reviews to return (default: 20, max: 100) **Response Format:** ```json { "count": 15, "results": [ { "id": 42, "type": "ride", "title": "Amazing coaster experience!", "content_snippet": "This ride was absolutely incredible. The airtime was perfect and the inversions were smooth...", "rating": 9, "created_at": "2025-08-28T21:30:00Z", "user": { "username": "coaster_fan_2024", "display_name": "Coaster Fan", "avatar_url": "https://media.thrillwiki.com/avatars/user123.jpg" }, "subject_name": "Steel Vengeance", "subject_slug": "steel-vengeance", "subject_url": "/parks/cedar-point/rides/steel-vengeance/", "park_name": "Cedar Point", "park_slug": "cedar-point", "park_url": "/parks/cedar-point/" }, { "id": 38, "type": "park", "title": "Great family park", "content_snippet": "Had a wonderful time with the family. The park was clean, staff was friendly, and there were rides for all ages...", "rating": 8, "created_at": "2025-08-28T20:15:00Z", "user": { "username": "family_fun", "display_name": "Family Fun", "avatar_url": "/static/images/default-avatar.png" }, "subject_name": "Dollywood", "subject_slug": "dollywood", "subject_url": "/parks/dollywood/", "park_name": null, "park_slug": null, "park_url": null } ] } ``` **Field Descriptions:** - `id`: Unique review identifier - `type`: Review type - "park" or "ride" - `title`: Review title/headline - `content_snippet`: Truncated review content (max 150 characters with smart word breaking) - `rating`: User rating from 1-10 - `created_at`: ISO timestamp when review was created - `user`: User information object - `username`: User's unique username - `display_name`: User's display name (falls back to username if not set) - `avatar_url`: URL to user's avatar image (uses default if not set) - `subject_name`: Name of the reviewed item (park or ride) - `subject_slug`: URL slug of the reviewed item - `subject_url`: Frontend URL to the reviewed item's detail page - `park_name`: For ride reviews, the name of the parent park (null for park reviews) - `park_slug`: For ride reviews, the slug of the parent park (null for park reviews) - `park_url`: For ride reviews, the URL to the parent park (null for park reviews) **Authentication:** None required (public endpoint) **Example Usage:** ```javascript // Fetch latest 10 reviews const response = await fetch('/api/v1/reviews/latest/?limit=10'); const data = await response.json(); // Display reviews data.results.forEach(review => { console.log(`${review.user.display_name} rated ${review.subject_name}: ${review.rating}/10`); console.log(`"${review.title}" - ${review.content_snippet}`); if (review.type === 'ride') { console.log(`Ride at ${review.park_name}`); } }); ``` **Error Responses:** - `400 Bad Request`: Invalid limit parameter - `500 Internal Server Error`: Database or server error **Notes:** - Reviews are filtered to only show published reviews (`is_published=True`) - Results are sorted by creation date (newest first) - Content snippets are intelligently truncated at word boundaries - Avatar URLs fall back to default avatar if user hasn't uploaded one - The endpoint combines reviews from both parks and rides into a single chronological feed