from django.db import models from django.urls import reverse from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType from django.core.validators import MinValueValidator, MaxValueValidator from history_tracking.models import HistoricalModel, VersionBranch, ChangeSet from history_tracking.signals import get_current_branch, ChangesetContextManager class Review(HistoricalModel): # Generic relation to allow reviews on different types (rides, parks) content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) object_id = models.PositiveIntegerField() content_object = GenericForeignKey('content_type', 'object_id') # Review details user = models.ForeignKey( 'accounts.User', on_delete=models.CASCADE, related_name='reviews' ) rating = models.PositiveSmallIntegerField( validators=[MinValueValidator(1), MaxValueValidator(10)] ) title = models.CharField(max_length=200) content = models.TextField() visit_date = models.DateField() # Metadata created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) # Moderation is_published = models.BooleanField(default=True) moderation_notes = models.TextField(blank=True) moderated_by = models.ForeignKey( 'accounts.User', on_delete=models.SET_NULL, null=True, blank=True, related_name='moderated_reviews' ) moderated_at = models.DateTimeField(null=True, blank=True) class Meta: ordering = ['-created_at'] indexes = [ models.Index(fields=['content_type', 'object_id']), ] def __str__(self): return f"Review of {self.content_object} by {self.user.username}" def save(self, *args, **kwargs) -> None: # Get the branch from context or use default current_branch = get_current_branch() if current_branch: # Save in the context of the current branch super().save(*args, **kwargs) else: # If no branch context, save in main branch main_branch, _ = VersionBranch.objects.get_or_create( name='main', defaults={'metadata': {'type': 'default_branch'}} ) with ChangesetContextManager(branch=main_branch): super().save(*args, **kwargs) def get_version_info(self) -> dict: """Get version control information for this review and its reviewed object""" content_type = ContentType.objects.get_for_model(self) latest_changes = ChangeSet.objects.filter( content_type=content_type, object_id=self.pk, status='applied' ).order_by('-created_at')[:5] active_branches = VersionBranch.objects.filter( changesets__content_type=content_type, changesets__object_id=self.pk, is_active=True ).distinct() # Get version info for the reviewed object if it's version controlled reviewed_object_branch = None if hasattr(self.content_object, 'get_version_info'): reviewed_object_branch = self.content_object.get_version_info().get('current_branch') return { 'latest_changes': latest_changes, 'active_branches': active_branches, 'current_branch': get_current_branch(), 'total_changes': latest_changes.count(), 'reviewed_object_branch': reviewed_object_branch } def get_absolute_url(self) -> str: """Get the absolute URL for this review""" if hasattr(self.content_object, 'get_absolute_url'): base_url = self.content_object.get_absolute_url() return f"{base_url}#review-{self.pk}" return reverse('reviews:review_detail', kwargs={'pk': self.pk}) class ReviewImage(models.Model): review = models.ForeignKey( Review, on_delete=models.CASCADE, related_name='images' ) image = models.ImageField(upload_to='review_images/') caption = models.CharField(max_length=200, blank=True) order = models.PositiveIntegerField(default=0) class Meta: ordering = ['order'] def __str__(self): return f"Image {self.order + 1} for {self.review}" class ReviewLike(models.Model): review = models.ForeignKey( Review, on_delete=models.CASCADE, related_name='likes' ) user = models.ForeignKey( 'accounts.User', on_delete=models.CASCADE, related_name='review_likes' ) created_at = models.DateTimeField(auto_now_add=True) class Meta: unique_together = ['review', 'user'] def __str__(self): return f"{self.user.username} likes {self.review}" class ReviewReport(models.Model): review = models.ForeignKey( Review, on_delete=models.CASCADE, related_name='reports' ) user = models.ForeignKey( 'accounts.User', on_delete=models.CASCADE, related_name='review_reports' ) reason = models.TextField() created_at = models.DateTimeField(auto_now_add=True) resolved = models.BooleanField(default=False) resolved_by = models.ForeignKey( 'accounts.User', on_delete=models.SET_NULL, null=True, blank=True, related_name='resolved_review_reports' ) resolution_notes = models.TextField(blank=True) resolved_at = models.DateTimeField(null=True, blank=True) class Meta: ordering = ['-created_at'] def __str__(self): return f"Report on {self.review} by {self.user.username}"