Skip to content
Merged
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
2 changes: 1 addition & 1 deletion forum/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
Openedx forum app.
"""

__version__ = "0.3.8"
__version__ = "0.3.9"
104 changes: 102 additions & 2 deletions forum/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
UserVote,
Subscription,
MongoContent,
ModerationAuditLog,
)


Expand Down Expand Up @@ -55,11 +56,12 @@ class CommentThreadAdmin(admin.ModelAdmin): # type: ignore
"context",
"closed",
"pinned",
"is_spam",
"created_at",
"updated_at",
)
search_fields = ("title", "body", "author__username", "course_id")
list_filter = ("thread_type", "context", "closed", "pinned")
list_filter = ("thread_type", "context", "closed", "pinned", "is_spam")


@admin.register(Comment)
Expand All @@ -74,9 +76,10 @@ class CommentAdmin(admin.ModelAdmin): # type: ignore
"updated_at",
"endorsed",
"anonymous",
"is_spam",
)
search_fields = ("body", "author__username", "comment_thread__title")
list_filter = ("endorsed", "anonymous")
list_filter = ("endorsed", "anonymous", "is_spam")


@admin.register(EditHistory)
Expand Down Expand Up @@ -152,3 +155,100 @@ class MongoContentAdmin(admin.ModelAdmin): # type: ignore

list_display = ("mongo_id", "content_object_id", "content_type")
search_fields = ("mongo_id",)


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

list_display = (
"timestamp",
"classification",
"actions_taken",
"body_preview",
"original_author",
"moderator_override",
"confidence_score",
)
list_filter = (
"classification",
"moderator_override",
"timestamp",
)
search_fields = (
"original_author__username",
"moderator__username",
"reasoning",
"override_reason",
"body",
)
readonly_fields = (
"timestamp",
"body",
"classifier_output",
"reasoning",
"classification",
"actions_taken",
"confidence_score",
"original_author",
)
fieldsets = (
(
"Moderation Decision",
{
"fields": (
"timestamp",
"classification",
"actions_taken",
"confidence_score",
"reasoning",
)
},
),
("Content Information", {"fields": ("body",)}),
("Author Information", {"fields": ("original_author",)}),
(
"Human Override",
{
"fields": (
"moderator_override",
"moderator",
"override_reason",
)
},
),
(
"Technical Details",
{
"fields": ("classifier_output",),
"classes": ("collapse",),
},
),
)

def body_preview(self, obj): # type: ignore
"""Return a truncated preview of the body for list display."""
if obj.body:
return obj.body[:100] + "..." if len(obj.body) > 100 else obj.body
return "-"

body_preview.short_description = "Body Preview" # type: ignore

# pylint: disable=unused-argument
def has_add_permission(self, request): # type: ignore[no-untyped-def]
"""Disable adding audit logs manually."""
return False

# pylint: disable=unused-argument
def has_delete_permission(self, request, obj=None): # type: ignore[no-untyped-def]
"""Disable deleting audit logs to maintain integrity."""
return False

def get_queryset(self, request): # type: ignore
"""Optimize queryset with related objects."""
return (
super()
.get_queryset(request)
.select_related("original_author", "moderator")
.order_by("-timestamp")
)
Loading