Skip to content

feat: implement discussion mute/unmute feature with user and staff-level controls#54

Open
naincy128 wants to merge 18 commits intorelease-ulmofrom
COSMO2-743
Open

feat: implement discussion mute/unmute feature with user and staff-level controls#54
naincy128 wants to merge 18 commits intorelease-ulmofrom
COSMO2-743

Conversation

@naincy128
Copy link

@naincy128 naincy128 commented Dec 1, 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.

Video Demonstration

compressed-video.mp4

Linked PRs

@naincy128 naincy128 changed the title feat: implement discussion mute/unmute feature with user and staff-le… feat: implement discussion mute/unmute feature with user and staff-level controls Dec 1, 2025
@naincy128 naincy128 marked this pull request as ready for review January 16, 2026 04:57
Copilot AI review requested due to automatic review settings January 16, 2026 04:57

This comment was marked as resolved.

@naincy128 naincy128 marked this pull request as draft January 19, 2026 09:03
@naincy128 naincy128 marked this pull request as ready for review January 20, 2026 05:28
Copilot AI review requested due to automatic review settings January 20, 2026 05:28

This comment was marked as duplicate.

Copilot AI review requested due to automatic review settings January 21, 2026 07:13

This comment was marked as resolved.

Copilot AI review requested due to automatic review settings January 21, 2026 10:02

This comment was marked as resolved.

Copilot AI review requested due to automatic review settings January 22, 2026 06:21

This comment was marked as resolved.

Copilot AI review requested due to automatic review settings January 22, 2026 12:24

This comment was marked as resolved.

Copilot AI review requested due to automatic review settings January 25, 2026 15:50
Copilot AI review requested due to automatic review settings February 6, 2026 07:39

This comment was marked as resolved.

Copilot AI review requested due to automatic review settings February 9, 2026 13:19

This comment was marked as resolved.

Copilot AI review requested due to automatic review settings February 10, 2026 12:04

This comment was marked as resolved.

Copilot AI review requested due to automatic review settings February 10, 2026 12:38

This comment was marked as resolved.

Copilot AI review requested due to automatic review settings February 11, 2026 06:20

This comment was marked as resolved.

Copilot AI review requested due to automatic review settings February 11, 2026 09:43
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

Copilot reviewed 13 out of 13 changed files in this pull request and generated 14 comments.


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

"""
API endpoint to mute a user in discussions using forum service.

**POST /api/discussion/v1/moderation/forum-mute/**
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

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

The docstring shows the endpoint as /api/discussion/v1/moderation/forum-mute/ but it should include the course_id parameter to match the actual URL pattern: /api/discussion/v1/moderation/forum-mute/{course_id}/. This inconsistency could confuse developers trying to use this API.

Suggested change
**POST /api/discussion/v1/moderation/forum-mute/**
**POST /api/discussion/v1/moderation/forum-mute/{course_id}/**

Copilot uses AI. Check for mistakes.
if not course_id:
return False

# # Convert course_id to CourseKey if it's a string
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

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

There's a commented-out line with double comment markers that should be removed. This appears to be leftover debugging or refactoring code.

Suggested change
# # Convert course_id to CourseKey if it's a string
# Convert course_id to CourseKey if it's a string

Copilot uses AI. Check for mistakes.
Comment on lines +142 to +145
fr"^v1/moderation/forum-mute-status/{settings.COURSE_ID_PATTERN}/(?P<user_id>[0-9]+)/$",
ForumMuteStatusView.as_view(),
name="forum_mute_status"
),
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

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

The URL pattern allows an empty string for user_id because the regex pattern [0-9]+ requires at least one digit, but the pattern itself doesn't enforce that the entire path segment is consumed. However, Django's URL routing should handle this correctly. Consider using a more explicit pattern or adding validation in the view to ensure user_id is properly formatted.

Copilot uses AI. Check for mistakes.
Comment on lines +380 to +391
personal_muted = [
u for u in muted_users
if u.get('scope') == 'personal' and str(u.get('muter_id')) == str(request.user.id)
]
course_wide_muted = [u for u in muted_users if u.get('scope') == 'course']

# Filter main muted_users list to exclude other users' personal mutes
filtered_muted_users = [
u for u in muted_users
if u.get('scope') != 'personal' or str(u.get('muter_id')) == str(request.user.id)
]

Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

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

The code iterates through muted_users multiple times (lines 369-376, 380-384, 387-390) which is inefficient. Consider consolidating these operations into a single pass through the list to improve performance, especially when there are many muted users.

Suggested change
personal_muted = [
u for u in muted_users
if u.get('scope') == 'personal' and str(u.get('muter_id')) == str(request.user.id)
]
course_wide_muted = [u for u in muted_users if u.get('scope') == 'course']
# Filter main muted_users list to exclude other users' personal mutes
filtered_muted_users = [
u for u in muted_users
if u.get('scope') != 'personal' or str(u.get('muter_id')) == str(request.user.id)
]
personal_muted = []
course_wide_muted = []
filtered_muted_users = []
for user_data in muted_users:
scope_value = user_data.get('scope')
is_personal = scope_value == 'personal'
is_course = scope_value == 'course'
is_current_user_muter = str(user_data.get('muter_id')) == str(request.user.id)
if is_personal and is_current_user_muter:
personal_muted.append(user_data)
if is_course:
course_wide_muted.append(user_data)
# Filter main muted_users list to exclude other users' personal mutes
if not is_personal or is_current_user_muter:
filtered_muted_users.append(user_data)

Copilot uses AI. Check for mistakes.
["anonymous", "raw_body", "title", "topic_id", "type"]
)
elif role == FORUM_ROLE_MODERATOR:

Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

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

Empty line added between method definitions that doesn't follow the convention in the rest of the file. Python PEP 8 recommends two blank lines between top-level function and class definitions, but only one blank line between method definitions within a class. This inconsistency should be removed.

Suggested change

Copilot uses AI. Check for mistakes.
"""
API endpoint to mute a user and report their content using forum service.

