feat: Add blog, media, and support apps, implement ride credits and image API, and remove toplist feature.

This commit is contained in:
pacnpal
2025-12-26 15:15:28 -05:00
parent cd8868a591
commit 00699d53b4
77 changed files with 7274 additions and 538 deletions

View File

@@ -0,0 +1,94 @@
import json
from django.core.serializers.json import DjangoJSONEncoder
from django.utils import timezone
from .models import User
class UserExportService:
"""Service for exporting all user data."""
@staticmethod
def export_user_data(user: User) -> dict:
"""
Export all data associated with a user or an object containing counts/metadata and actual data.
Args:
user: The user to export data for
Returns:
dict: The complete user data export
"""
# Import models locally to avoid circular imports
from apps.parks.models import ParkReview
from apps.rides.models import RideReview
from apps.lists.models import UserList
# User account and profile
user_data = {
"username": user.username,
"email": user.email,
"date_joined": user.date_joined,
"first_name": user.first_name,
"last_name": user.last_name,
"is_active": user.is_active,
"role": user.role,
}
profile_data = {}
if hasattr(user, "profile"):
profile = user.profile
profile_data = {
"display_name": profile.display_name,
"bio": profile.bio,
"location": profile.location,
"pronouns": profile.pronouns,
"unit_system": profile.unit_system,
"social_media": {
"twitter": profile.twitter,
"instagram": profile.instagram,
"youtube": profile.youtube,
"discord": profile.discord,
},
"ride_credits": {
"coaster": profile.coaster_credits,
"dark_ride": profile.dark_ride_credits,
"flat_ride": profile.flat_ride_credits,
"water_ride": profile.water_ride_credits,
}
}
# Reviews
park_reviews = list(ParkReview.objects.filter(user=user).values(
"park__name", "rating", "review", "created_at", "updated_at", "is_published"
))
ride_reviews = list(RideReview.objects.filter(user=user).values(
"ride__name", "rating", "review", "created_at", "updated_at", "is_published"
))
# Lists
user_lists = []
for user_list in UserList.objects.filter(user=user):
items = list(user_list.items.values("order", "content_type__model", "object_id", "comment"))
user_lists.append({
"title": user_list.title,
"description": user_list.description,
"created_at": user_list.created_at,
"items": items
})
export_data = {
"account": user_data,
"profile": profile_data,
"preferences": getattr(user, "notification_preferences", {}),
"content": {
"park_reviews": park_reviews,
"ride_reviews": ride_reviews,
"lists": user_lists,
},
"export_info": {
"generated_at": timezone.now(),
"version": "1.0"
}
}
return export_data

View File

@@ -13,29 +13,11 @@ class Migration(migrations.Migration):
dependencies = [
("accounts", "0013_add_user_query_indexes"),
("contenttypes", "0002_remove_content_type_name"),
(
"django_cloudflareimages_toolkit",
"0002_rename_cloudflare_i_user_id_b8c8a5_idx_cloudflare__user_id_a3ad50_idx_and_more",
),
("django_cloudflareimages_toolkit", "0001_initial"),
]
operations = [
migrations.RemoveField(
model_name="toplist",
name="user",
),
migrations.RemoveField(
model_name="toplistitem",
name="top_list",
),
migrations.AlterUniqueTogether(
name="toplistitem",
unique_together=None,
),
migrations.RemoveField(
model_name="toplistitem",
name="content_type",
),
migrations.AlterModelOptions(
name="user",
options={"verbose_name": "User", "verbose_name_plural": "Users"},

View File

@@ -15,15 +15,12 @@ from apps.accounts.admin import (
CustomUserAdmin,
EmailVerificationAdmin,
PasswordResetAdmin,
TopListAdmin,
TopListItemAdmin,
UserProfileAdmin,
)
from apps.accounts.models import (
EmailVerification,
PasswordReset,
TopList,
TopListItem,
User,
UserProfile,
)
@@ -157,51 +154,4 @@ class TestPasswordResetAdmin(TestCase):
assert "cleanup_old_tokens" in actions
class TestTopListAdmin(TestCase):
"""Tests for TopListAdmin class."""
def setUp(self):
self.factory = RequestFactory()
self.site = AdminSite()
self.admin = TopListAdmin(model=TopList, admin_site=self.site)
def test_list_select_related(self):
"""Verify select_related for user."""
assert "user" in self.admin.list_select_related
def test_list_prefetch_related(self):
"""Verify prefetch_related for items."""
assert "items" in self.admin.list_prefetch_related
def test_publish_actions(self):
"""Verify publish actions exist."""
request = self.factory.get("/admin/")
request.user = UserModel(is_superuser=True)
actions = self.admin.get_actions(request)
assert "publish_lists" in actions
assert "unpublish_lists" in actions
class TestTopListItemAdmin(TestCase):
"""Tests for TopListItemAdmin class."""
def setUp(self):
self.factory = RequestFactory()
self.site = AdminSite()
self.admin = TopListItemAdmin(model=TopListItem, admin_site=self.site)
def test_list_select_related(self):
"""Verify select_related for top_list and user."""
assert "top_list" in self.admin.list_select_related
assert "top_list__user" in self.admin.list_select_related
assert "content_type" in self.admin.list_select_related
def test_reorder_actions(self):
"""Verify reorder actions exist."""
request = self.factory.get("/admin/")
request.user = UserModel(is_superuser=True)
actions = self.admin.get_actions(request)
assert "move_up" in actions
assert "move_down" in actions