Skip to content

Conversation

@naincy128
Copy link
Collaborator

@naincy128 naincy128 commented Dec 16, 2025

Description

This update introduces a comprehensive Mute / Unmute feature for discussion forums, enabling learners and staff to manage unwanted interactions more effectively while maintaining a healthy learning environment. The feature supports both personal and course-wide mute scopes, with clear role-based restrictions and overrides.

The implementation ensures muted content is hidden retroactively as well as for future posts, without notifying muted users. Special handling is included to prevent learners from muting staff or themselves, while giving staff full moderation control across the course.

Features

Learner Capabilities

  • Added ability for learners to mute other learners (staff cannot be muted).
  • Added personal mute list view and management.
  • Enabled unmuting of previously muted users.
  • Introduced “Mute and Report” action to mute a user and report their content in a single step.
  • Prevented learners from muting themselves.

Staff Capabilities

  • Includes all learner-level mute features.
  • Added ability to mute users course-wide.
  • Enabled viewing of both personal and course-wide mute lists.
  • Allowed staff to unmute users across all mute scopes.

Key Behaviors

  • Muted users are not notified when they are muted.
  • Staff users cannot be muted by learners.
  • Course-wide mutes override personal mutes.

Linked PRs

@naincy128 naincy128 marked this pull request as ready for review January 16, 2026 04:58
Copilot AI review requested due to automatic review settings January 16, 2026 04:58
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements a comprehensive mute/unmute feature for discussion forums, enabling learners and staff to manage unwanted interactions. The implementation supports both personal and course-wide mute scopes with role-based restrictions, and includes support for both MySQL and MongoDB backends.

Changes:

  • Added database models for DiscussionMute and DiscussionMuteException with appropriate constraints and indexes
  • Implemented API views and endpoints for mute, unmute, mute-and-report, and status checking operations
  • Created backend implementations for both MySQL and MongoDB with consistent interfaces
  • Added management commands for creating MongoDB indexes for mute functionality

Reviewed changes

Copilot reviewed 44 out of 44 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
forum/views/mutes.py New API views for mute/unmute operations with error handling
forum/serializers/mute.py Serializers for mute input/output with proper validation
forum/migrations/0006_add_discussion_mute_models.py Database migration creating mute-related tables with constraints
forum/backends/mysql/models.py Django models for DiscussionMute and DiscussionMuteException with validation
forum/backends/mysql/api.py MySQL backend implementation of mute operations
forum/backends/mongodb/mutes.py MongoDB models and operations for mute functionality
forum/backends/mongodb/api.py MongoDB backend API implementation
forum/backends/backend.py Abstract base class method signatures for mute operations
forum/api/mutes.py High-level API layer delegating to appropriate backend
forum/management/commands/forum_create_mute_mongodb_indexes.py MongoDB index creation for optimal mute query performance
Various test files Import ordering improvements (PEP 8 compliance)
Various view/command files Unused parameter prefixing with underscore
forum/admin.py Admin interface registration for mute models

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