**POST /api/discussion/v1/moderation/forum-mute-and-report/**
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

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

The docstring shows the endpoint as /api/discussion/v1/moderation/forum-mute-and-report/ but it should include the course_id parameter to match the actual URL pattern: /api/discussion/v1/moderation/forum-mute-and-report/{course_id}/. This inconsistency could confuse developers trying to use this API.

Suggested change
**POST /api/discussion/v1/moderation/forum-mute-and-report/**
**POST /api/discussion/v1/moderation/forum-mute-and-report/{course_id}/**

Copilot uses AI. Check for mistakes.
"""
API endpoint to get mute status for a user using forum service.

**GET /api/discussion/v1/moderation/forum-mute-status/{user_id}/**
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

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

The docstring shows the endpoint as /api/discussion/v1/moderation/forum-mute-status/{user_id}/ but it should include the course_id parameter before user_id to match the actual URL pattern: /api/discussion/v1/moderation/forum-mute-status/{course_id}/{user_id}/. This inconsistency could confuse developers trying to use this API.

Suggested change
**GET /api/discussion/v1/moderation/forum-mute-status/{user_id}/**
**GET /api/discussion/v1/moderation/forum-mute-status/{course_id}/{user_id}/**

Copilot uses AI. Check for mistakes.
Comment on lines 1223 to 1244
if paginated_results.page != page:
raise PageNotFoundError("Page not found (No results on this page).")

# Always filter muted content for All Posts tab (unless include_muted is explicitly True)
if include_muted:
# Only the muted section should set include_muted True
filtered_threads = paginated_results.collection
else:
# Always filter out muted content for All Posts, even after restoration
filtered_threads = filter_muted_content(
request.user,
course_key,
paginated_results.collection
)

results = _serialize_discussion_entities(
request,
context,
paginated_results.collection,
filtered_threads,
requested_fields,
DiscussionEntity.thread,
)
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

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

The pagination count uses paginated_results.thread_count which includes muted threads, but the actual results have been filtered to exclude muted content. This creates a mismatch where the pagination metadata (total count, page count) doesn't match the actual number of items returned, leading to incorrect pagination behavior and potentially confusing users. The paginator should use len(filtered_threads) instead to reflect the actual filtered count.

Copilot uses AI. Check for mistakes.
)
paginator = DiscussionAPIPagination(
request, page, num_pages, len(filtered_threads)
request, page, num_pages, len(filtered_threads_with_deletion_status)
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

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

The pagination count uses len(filtered_threads_with_deletion_status) which is correct, but this pagination is created after applying mute filtering. However, the original num_pages from the comment service doesn't account for the filtered count, which may cause pagination issues when many users are muted.

Suggested change
request, page, num_pages, len(filtered_threads_with_deletion_status)
request,
page_num=page,
num_pages=None,
count=len(filtered_threads_with_deletion_status),

Copilot uses AI. Check for mistakes.
Copy link

@jcapphelix jcapphelix left a comment

Choose a reason for hiding this comment

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

Approved.

Do inform before merging.

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