Refactor test utilities and enhance ASGI settings

- Cleaned up and standardized assertions in ApiTestMixin for API response validation.
- Updated ASGI settings to use os.environ for setting the DJANGO_SETTINGS_MODULE.
- Removed unused imports and improved formatting in settings.py.
- Refactored URL patterns in urls.py for better readability and organization.
- Enhanced view functions in views.py for consistency and clarity.
- Added .flake8 configuration for linting and style enforcement.
- Introduced type stubs for django-environ to improve type checking with Pylance.
This commit is contained in:
pacnpal
2025-08-20 19:51:59 -04:00
parent 69c07d1381
commit 66ed4347a9
230 changed files with 15094 additions and 11578 deletions

View File

@@ -6,7 +6,6 @@ Following Django styleguide pattern for business logic encapsulation.
from typing import Optional, Dict, Any, Union
from django.db import transaction
from django.utils import timezone
from django.core.exceptions import ValidationError
from django.contrib.auth.models import User
from django.db.models import QuerySet
@@ -15,25 +14,22 @@ from .models import EditSubmission
class ModerationService:
"""Service for handling content moderation workflows."""
@staticmethod
def approve_submission(
*,
submission_id: int,
moderator: User,
notes: Optional[str] = None
*, submission_id: int, moderator: User, notes: Optional[str] = None
) -> Union[object, None]:
"""
Approve a content submission and apply changes.
Args:
submission_id: ID of the submission to approve
moderator: User performing the approval
notes: Optional notes about the approval
Returns:
The created/updated object or None if approval failed
Raises:
EditSubmission.DoesNotExist: If submission doesn't exist
ValidationError: If submission data is invalid
@@ -43,14 +39,15 @@ class ModerationService:
submission = EditSubmission.objects.select_for_update().get(
id=submission_id
)
if submission.status != 'PENDING':
if submission.status != "PENDING":
raise ValueError(f"Submission {submission_id} is not pending approval")
try:
# Call the model's approve method which handles the business logic
# Call the model's approve method which handles the business
# logic
obj = submission.approve(moderator)
# Add moderator notes if provided
if notes:
if submission.notes:
@@ -58,36 +55,33 @@ class ModerationService:
else:
submission.notes = f"[Moderator]: {notes}"
submission.save()
return obj
except Exception as e:
# Mark as rejected on any error
submission.status = 'REJECTED'
submission.status = "REJECTED"
submission.handled_by = moderator
submission.handled_at = timezone.now()
submission.notes = f"Approval failed: {str(e)}"
submission.save()
raise
@staticmethod
def reject_submission(
*,
submission_id: int,
moderator: User,
reason: str
*, submission_id: int, moderator: User, reason: str
) -> EditSubmission:
"""
Reject a content submission.
Args:
submission_id: ID of the submission to reject
moderator: User performing the rejection
reason: Reason for rejection
Returns:
Updated submission object
Raises:
EditSubmission.DoesNotExist: If submission doesn't exist
ValueError: If submission cannot be rejected
@@ -96,21 +90,21 @@ class ModerationService:
submission = EditSubmission.objects.select_for_update().get(
id=submission_id
)
if submission.status != 'PENDING':
if submission.status != "PENDING":
raise ValueError(f"Submission {submission_id} is not pending review")
submission.status = 'REJECTED'
submission.status = "REJECTED"
submission.handled_by = moderator
submission.handled_at = timezone.now()
submission.notes = f"Rejected: {reason}"
# Call full_clean before saving - CRITICAL STYLEGUIDE FIX
submission.full_clean()
submission.save()
return submission
@staticmethod
def create_edit_submission(
*,
@@ -118,21 +112,21 @@ class ModerationService:
changes: Dict[str, Any],
submitter: User,
submission_type: str = "UPDATE",
notes: Optional[str] = None
notes: Optional[str] = None,
) -> EditSubmission:
"""
Create a new edit submission for moderation.
Args:
content_object: The object being edited
changes: Dictionary of field changes
submitter: User submitting the changes
submission_type: Type of submission ("CREATE" or "UPDATE")
notes: Optional notes about the submission
Returns:
Created EditSubmission object
Raises:
ValidationError: If submission data is invalid
"""
@@ -141,33 +135,33 @@ class ModerationService:
changes=changes,
submitted_by=submitter,
submission_type=submission_type,
notes=notes or ""
notes=notes or "",
)
# Call full_clean before saving - CRITICAL STYLEGUIDE FIX
submission.full_clean()
submission.save()
return submission
@staticmethod
def update_submission_changes(
*,
submission_id: int,
moderator_changes: Dict[str, Any],
moderator: User
moderator: User,
) -> EditSubmission:
"""
Update submission with moderator changes before approval.
Args:
submission_id: ID of the submission to update
moderator_changes: Dictionary of moderator modifications
moderator: User making the changes
Returns:
Updated submission object
Raises:
EditSubmission.DoesNotExist: If submission doesn't exist
ValueError: If submission cannot be modified
@@ -176,25 +170,25 @@ class ModerationService:
submission = EditSubmission.objects.select_for_update().get(
id=submission_id
)
if submission.status != 'PENDING':
if submission.status != "PENDING":
raise ValueError(f"Submission {submission_id} is not pending review")
submission.moderator_changes = moderator_changes
# Add note about moderator changes
note = f"[Moderator changes by {moderator.username}]"
if submission.notes:
submission.notes += f"\n{note}"
else:
submission.notes = note
# Call full_clean before saving - CRITICAL STYLEGUIDE FIX
submission.full_clean()
submission.save()
return submission
@staticmethod
def get_pending_submissions_for_moderator(
*,
@@ -204,41 +198,33 @@ class ModerationService:
) -> QuerySet:
"""
Get pending submissions for a moderator to review.
Args:
moderator: The moderator user
content_type: Optional filter by content type
limit: Maximum number of submissions to return
Returns:
QuerySet of pending submissions
"""
from .selectors import pending_submissions_for_review
return pending_submissions_for_review(
content_type=content_type,
limit=limit
)
return pending_submissions_for_review(content_type=content_type, limit=limit)
@staticmethod
def get_submission_statistics(
*,
days: int = 30,
moderator: Optional[User] = None
*, days: int = 30, moderator: Optional[User] = None
) -> Dict[str, Any]:
"""
Get moderation statistics for a time period.
Args:
days: Number of days to analyze
moderator: Optional filter by specific moderator
Returns:
Dictionary containing moderation statistics
"""
from .selectors import moderation_statistics_summary
return moderation_statistics_summary(
days=days,
moderator=moderator
)
return moderation_statistics_summary(days=days, moderator=moderator)