Skip to content

Commit

Permalink
feat!: modify API (#560)
Browse files Browse the repository at this point in the history
return watch hours, courses count and programs count

Co-authored-by: Muhammad Faraz  Maqsood <[email protected]>
  • Loading branch information
Faraz32123 and Muhammad Faraz Maqsood committed Jul 9, 2024
1 parent db838de commit c382f39
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
"""

from django.urls import path # pylint: disable=unused-import
from .views import UserWatchHoursAPIView
from .views import UserStatsAPIView


app_name = "nafath_api_v1"

urlpatterns = [
path(r"user_watch_hours", UserWatchHoursAPIView.as_view()),
path(r"user_stats", UserStatsAPIView.as_view()),
]
73 changes: 68 additions & 5 deletions openedx/features/sdaia_features/course_progress/api/v1/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,27 @@
import logging
import requests

from ccx_keys.locator import CCXLocator
from django.conf import settings
from django.utils.decorators import method_decorator
from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication
from rest_framework import permissions, status
from rest_framework.response import Response
from rest_framework.views import APIView

from common.djangoapps.student.roles import (
CourseInstructorRole,
CourseStaffRole,
UserBasedRole,
)
from common.djangoapps.util.disable_rate_limit import can_disable_rate_limit
from lms.djangoapps.program_enrollments.rest_api.v1.views import (
UserProgramReadOnlyAccessView,
)
from openedx.core.djangoapps.catalog.utils import get_programs, get_programs_by_type
from openedx.core.djangoapps.cors_csrf.decorators import ensure_csrf_cookie_cross_domain
from openedx.core.djangoapps.enrollments.errors import CourseEnrollmentError
from openedx.core.djangoapps.enrollments.data import get_course_enrollments
from openedx.core.djangoapps.enrollments.views import EnrollmentCrossDomainSessionAuth
from openedx.core.lib.api.authentication import BearerAuthenticationAllowInactiveUser
from openedx.core.lib.api.permissions import ApiKeyHeaderPermissionIsAuthenticated
Expand All @@ -23,16 +35,18 @@


@can_disable_rate_limit
class UserWatchHoursAPIView(APIView):
class UserStatsAPIView(APIView):
"""
APIView to get the total watch hours for a user.
**Example Requests**
GET /sdaia/api/v1/user_watch_hours
GET /sdaia/api/v1/user_stats
It return watch_time in hours
Response: {
"watch_time": 0.00043390860160191856
"watch_hours": 0.00043390860160191856,
"enrolled_courses": enrolled_courses,
"enrolled_programs": enrolled_programs,
}
"""

Expand All @@ -48,19 +62,68 @@ def get(self, request):
"""
Gets the total watch hours for a user.
"""
user_id = request.user.id
user = request.user
user_id = user.id
clickhouse_uri = (
f"{settings.CAIRN_CLICKHOUSE_HTTP_SCHEME}://{settings.CAIRN_CLICKHOUSE_USERNAME}:{settings.CAIRN_CLICKHOUSE_PASSWORD}@"
f"{settings.CAIRN_CLICKHOUSE_HOST}:{settings.CAIRN_CLICKHOUSE_HTTP_PORT}/?database={settings.CAIRN_CLICKHOUSE_DATABASE}"
)
query = f"SELECT SUM(duration) as `Watch time` FROM `openedx`.`video_view_segments` WHERE user_id={user_id};"

############ WATCH HOURS ############
try:
response = requests.get(clickhouse_uri, data=query.encode("utf8"))
watch_time = float(response.content.decode().strip()) / (60 * 60)
return Response(status=status.HTTP_200_OK, data={"watch_time": watch_time})
except Exception as e:
log.error(
f"Unable to fetch watch for user {user_id} due to this exception: {str(e)}"
)
raise HTTPException(status_code=500, detail=str(e))

############ PROGRAMS COUNT ############
programs = []
requested_program_type = "masters"
if user.is_staff:
programs = get_programs_by_type(request.site, requested_program_type)
else:
program_dict = {}
# Check if the user is a course staff of any course which is a part of a program.
programs_user_is_staff_for = (
UserProgramReadOnlyAccessView().get_programs_user_is_course_staff_for(
user, requested_program_type
)
)
for staff_program in programs_user_is_staff_for:
program_dict.setdefault(staff_program["uuid"], staff_program)

# Now get the program enrollments for user purely as a learner add to the list
enrolled_programs = (
UserProgramReadOnlyAccessView()._get_enrolled_programs_from_model(user)
)
for learner_program in enrolled_programs:
program_dict.setdefault(learner_program["uuid"], learner_program)

programs = list(program_dict.values())
enrolled_programs = len(programs)

############ COURSES COUNT ############
username = user.username
try:
enrolled_courses = len(get_course_enrollments(username))
except CourseEnrollmentError:
return Response(
status=status.HTTP_400_BAD_REQUEST,
data={
"message": f"An error occurred while retrieving enrollments for user {username}"
},
)

############ Response ############
return Response(
status=status.HTTP_200_OK,
data={
"watch_hours": watch_time,
"enrolled_courses": enrolled_courses,
"enrolled_programs": enrolled_programs,
},
)

0 comments on commit c382f39

Please sign in to comment.