mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 06:11:07 -05:00
Refactor code structure and remove redundant changes
This commit is contained in:
364
backend/apps/api/v1/views/trending.py
Normal file
364
backend/apps/api/v1/views/trending.py
Normal file
@@ -0,0 +1,364 @@
|
||||
"""
|
||||
Trending content API views for ThrillWiki API v1.
|
||||
|
||||
This module contains endpoints for trending and new content discovery
|
||||
including trending parks, rides, and recently added content.
|
||||
"""
|
||||
|
||||
from datetime import datetime, date
|
||||
from django.utils import timezone
|
||||
from rest_framework import status
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.request import Request
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.permissions import AllowAny
|
||||
from drf_spectacular.utils import extend_schema, extend_schema_view
|
||||
from drf_spectacular.types import OpenApiTypes
|
||||
|
||||
|
||||
@extend_schema_view(
|
||||
get=extend_schema(
|
||||
summary="Get trending content",
|
||||
description="Retrieve trending parks and rides based on view counts, ratings, and recency.",
|
||||
parameters=[
|
||||
{
|
||||
"name": "limit",
|
||||
"in": "query",
|
||||
"description": "Number of trending items to return (default: 20, max: 100)",
|
||||
"required": False,
|
||||
"schema": {"type": "integer", "default": 20, "maximum": 100},
|
||||
},
|
||||
{
|
||||
"name": "timeframe",
|
||||
"in": "query",
|
||||
"description": "Timeframe for trending calculation (day, week, month) - default: week",
|
||||
"required": False,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"enum": ["day", "week", "month"],
|
||||
"default": "week",
|
||||
},
|
||||
},
|
||||
],
|
||||
responses={200: OpenApiTypes.OBJECT},
|
||||
tags=["Trending"],
|
||||
),
|
||||
)
|
||||
class TrendingAPIView(APIView):
|
||||
"""API endpoint for trending content."""
|
||||
|
||||
permission_classes = [AllowAny]
|
||||
|
||||
def get(self, request: Request) -> Response:
|
||||
"""Get trending parks and rides."""
|
||||
try:
|
||||
from apps.core.services.trending_service import TrendingService
|
||||
except ImportError:
|
||||
# Fallback if trending service is not available
|
||||
return self._get_fallback_trending_content(request)
|
||||
|
||||
# Parse parameters
|
||||
limit = min(int(request.query_params.get("limit", 20)), 100)
|
||||
|
||||
# Get trending content
|
||||
trending_service = TrendingService()
|
||||
all_trending = trending_service.get_trending_content(limit=limit * 2)
|
||||
|
||||
# Separate by content type
|
||||
trending_rides = []
|
||||
trending_parks = []
|
||||
|
||||
for item in all_trending:
|
||||
if item.get("category") == "ride":
|
||||
trending_rides.append(item)
|
||||
elif item.get("category") == "park":
|
||||
trending_parks.append(item)
|
||||
|
||||
# Limit each category
|
||||
trending_rides = trending_rides[: limit // 3] if trending_rides else []
|
||||
trending_parks = trending_parks[: limit // 3] if trending_parks else []
|
||||
|
||||
# Create mock latest reviews (since not implemented yet)
|
||||
latest_reviews = [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Steel Vengeance Review",
|
||||
"location": "Cedar Point",
|
||||
"category": "Roller Coaster",
|
||||
"rating": 5.0,
|
||||
"rank": 1,
|
||||
"views": 1234,
|
||||
"views_change": "+45%",
|
||||
"slug": "steel-vengeance-review",
|
||||
}
|
||||
][: limit // 3]
|
||||
|
||||
# Return in expected frontend format
|
||||
response_data = {
|
||||
"trending_rides": trending_rides,
|
||||
"trending_parks": trending_parks,
|
||||
"latest_reviews": latest_reviews,
|
||||
}
|
||||
|
||||
return Response(response_data)
|
||||
|
||||
def _get_fallback_trending_content(self, request: Request) -> Response:
|
||||
"""Fallback method when trending service is not available."""
|
||||
limit = min(int(request.query_params.get("limit", 20)), 100)
|
||||
|
||||
# Mock trending data
|
||||
trending_rides = [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Steel Vengeance",
|
||||
"location": "Cedar Point",
|
||||
"category": "Roller Coaster",
|
||||
"rating": 4.8,
|
||||
"rank": 1,
|
||||
"views": 15234,
|
||||
"views_change": "+25%",
|
||||
"slug": "steel-vengeance",
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "Lightning Rod",
|
||||
"location": "Dollywood",
|
||||
"category": "Roller Coaster",
|
||||
"rating": 4.7,
|
||||
"rank": 2,
|
||||
"views": 12456,
|
||||
"views_change": "+18%",
|
||||
"slug": "lightning-rod",
|
||||
},
|
||||
][: limit // 3]
|
||||
|
||||
trending_parks = [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Cedar Point",
|
||||
"location": "Sandusky, OH",
|
||||
"category": "Theme Park",
|
||||
"rating": 4.6,
|
||||
"rank": 1,
|
||||
"views": 45678,
|
||||
"views_change": "+12%",
|
||||
"slug": "cedar-point",
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "Magic Kingdom",
|
||||
"location": "Orlando, FL",
|
||||
"category": "Theme Park",
|
||||
"rating": 4.5,
|
||||
"rank": 2,
|
||||
"views": 67890,
|
||||
"views_change": "+8%",
|
||||
"slug": "magic-kingdom",
|
||||
},
|
||||
][: limit // 3]
|
||||
|
||||
latest_reviews = [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Steel Vengeance Review",
|
||||
"location": "Cedar Point",
|
||||
"category": "Roller Coaster",
|
||||
"rating": 5.0,
|
||||
"rank": 1,
|
||||
"views": 1234,
|
||||
"views_change": "+45%",
|
||||
"slug": "steel-vengeance-review",
|
||||
}
|
||||
][: limit // 3]
|
||||
|
||||
response_data = {
|
||||
"trending_rides": trending_rides,
|
||||
"trending_parks": trending_parks,
|
||||
"latest_reviews": latest_reviews,
|
||||
}
|
||||
|
||||
return Response(response_data)
|
||||
|
||||
|
||||
@extend_schema_view(
|
||||
get=extend_schema(
|
||||
summary="Get new content",
|
||||
description="Retrieve recently added parks and rides.",
|
||||
parameters=[
|
||||
{
|
||||
"name": "limit",
|
||||
"in": "query",
|
||||
"description": "Number of new items to return (default: 20, max: 100)",
|
||||
"required": False,
|
||||
"schema": {"type": "integer", "default": 20, "maximum": 100},
|
||||
},
|
||||
{
|
||||
"name": "days",
|
||||
"in": "query",
|
||||
"description": "Number of days to look back for new content (default: 30, max: 365)",
|
||||
"required": False,
|
||||
"schema": {"type": "integer", "default": 30, "maximum": 365},
|
||||
},
|
||||
],
|
||||
responses={200: OpenApiTypes.OBJECT},
|
||||
tags=["Trending"],
|
||||
),
|
||||
)
|
||||
class NewContentAPIView(APIView):
|
||||
"""API endpoint for new content."""
|
||||
|
||||
permission_classes = [AllowAny]
|
||||
|
||||
def get(self, request: Request) -> Response:
|
||||
"""Get new parks and rides."""
|
||||
try:
|
||||
from apps.core.services.trending_service import TrendingService
|
||||
except ImportError:
|
||||
# Fallback if trending service is not available
|
||||
return self._get_fallback_new_content(request)
|
||||
|
||||
# Parse parameters
|
||||
limit = min(int(request.query_params.get("limit", 20)), 100)
|
||||
|
||||
# Get new content with longer timeframe to get more data
|
||||
trending_service = TrendingService()
|
||||
all_new_content = trending_service.get_new_content(
|
||||
limit=limit * 2, days_back=60
|
||||
)
|
||||
|
||||
recently_added = []
|
||||
newly_opened = []
|
||||
upcoming = []
|
||||
|
||||
# Categorize items based on date
|
||||
today = date.today()
|
||||
|
||||
for item in all_new_content:
|
||||
date_added = item.get("date_added", "")
|
||||
if date_added:
|
||||
try:
|
||||
# Parse the date string
|
||||
if isinstance(date_added, str):
|
||||
item_date = datetime.fromisoformat(date_added).date()
|
||||
else:
|
||||
item_date = date_added
|
||||
|
||||
# Calculate days difference
|
||||
days_diff = (today - item_date).days
|
||||
|
||||
if days_diff <= 30: # Recently added (last 30 days)
|
||||
recently_added.append(item)
|
||||
elif days_diff <= 365: # Newly opened (last year)
|
||||
newly_opened.append(item)
|
||||
else: # Older items
|
||||
newly_opened.append(item)
|
||||
|
||||
except (ValueError, TypeError):
|
||||
# If date parsing fails, add to recently added
|
||||
recently_added.append(item)
|
||||
else:
|
||||
recently_added.append(item)
|
||||
|
||||
# Create mock upcoming items
|
||||
upcoming = [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Epic Universe",
|
||||
"location": "Universal Orlando",
|
||||
"category": "Theme Park",
|
||||
"date_added": "Opening 2025",
|
||||
"slug": "epic-universe",
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "New Fantasyland Expansion",
|
||||
"location": "Magic Kingdom",
|
||||
"category": "Land Expansion",
|
||||
"date_added": "Opening 2026",
|
||||
"slug": "fantasyland-expansion",
|
||||
},
|
||||
]
|
||||
|
||||
# Limit each category
|
||||
recently_added = recently_added[: limit // 3] if recently_added else []
|
||||
newly_opened = newly_opened[: limit // 3] if newly_opened else []
|
||||
upcoming = upcoming[: limit // 3] if upcoming else []
|
||||
|
||||
# Return in expected frontend format
|
||||
response_data = {
|
||||
"recently_added": recently_added,
|
||||
"newly_opened": newly_opened,
|
||||
"upcoming": upcoming,
|
||||
}
|
||||
|
||||
return Response(response_data)
|
||||
|
||||
def _get_fallback_new_content(self, request: Request) -> Response:
|
||||
"""Fallback method when trending service is not available."""
|
||||
limit = min(int(request.query_params.get("limit", 20)), 100)
|
||||
|
||||
# Mock new content data
|
||||
recently_added = [
|
||||
{
|
||||
"id": 1,
|
||||
"name": "Iron Gwazi",
|
||||
"location": "Busch Gardens Tampa",
|
||||
"category": "Roller Coaster",
|
||||
"date_added": "2024-12-01",
|
||||
"slug": "iron-gwazi",
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"name": "VelociCoaster",
|
||||
"location": "Universal's Islands of Adventure",
|
||||
"category": "Roller Coaster",
|
||||
"date_added": "2024-11-15",
|
||||
"slug": "velocicoaster",
|
||||
},
|
||||
][: limit // 3]
|
||||
|
||||
newly_opened = [
|
||||
{
|
||||
"id": 3,
|
||||
"name": "Guardians of the Galaxy",
|
||||
"location": "EPCOT",
|
||||
"category": "Roller Coaster",
|
||||
"date_added": "2024-10-01",
|
||||
"slug": "guardians-galaxy",
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"name": "TRON Lightcycle Run",
|
||||
"location": "Magic Kingdom",
|
||||
"category": "Roller Coaster",
|
||||
"date_added": "2024-09-15",
|
||||
"slug": "tron-lightcycle",
|
||||
},
|
||||
][: limit // 3]
|
||||
|
||||
upcoming = [
|
||||
{
|
||||
"id": 5,
|
||||
"name": "Epic Universe",
|
||||
"location": "Universal Orlando",
|
||||
"category": "Theme Park",
|
||||
"date_added": "Opening 2025",
|
||||
"slug": "epic-universe",
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"name": "New Fantasyland Expansion",
|
||||
"location": "Magic Kingdom",
|
||||
"category": "Land Expansion",
|
||||
"date_added": "Opening 2026",
|
||||
"slug": "fantasyland-expansion",
|
||||
},
|
||||
][: limit // 3]
|
||||
|
||||
response_data = {
|
||||
"recently_added": recently_added,
|
||||
"newly_opened": newly_opened,
|
||||
"upcoming": upcoming,
|
||||
}
|
||||
|
||||
return Response(response_data)
|
||||
Reference in New Issue
Block a user