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
12 changes: 11 additions & 1 deletion forum/backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

from typing import Callable, Optional

from edx_django_utils.monitoring import set_custom_attribute # type: ignore[import-untyped]

from forum.backends.mongodb.api import MongoBackend
from forum.backends.mysql.api import MySQLBackend

Expand Down Expand Up @@ -37,7 +39,15 @@ def get_backend(
"""Return a factory function that lazily loads the backend API based on course_id."""

def _get_backend() -> MongoBackend | MySQLBackend:
if is_mysql_backend_enabled(course_id):
backend_enabled = is_mysql_backend_enabled(course_id)

# Track which backend is being used
backend_type = "mysql" if backend_enabled else "mongodb"
set_custom_attribute("forum.backend", backend_type)
if course_id:
set_custom_attribute("forum.backend.course_id", str(course_id))

if backend_enabled:
return MySQLBackend()
return MongoBackend()

Expand Down
20 changes: 20 additions & 0 deletions forum/backends/mongodb/comments.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from typing import Any, Optional

from bson import ObjectId
from edx_django_utils.monitoring import set_custom_attribute # type: ignore[import-untyped]

from forum.backends.mongodb.contents import BaseContents
from forum.backends.mongodb.threads import CommentThread
Expand Down Expand Up @@ -102,6 +103,18 @@ def insert(
Returns:
str: The ID of the inserted document.
"""
# Track comment insertion
set_custom_attribute("forum.backend.operation", "insert_comment")
set_custom_attribute("forum.thread_id", comment_thread_id)
set_custom_attribute("forum.course_id", course_id)
set_custom_attribute("forum.author_id", author_id)
set_custom_attribute("forum.comment_depth", str(depth))
if parent_id:
set_custom_attribute("forum.parent_comment_id", parent_id)
set_custom_attribute("forum.is_child_comment", True)
else:
set_custom_attribute("forum.is_child_comment", False)

date = datetime.now()
comment_data = {
"votes": self.get_votes_dict(up=[], down=[]),
Expand Down Expand Up @@ -275,6 +288,13 @@ def delete( # type: ignore[override]
Returns:
The number of comments deleted.
"""
# Track comment deletion
set_custom_attribute("forum.backend.operation", "delete_comment")
set_custom_attribute("forum.comment_id", _id)
set_custom_attribute("forum.delete_mode", mode)
if deleted_by:
set_custom_attribute("forum.deleted_by", deleted_by)

comment = self.get(_id)
if not comment:
return 0, 0
Expand Down
13 changes: 13 additions & 0 deletions forum/backends/mongodb/threads.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from typing import Any, Optional

from bson import ObjectId
from edx_django_utils.monitoring import set_custom_attribute # type: ignore[import-untyped]

from forum.backends.mongodb.contents import BaseContents
from forum.backends.mongodb.users import Users
Expand All @@ -20,6 +21,9 @@ class CommentThread(BaseContents):

def delete(self, _id: str) -> int:
"""Delete CommentThread"""
set_custom_attribute("forum.backend.operation", "delete_thread")
set_custom_attribute("forum.thread_id", _id)

result = super().delete(_id)
get_handler_by_name("comment_thread_deleted").send(
sender=self.__class__, comment_thread_id=_id
Expand Down Expand Up @@ -142,6 +146,15 @@ def insert(
if historical_abuse_flaggers is None:
historical_abuse_flaggers = []

# Track thread insertion
set_custom_attribute("forum.backend.operation", "insert_thread")
set_custom_attribute("forum.thread_type", thread_type)
set_custom_attribute("forum.course_id", course_id)
set_custom_attribute("forum.commentable_id", commentable_id)
set_custom_attribute("forum.author_id", author_id)
if group_id:
set_custom_attribute("forum.group_id", str(group_id))

date = datetime.now()
thread_data = {
"votes": self.get_votes_dict(up=[], down=[]),
Expand Down
53 changes: 53 additions & 0 deletions forum/backends/mysql/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
When,
)
from django.utils import timezone
from edx_django_utils.monitoring import set_custom_attribute # type: ignore[import-untyped]
from rest_framework import status
from rest_framework.response import Response

Expand Down Expand Up @@ -1686,6 +1687,10 @@ def create_comment(cls, data: dict[str, Any]) -> str:
@classmethod
def delete_comment(cls, comment_id: str) -> None:
"""Delete comment from comment_id."""
set_custom_attribute("forum.backend.operation", "delete_comment")
set_custom_attribute("forum.comment_id", comment_id)
set_custom_attribute("forum.delete_mode", "hard")

comment = Comment.objects.get(pk=comment_id)
if comment.parent:
cls.update_child_count_in_parent_comment(str(comment.parent.pk), -1)
Expand All @@ -1701,6 +1706,12 @@ def soft_delete_comment(
Returns:
tuple: (responses_deleted, replies_deleted)
"""
set_custom_attribute("forum.backend.operation", "delete_comment")
set_custom_attribute("forum.comment_id", comment_id)
set_custom_attribute("forum.delete_mode", "soft")
if deleted_by:
set_custom_attribute("forum.deleted_by", deleted_by)

comment = Comment.objects.get(pk=comment_id)
deleted_user: Optional[User] = None
if deleted_by:
Expand Down Expand Up @@ -2044,6 +2055,17 @@ def get_commentables_counts_based_on_type(course_id: str) -> dict[str, Any]:
@staticmethod
def update_comment(comment_id: str, **kwargs: Any) -> int:
"""Updates a comment in the database."""
# Track comment update
set_custom_attribute("forum.backend.operation", "update_comment")
set_custom_attribute("forum.comment_id", comment_id)

# Track what's being updated
update_fields = [k for k in kwargs if kwargs.get(k) is not None]
if update_fields:
set_custom_attribute("forum.update_fields", ",".join(update_fields))
if "course_id" in kwargs:
set_custom_attribute("forum.course_id", kwargs["course_id"])

try:
comment = Comment.objects.get(id=comment_id)
except Comment.DoesNotExist:
Expand Down Expand Up @@ -2244,6 +2266,9 @@ def get_subscriptions(cls, query: dict[str, Any]) -> list[dict[str, Any]]:
@staticmethod
def delete_thread(thread_id: str) -> int:
"""Delete thread from thread_id."""
set_custom_attribute("forum.backend.operation", "delete_thread")
set_custom_attribute("forum.thread_id", thread_id)

try:
thread = CommentThread.objects.get(pk=thread_id)
except ObjectDoesNotExist:
Expand All @@ -2254,6 +2279,12 @@ def delete_thread(thread_id: str) -> int:
@staticmethod
def soft_delete_thread(thread_id: str, deleted_by: Optional[str] = None) -> int:
"""Soft delete thread by marking it as deleted."""
set_custom_attribute("forum.backend.operation", "delete_thread")
set_custom_attribute("forum.thread_id", thread_id)
set_custom_attribute("forum.delete_mode", "soft")
if deleted_by:
set_custom_attribute("forum.deleted_by", deleted_by)

try:
thread = CommentThread.objects.get(pk=thread_id)
except ObjectDoesNotExist:
Expand All @@ -2268,6 +2299,17 @@ def soft_delete_thread(thread_id: str, deleted_by: Optional[str] = None) -> int:
@staticmethod
def create_thread(data: dict[str, Any]) -> str:
"""Create thread."""
# Track thread creation
set_custom_attribute("forum.backend.operation", "create_thread")
set_custom_attribute("forum.course_id", data["course_id"])
set_custom_attribute("forum.thread_type", data.get("thread_type", "discussion"))
set_custom_attribute(
"forum.commentable_id", data.get("commentable_id", "course")
)
set_custom_attribute("forum.author_id", data["author_id"])
if "group_id" in data:
set_custom_attribute("forum.group_id", str(data["group_id"]))

optional_args = {}
if group_id := data.get("group_id"):
optional_args["group_id"] = group_id
Expand All @@ -2292,6 +2334,17 @@ def update_thread(
**kwargs: Any,
) -> int:
"""Updates a thread document in the database."""
# Track thread update
set_custom_attribute("forum.backend.operation", "update_thread")
set_custom_attribute("forum.thread_id", thread_id)

# Track what's being updated
update_fields = [k for k in kwargs if kwargs.get(k) is not None]
if update_fields:
set_custom_attribute("forum.update_fields", ",".join(update_fields))
if "course_id" in kwargs:
set_custom_attribute("forum.course_id", kwargs["course_id"])

thread = CommentThread.objects.get(id=thread_id)

if "thread_type" in kwargs:
Expand Down
2 changes: 1 addition & 1 deletion forum/backends/mysql/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from django.db.models import QuerySet
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from model_utils.models import TimeStampedModel
from model_utils.models import TimeStampedModel # pylint: disable=import-error
from opaque_keys.edx.django.models import CourseKeyField

from forum.utils import validate_upvote_or_downvote
Expand Down
1 change: 0 additions & 1 deletion forum/migration_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
)
from forum.utils import make_aware, get_trunc_title


logger = logging.getLogger(__name__)


Expand Down
3 changes: 3 additions & 0 deletions forum/settings/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ def plugin_settings(settings: Any) -> None:
"""
Common settings for forum app
"""
# Configure Datadog monitoring
settings.OPENEDX_TELEMETRY = ["edx_django_utils.monitoring.DatadogBackend"]

# Search backend
if getattr(settings, "MEILISEARCH_ENABLED", False):
settings.FORUM_SEARCH_BACKEND = getattr(
Expand Down
41 changes: 38 additions & 3 deletions forum/views/comments.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
"""Forum Comments API Views."""

from edx_django_utils.monitoring import set_custom_attribute # type: ignore[import-untyped]
from rest_framework import status
from rest_framework.permissions import AllowAny
from rest_framework.request import Request
Expand Down Expand Up @@ -38,6 +39,9 @@ def get(self, request: Request, comment_id: str) -> Response:
Response:
The details of the comment for the given comment_id.
"""
set_custom_attribute("forum.operation", "get_comment")
set_custom_attribute("forum.comment_id", comment_id)

try:
data = get_parent_comment(comment_id)
except ForumV2RequestError:
Expand Down Expand Up @@ -65,8 +69,16 @@ def post(self, request: Request, comment_id: str) -> Response:
Response:
The details of the comment that is created.
"""
set_custom_attribute("forum.operation", "create_child_comment")
set_custom_attribute("forum.parent_comment_id", comment_id)

request_data = request.data
if "course_id" in request_data:
set_custom_attribute("forum.course_id", request_data["course_id"])
if "user_id" in request_data:
set_custom_attribute("forum.author_id", request_data["user_id"])

try:
request_data = request.data
comment = create_child_comment(
comment_id,
request_data["body"],
Expand Down Expand Up @@ -99,8 +111,20 @@ def put(self, request: Request, comment_id: str) -> Response:
Response:
The details of the comment that is updated.
"""
set_custom_attribute("forum.operation", "update_comment")
set_custom_attribute("forum.comment_id", comment_id)

# Track what fields are being updated
request_data = request.data
if request_data:
update_fields = [
k for k in request_data.keys() if request_data.get(k) is not None
]
set_custom_attribute("forum.update_fields", ",".join(update_fields))
if "course_id" in request_data:
set_custom_attribute("forum.course_id", request_data["course_id"])

try:
request_data = request.data
if anonymous := request_data.get("anonymous"):
anonymous = str_to_bool(anonymous)
if anonymous_to_peers := request_data.get("anonymous_to_peers"):
Expand Down Expand Up @@ -146,6 +170,9 @@ def delete(self, request: Request, comment_id: str) -> Response:
Response:
The details of the comment that is deleted.
"""
set_custom_attribute("forum.operation", "delete_comment")
set_custom_attribute("forum.comment_id", comment_id)

try:
deleted_comment = delete_comment(comment_id)
except ForumV2RequestError:
Expand Down Expand Up @@ -179,8 +206,16 @@ def post(self, request: Request, thread_id: str) -> Response:
Response:
The details of the comment that is created.
"""
set_custom_attribute("forum.operation", "create_parent_comment")
set_custom_attribute("forum.thread_id", thread_id)

request_data = request.data
if "course_id" in request_data:
set_custom_attribute("forum.course_id", request_data["course_id"])
if "user_id" in request_data:
set_custom_attribute("forum.author_id", request_data["user_id"])

try:
request_data = request.data
comment = create_parent_comment(
thread_id,
request_data["body"],
Expand Down
40 changes: 40 additions & 0 deletions forum/views/threads.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import logging
from typing import Any

from edx_django_utils.monitoring import set_custom_attribute # type: ignore[import-untyped]
from rest_framework import status
from rest_framework.permissions import AllowAny
from rest_framework.request import Request
Expand Down Expand Up @@ -42,8 +43,22 @@ def get(self, request: Request, thread_id: str) -> Response:
Returns:
Response: A Response object containing the serialized thread data or an error message.
"""
set_custom_attribute("forum.operation", "get_thread")
set_custom_attribute("forum.thread_id", thread_id)

try:
params = request.query_params.dict()

# Track request parameters
if "user_id" in params:
set_custom_attribute("forum.user_id", params["user_id"])
if "with_responses" in params:
set_custom_attribute("forum.with_responses", params["with_responses"])
if "resp_limit" in params:
set_custom_attribute("forum.resp_limit", params["resp_limit"])
if "resp_skip" in params:
set_custom_attribute("forum.resp_skip", params["resp_skip"])

data = get_thread(thread_id, params)
except ForumV2RequestError as error:
return Response(
Expand All @@ -64,6 +79,9 @@ def delete(self, request: Request, thread_id: str) -> Response:
Response:
The details of the thread that is deleted.
"""
set_custom_attribute("forum.operation", "delete_thread")
set_custom_attribute("forum.thread_id", thread_id)

try:
serialized_data = delete_thread(thread_id)
return Response(serialized_data, status=status.HTTP_200_OK)
Expand All @@ -85,6 +103,15 @@ def put(self, request: Request, thread_id: str) -> Response:
Response:
The details of the thread that is updated.
"""
set_custom_attribute("forum.operation", "update_thread")
set_custom_attribute("forum.thread_id", thread_id)

# Track what fields are being updated
if request.data:
update_fields = list(request.data.keys())
set_custom_attribute("forum.update_fields", ",".join(update_fields))
if "course_id" in request.data:
set_custom_attribute("forum.course_id", request.data["course_id"])

try:
serialized_data = update_thread(thread_id, **request.data)
Expand Down Expand Up @@ -117,6 +144,19 @@ def post(self, request: Request) -> Response:
Response:
The details of the thread that is created.
"""
set_custom_attribute("forum.operation", "create_thread")

# Track thread creation context
if "course_id" in request.data:
set_custom_attribute("forum.course_id", request.data["course_id"])
if "thread_type" in request.data:
set_custom_attribute("forum.thread_type", request.data["thread_type"])
if "commentable_id" in request.data:
set_custom_attribute("forum.commentable_id", request.data["commentable_id"])
if "user_id" in request.data:
set_custom_attribute("forum.author_id", request.data["user_id"])
if "group_id" in request.data:
set_custom_attribute("forum.group_id", str(request.data["group_id"]))

try:
params = request.data
Expand Down
Loading
Loading