Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions forum/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
UserVote,
Subscription,
MongoContent,
DiscussionMute,
DiscussionMuteException,
ModerationAuditLog,
)

Expand Down Expand Up @@ -148,6 +150,23 @@ class SubscriptionAdmin(admin.ModelAdmin): # type: ignore
search_fields = ("subscriber__username",)
list_filter = ("source_content_type",)

@admin.register(DiscussionMute)
class DiscussionMuteAdmin(admin.ModelAdmin): # type: ignore
"""Admin interface for DiscussionMute model."""

list_display = ("muted_user", "muted_by", "course_id", "scope", "reason", "is_active", "created", "modified")
search_fields = ("muted_user__username", "muted_by__username", "reason", "course_id")
list_filter = ("scope", "is_active", "created", "modified")


@admin.register(DiscussionMuteException)
class DiscussionMuteExceptionAdmin(admin.ModelAdmin): # type: ignore
"""Admin interface for DiscussionMuteException model."""

list_display = ("muted_user", "exception_user", "course_id", "created")
search_fields = ("muted_user__username", "exception_user__username", "course_id")
list_filter = ("created",)


@admin.register(MongoContent)
class MongoContentAdmin(admin.ModelAdmin): # type: ignore
Expand Down
14 changes: 14 additions & 0 deletions forum/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@
update_comment_flag,
update_thread_flag,
)
from .mutes import (
get_muted_users,
get_user_mute_status,
mute_user,
unmute_user,
mute_and_report_user,
get_all_muted_users_for_course,
)
from .pins import pin_thread, unpin_thread
from .search import search_threads
from .subscriptions import (
Expand Down Expand Up @@ -87,4 +95,10 @@
"update_user",
"update_username",
"update_users_in_course",
"mute_user",
"unmute_user",
"get_user_mute_status",
"get_muted_users",
"get_all_muted_users_for_course",
"mute_and_report_user",
]
233 changes: 233 additions & 0 deletions forum/api/mutes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
"""
Native Python APIs for discussion moderation (mute/unmute).
"""

from datetime import datetime
from typing import Any, Dict, List, Optional

from forum.backend import get_backend
from forum.utils import ForumV2RequestError


def mute_user(
muted_user_id: str,
muted_by_id: str,
course_id: str,
scope: str = "personal",
reason: str = "",
**kwargs: Any
) -> Dict[str, Any]:
"""
Mute a user in discussions.

Args:
muted_user_id: ID of user to mute
muted_by_id: ID of user performing the mute
course_id: Course identifier
scope: Mute scope ('personal' or 'course')
reason: Optional reason for mute

Returns:
Dictionary containing mute record data
"""
try:
backend = get_backend(course_id)()
return backend.mute_user(
muted_user_id=muted_user_id,
muted_by_id=muted_by_id,
course_id=course_id,
scope=scope,
reason=reason,
**kwargs
)
except ValueError as e:
raise ForumV2RequestError(str(e)) from e
except Exception as e:
raise ForumV2RequestError(f"Failed to mute user: {str(e)}") from e


def unmute_user(
muted_user_id: str,
unmuted_by_id: str,
course_id: str,
scope: str = "personal",
muted_by_id: Optional[str] = None,
**kwargs: Any
) -> Dict[str, Any]:
"""
Unmute a user in discussions.

Args:
muted_user_id: ID of user to unmute
unmuted_by_id: ID of user performing the unmute
course_id: Course identifier
scope: Mute scope ('personal' or 'course')
muted_by_id: Optional filter by who performed the original mute

Returns:
Dictionary containing unmute operation result
"""
try:
backend = get_backend(course_id)()
return backend.unmute_user(
muted_user_id=muted_user_id,
unmuted_by_id=unmuted_by_id,
course_id=course_id,
scope=scope,
muted_by_id=muted_by_id,
**kwargs
)
except ValueError as e:
raise ForumV2RequestError(str(e)) from e
except Exception as e:
raise ForumV2RequestError(f"Failed to unmute user: {str(e)}") from e


def get_user_mute_status(
user_id: str,
course_id: str,
viewer_id: str,
**kwargs: Any
) -> Dict[str, Any]:
"""
Get mute status for a user in a course.

Args:
user_id: ID of user to check
course_id: Course identifier
viewer_id: ID of user requesting the status

Returns:
Dictionary containing mute status information
"""
try:
backend = get_backend(course_id)()
return backend.get_user_mute_status(
muted_user_id=user_id,
course_id=course_id,
requesting_user_id=viewer_id,
**kwargs
)
except ValueError as e:
raise ForumV2RequestError(str(e)) from e
except Exception as e:
raise ForumV2RequestError(f"Failed to get mute status: {str(e)}") from e


def get_muted_users(
muted_by_id: str,
course_id: str,
scope: str = "all",
**kwargs: Any
) -> Dict[str, Any]:
"""
Get list of users muted by a specific user.

Args:
muted_by_id: ID of the user who muted others
course_id: Course identifier
scope: Scope filter ('personal', 'course', or 'all')

Returns:
Dictionary containing list of muted user records
"""
try:
backend = get_backend(course_id)()
return backend.get_muted_users(
moderator_id=muted_by_id,
course_id=course_id,
scope=scope,
**kwargs
)
except ValueError as e:
raise ForumV2RequestError(str(e)) from e
except Exception as e:
raise ForumV2RequestError(f"Failed to get muted users: {str(e)}") from e


def mute_and_report_user(
muted_user_id: str,
muted_by_id: str,
course_id: str,
scope: str = "personal",
reason: str = "",
**kwargs: Any
) -> Dict[str, Any]:
"""
Mute a user and create a report against them in discussions.

Args:
muted_user_id: ID of user to mute
muted_by_id: ID of user performing the mute
course_id: Course identifier
scope: Mute scope ('personal' or 'course')
reason: Reason for muting and reporting

Returns:
Dictionary containing mute and report operation result
"""
try:
backend = get_backend(course_id)()

# Mute the user
mute_result = backend.mute_user(
muted_user_id=muted_user_id,
muted_by_id=muted_by_id,
course_id=course_id,
scope=scope,
reason=reason,
**kwargs
)

# 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()
}

return {
'status': 'success',
'message': 'User muted and reported',
'mute_record': mute_result,
'report_record': report_result
}
except ValueError as e:
raise ForumV2RequestError(str(e)) from e
except Exception as e:
raise ForumV2RequestError(f"Failed to mute and report user: {str(e)}") from e


def get_all_muted_users_for_course(
course_id: str,
requester_id: Optional[str] = None,
scope: str = "all",
**kwargs: Any
) -> Dict[str, Any]:
"""
Get all muted users in a course (requires appropriate permissions).

Args:
course_id: Course identifier
requester_id: ID of the user requesting the list (optional)
scope: Scope filter ('personal', 'course', or 'all')

Returns:
Dictionary containing list of all muted users in the course
"""
try:
backend = get_backend(course_id)()
return backend.get_all_muted_users_for_course(
course_id=course_id,
scope=scope,
**kwargs
)
except ValueError as e:
raise ForumV2RequestError(str(e)) from e
except Exception as e:
raise ForumV2RequestError(f"Failed to get course muted users: {str(e)}") from e
Loading
Loading