Files
thrillwiki_django_no_react/docs/frontend.md
pacnpal ac745cc541 ok
2025-08-28 23:20:09 -04:00

421 lines
13 KiB
Markdown

# 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