mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 04:31:09 -05:00
421 lines
13 KiB
Markdown
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
|