mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-22 03:31:13 -05:00
Implement reviews and voting system
- Added Review model with fields for user, content type, title, content, rating, visit metadata, helpful votes, moderation status, and timestamps. - Created ReviewHelpfulVote model to track user votes on reviews. - Implemented moderation workflow for reviews with approve and reject methods. - Developed admin interface for managing reviews and helpful votes, including custom display methods and actions for bulk approval/rejection. - Added migrations for the new models and their relationships. - Ensured unique constraints and indexes for efficient querying.
This commit is contained in:
@@ -255,3 +255,165 @@ class UserProfile(BaseModel):
|
||||
status='approved'
|
||||
).count()
|
||||
self.save(update_fields=['total_submissions', 'approved_submissions'])
|
||||
|
||||
|
||||
class UserRideCredit(BaseModel):
|
||||
"""
|
||||
Track which rides users have ridden (ride credits/coaster counting).
|
||||
|
||||
Users can log which rides they've been on and track their first ride date.
|
||||
"""
|
||||
|
||||
user = models.ForeignKey(
|
||||
User,
|
||||
on_delete=models.CASCADE,
|
||||
related_name='ride_credits'
|
||||
)
|
||||
ride = models.ForeignKey(
|
||||
'entities.Ride',
|
||||
on_delete=models.CASCADE,
|
||||
related_name='user_credits'
|
||||
)
|
||||
|
||||
# First ride date
|
||||
first_ride_date = models.DateField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Date of first ride"
|
||||
)
|
||||
|
||||
# Ride count for this specific ride
|
||||
ride_count = models.PositiveIntegerField(
|
||||
default=1,
|
||||
help_text="Number of times user has ridden this ride"
|
||||
)
|
||||
|
||||
# Notes about the ride experience
|
||||
notes = models.TextField(
|
||||
blank=True,
|
||||
help_text="User notes about this ride"
|
||||
)
|
||||
|
||||
class Meta:
|
||||
db_table = 'user_ride_credits'
|
||||
unique_together = [['user', 'ride']]
|
||||
ordering = ['-first_ride_date', '-created']
|
||||
indexes = [
|
||||
models.Index(fields=['user', 'first_ride_date']),
|
||||
models.Index(fields=['ride']),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.user.username} - {self.ride.name}"
|
||||
|
||||
@property
|
||||
def park(self):
|
||||
"""Get the park this ride is at"""
|
||||
return self.ride.park
|
||||
|
||||
|
||||
class UserTopList(BaseModel):
|
||||
"""
|
||||
User-created ranked lists (top parks, top rides, top coasters, etc.).
|
||||
|
||||
Users can create and share their personal rankings of parks, rides, and other entities.
|
||||
"""
|
||||
|
||||
LIST_TYPE_CHOICES = [
|
||||
('parks', 'Parks'),
|
||||
('rides', 'Rides'),
|
||||
('coasters', 'Coasters'),
|
||||
]
|
||||
|
||||
user = models.ForeignKey(
|
||||
User,
|
||||
on_delete=models.CASCADE,
|
||||
related_name='top_lists'
|
||||
)
|
||||
|
||||
# List metadata
|
||||
list_type = models.CharField(
|
||||
max_length=20,
|
||||
choices=LIST_TYPE_CHOICES,
|
||||
db_index=True,
|
||||
help_text="Type of entities in this list"
|
||||
)
|
||||
title = models.CharField(
|
||||
max_length=200,
|
||||
help_text="Title of the list"
|
||||
)
|
||||
description = models.TextField(
|
||||
blank=True,
|
||||
help_text="Description of the list"
|
||||
)
|
||||
|
||||
# Privacy
|
||||
is_public = models.BooleanField(
|
||||
default=True,
|
||||
db_index=True,
|
||||
help_text="Whether this list is publicly visible"
|
||||
)
|
||||
|
||||
class Meta:
|
||||
db_table = 'user_top_lists'
|
||||
ordering = ['-created']
|
||||
indexes = [
|
||||
models.Index(fields=['user', 'list_type']),
|
||||
models.Index(fields=['is_public', 'created']),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.user.username} - {self.title}"
|
||||
|
||||
@property
|
||||
def item_count(self):
|
||||
"""Get the number of items in this list"""
|
||||
return self.items.count()
|
||||
|
||||
|
||||
class UserTopListItem(BaseModel):
|
||||
"""
|
||||
Individual items in a user's top list with position and notes.
|
||||
"""
|
||||
|
||||
top_list = models.ForeignKey(
|
||||
UserTopList,
|
||||
on_delete=models.CASCADE,
|
||||
related_name='items'
|
||||
)
|
||||
|
||||
# Generic relation to park or ride
|
||||
from django.contrib.contenttypes.fields import GenericForeignKey
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
content_type = models.ForeignKey(
|
||||
ContentType,
|
||||
on_delete=models.CASCADE,
|
||||
limit_choices_to={'model__in': ('park', 'ride')}
|
||||
)
|
||||
object_id = models.PositiveIntegerField()
|
||||
content_object = GenericForeignKey('content_type', 'object_id')
|
||||
|
||||
# Position in list (1 = top)
|
||||
position = models.PositiveIntegerField(
|
||||
help_text="Position in the list (1 = top)"
|
||||
)
|
||||
|
||||
# Optional notes about this specific item
|
||||
notes = models.TextField(
|
||||
blank=True,
|
||||
help_text="User notes about why this item is ranked here"
|
||||
)
|
||||
|
||||
class Meta:
|
||||
db_table = 'user_top_list_items'
|
||||
ordering = ['position']
|
||||
unique_together = [['top_list', 'position']]
|
||||
indexes = [
|
||||
models.Index(fields=['top_list', 'position']),
|
||||
models.Index(fields=['content_type', 'object_id']),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
entity_name = str(self.content_object) if self.content_object else f"ID {self.object_id}"
|
||||
return f"#{self.position}: {entity_name}"
|
||||
|
||||
Reference in New Issue
Block a user