if not muter_id:
return Response(
{"error": "moderator_id is required"},
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message refers to 'moderator_id' but the actual parameter name extracted from request.data is 'muter_id' (line 109). The error message should say 'muter_id is required' to match the actual field name being validated.

Suggested change
{"error": "moderator_id is required"},
{"error": "muter_id is required"},

Copilot uses AI. Check for mistakes.

def get_all_muted_users_for_course(
course_id: str,
_requester_id: Optional[str] = None,
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parameter is prefixed with underscore indicating it's unused, but it should be passed to the backend call. The backend implementation expects 'requester_id' for filtering personal mutes. Either remove the underscore and pass it through, or document why it's intentionally unused.

Copilot uses AI. Check for mistakes.
unmuted_by_id: str,
course_id: str,
scope: str = "personal",
muted_by_id: Optional[str] = None,
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Parameter name 'muted_by_id' shadows the function parameter 'unmuted_by_id' conceptually and creates confusion. In unmute operations, this represents the original muter for personal scope unmutes. Consider renaming to 'original_muter_id' or 'filter_muted_by_id' for clarity.

Copilot uses AI. Check for mistakes.
Comment on lines +2528 to +2531
"""Create a mute exception for course-wide mutes."""
# TODO: Implement mute exception logic when needed
raise NotImplementedError("Mute exceptions not yet implemented")

Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The create_mute_exception method raises NotImplementedError, but the feature description mentions course-wide mutes override personal mutes and staff can unmute across all scopes. This suggests exceptions might be needed for the feature to work as described. Clarify if this is intentional technical debt or if the implementation is incomplete.

Suggested change
"""Create a mute exception for course-wide mutes."""
# TODO: Implement mute exception logic when needed
raise NotImplementedError("Mute exceptions not yet implemented")
"""Create or reactivate a mute exception for course-wide mutes.
This allows a specific user to be exempt from a course-wide mute
on a given muted user.
"""
try:
muted_user = User.objects.get(pk=muted_user_id)
exception_user = User.objects.get(pk=exception_user_id)
# Ensure idempotent behavior: reuse existing exception if present.
exception, created = DiscussionMuteException.objects.get_or_create(
muted_user=muted_user,
exception_user=exception_user,
course_id=course_id,
)
# If the exception model tracks active state, reactivate as needed.
if not created and hasattr(exception, "is_active"):
if not getattr(exception, "is_active"):
setattr(exception, "is_active", True)
exception.save()
# Prefer model's serialization helper if available.
if hasattr(exception, "to_dict") and callable(exception.to_dict):
return exception.to_dict()
# Fallback minimal representation.
data: dict[str, Any] = {
"id": getattr(exception, "id", None),
"muted_user_id": muted_user_id,
"exception_user_id": exception_user_id,
"course_id": course_id,
}
# Include active flag if present on the model.
if hasattr(exception, "is_active"):
data["is_active"] = getattr(exception, "is_active")
return data
except Exception as e:
raise ValueError(f"Failed to create mute exception: {e}") from e

Copilot uses AI. Check for mistakes.
Comment on lines +173 to +182
# Create a basic report record (placeholder implementation)
# In a full implementation, this would integrate with a proper reporting system
report_result = {
"status": "success",
"report_id": f"report_{muted_user_id}_{muted_by_id}_{course_id}",
"reported_user_id": muted_user_id,
"reported_by_id": muted_by_id,
"course_id": course_id,
"reason": reason,
"created": datetime.utcnow().isoformat(),
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mute_and_report_user function creates a placeholder report that is never persisted. This means the 'report' part of 'mute and report' doesn't actually happen. The backend implementations call mute_user and add a 'reported' flag, but no actual report is created in any system. This could mislead users who expect reporting functionality to work.

Copilot uses AI. Check for mistakes.
Comment on lines +2133 to +2142
# MongoDB implementation placeholder
return {
"action_type": action_type,
"target_user_id": target_user_id,
"moderator_id": moderator_id,
"course_id": course_id,
"scope": scope,
"reason": reason,
"metadata": metadata or {},
"backend": "mongodb",
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The log_moderation_action method returns placeholder data without persisting logs. While the main mute/unmute operations do log actions via DiscussionModerationLogs, this standalone method doesn't persist anything, which could confuse callers expecting actual logging.

Copilot uses AI. Check for mistakes.
Comment on lines +2565 to +2578
"""Log a moderation action."""
# TODO: Implement moderation logging when needed
log_entry = {
"action_type": action_type,
"target_user_id": target_user_id,
"moderator_id": moderator_id,
"course_id": course_id,
"scope": scope,
"reason": reason,
"metadata": metadata or {},
"timestamp": timezone.now().isoformat(),
}
# For now, just return the log entry data
return log_entry
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The log_moderation_action method in MySQL backend also returns placeholder data. While mute/unmute operations do log to ModerationAuditLog, this standalone logging method doesn't persist anything. Consider implementing actual persistence or removing this method if it's not needed.

Suggested change
"""Log a moderation action."""
# TODO: Implement moderation logging when needed
log_entry = {
"action_type": action_type,
"target_user_id": target_user_id,
"moderator_id": moderator_id,
"course_id": course_id,
"scope": scope,
"reason": reason,
"metadata": metadata or {},
"timestamp": timezone.now().isoformat(),
}
# For now, just return the log entry data
return log_entry
"""Log a moderation action.
Note:
This method is currently not implemented and will raise an error
if called. Use the existing moderation audit logging mechanisms
(e.g., those used by mute/unmute operations) instead.
"""
# TODO: Implement moderation logging persistence when requirements are defined
raise NotImplementedError("Moderation action logging not yet implemented")

Copilot uses AI. Check for mistakes.
Comment on lines +2071 to +2076
try:
# MongoDB implementation placeholder
return []
except Exception as e:
raise ForumV2RequestError(f"Failed to get muted users: {str(e)}") from e

Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This statement is unreachable.

Suggested change
try:
# MongoDB implementation placeholder
return []
except Exception as e:
raise ForumV2RequestError(f"Failed to get muted users: {str(e)}") from e
# MongoDB implementation placeholder
# Returns an empty list until MongoDB support is implemented.
return []

Copilot uses AI. Check for mistakes.
Comment on lines +2092 to +2103
try:
# MongoDB implementation placeholder
return {
"muted_user_id": muted_user_id,
"exception_user_id": exception_user_id,
"course_id": course_id,
"backend": "mongodb",
}
except Exception as e:
raise ForumV2RequestError(
f"Failed to create mute exception: {str(e)}"
) from e
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This statement is unreachable.

Suggested change
try:
# MongoDB implementation placeholder
return {
"muted_user_id": muted_user_id,
"exception_user_id": exception_user_id,
"course_id": course_id,
"backend": "mongodb",
}
except Exception as e:
raise ForumV2RequestError(
f"Failed to create mute exception: {str(e)}"
) from e
# MongoDB implementation placeholder
return {
"muted_user_id": muted_user_id,
"exception_user_id": exception_user_id,
"course_id": course_id,
"backend": "mongodb",
}

Copilot uses AI. Check for mistakes.
"metadata": metadata or {},
"backend": "mongodb",
}
except Exception as e:
Copy link

Copilot AI Jan 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This statement is unreachable.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants