Add ReviewEvent model and ReviewSubmissionService for review management

- Created a new ReviewEvent model to track review events with fields for content, rating, moderation status, and timestamps.
- Added ForeignKey relationships to connect ReviewEvent with ContentSubmission, User, and Review.
- Implemented ReviewSubmissionService to handle review submissions, including creation, updates, and moderation workflows.
- Introduced atomic transactions to ensure data integrity during review submissions and updates.
- Added logging for review submission and moderation actions for better traceability.
- Implemented validation to prevent duplicate reviews and ensure only the review owner can update their review.
This commit is contained in:
pacnpal
2025-11-08 16:49:58 -05:00
parent 618310a87b
commit 9122320e7e
18 changed files with 3170 additions and 171 deletions

View File

@@ -15,6 +15,7 @@ from ninja.pagination import paginate, PageNumberPagination
import logging
from apps.reviews.models import Review, ReviewHelpfulVote
from apps.reviews.services import ReviewSubmissionService
from apps.entities.models import Park, Ride
from apps.users.permissions import jwt_auth, require_auth
from ..schemas import (
@@ -98,7 +99,7 @@ def _serialize_review(review: Review, user=None) -> dict:
@require_auth
def create_review(request, data: ReviewCreateSchema):
"""
Create a new review for a park or ride.
Create a new review for a park or ride through the Sacred Pipeline.
**Authentication:** Required
@@ -111,9 +112,13 @@ def create_review(request, data: ReviewCreateSchema):
- visit_date: Optional visit date
- wait_time_minutes: Optional wait time
**Returns:** Created review (pending moderation)
**Returns:** Created review or submission confirmation
**Note:** Reviews automatically enter moderation workflow.
**Flow:**
- Moderators: Review created immediately (bypass moderation)
- Regular users: Submission created, enters moderation queue
**Note:** All reviews flow through ContentSubmission pipeline.
Users can only create one review per entity.
"""
try:
@@ -122,37 +127,31 @@ def create_review(request, data: ReviewCreateSchema):
# Get and validate entity
entity, content_type = _get_entity(data.entity_type, data.entity_id)
# Check for duplicate review
existing = Review.objects.filter(
# Create review through Sacred Pipeline
submission, review = ReviewSubmissionService.create_review_submission(
user=user,
content_type=content_type,
object_id=entity.id
).first()
if existing:
return 409, {
'detail': f"You have already reviewed this {data.entity_type}. "
f"Use PUT /reviews/{existing.id}/ to update your review."
}
# Create review
review = Review.objects.create(
user=user,
content_type=content_type,
object_id=entity.id,
entity=entity,
rating=data.rating,
title=data.title,
content=data.content,
rating=data.rating,
visit_date=data.visit_date,
wait_time_minutes=data.wait_time_minutes,
moderation_status=Review.MODERATION_PENDING,
source='api'
)
logger.info(f"Review created: {review.id} by {user.email} for {data.entity_type} {entity.id}")
# If moderator bypass happened, Review was created immediately
if review:
logger.info(f"Review created (moderator): {review.id} by {user.email}")
review_data = _serialize_review(review, user)
return 201, review_data
# Serialize and return
review_data = _serialize_review(review, user)
return 201, review_data
# Regular user: submission pending moderation
logger.info(f"Review submission created: {submission.id} by {user.email}")
return 201, {
'submission_id': str(submission.id),
'status': 'pending_moderation',
'message': 'Review submitted for moderation. You will be notified when it is approved.',
}
except ValidationError as e:
return 400, {'detail': str(e)}