-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #55 from eduNEXT/and/stats_api_view
And/stats api view
- Loading branch information
Showing
24 changed files
with
1,087 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
"""Backend for branding django app module. | ||
This file contains all the necessary branding dependencies from | ||
https://github.com/eduNEXT/edunext-platform/blob/ednx-release/mango.master/lms/djangoapps/branding/__init__.py""" | ||
from lms.djangoapps.branding import get_visible_courses | ||
|
||
|
||
def get_visible_courses_method(): | ||
"""Allow to get the get_visible_courses function from | ||
https://github.com/eduNEXT/edunext-platform/blob/ednx-release/mango.master/lms/djangoapps/branding/__init__.py#L17 | ||
Returns: | ||
get_visible_courses function. | ||
""" | ||
return get_visible_courses |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
"""Wrapper for module in branding app. | ||
This contains all the required dependencies from branding. | ||
Attributes: | ||
get_visible_courses: Wrapper get_visible_courses function. | ||
""" | ||
from importlib import import_module | ||
|
||
from django.conf import settings | ||
|
||
backend = import_module(settings.EOX_NELP_BRANDING_BACKEND) | ||
|
||
get_visible_courses = backend.get_visible_courses_method() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
"""Test backend for bulk-email module.""" | ||
from mock import Mock | ||
|
||
|
||
def get_visible_courses_method(): | ||
"""Return test function. | ||
Returns: | ||
Mock class. | ||
""" | ||
return Mock() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
"""eox_nelp course_experience api urls | ||
""" | ||
from django.urls import include, path | ||
|
||
app_name = "eox_nelp" # pylint: disable=invalid-name | ||
|
||
urlpatterns = [ # pylint: disable=invalid-name | ||
path("v1/", include("eox_nelp.stats.api.v1.urls", namespace="v1")), | ||
] |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
""" | ||
Course API URLs | ||
""" | ||
from django.conf import settings | ||
from django.urls import path, re_path | ||
|
||
from eox_nelp.stats.api.v1.views import GeneralCourseStatsView, GeneralTenantStatsView | ||
|
||
app_name = "eox_nelp" # pylint: disable=invalid-name | ||
|
||
urlpatterns = [ | ||
path('tenant/', GeneralTenantStatsView.as_view(), name="general-stats"), | ||
path('courses/', GeneralCourseStatsView.as_view(), name="courses-stats"), | ||
re_path(rf'^courses/{settings.COURSE_ID_PATTERN}', GeneralCourseStatsView.as_view(), name="course-stats"), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
"""Stats API v1 view file. | ||
views: | ||
GeneralTenantStatsView: View that handles the general tenant stats. | ||
GeneralTenantCoursesView: View that handles the general courses stats. | ||
""" | ||
from django.contrib.auth import get_user_model | ||
from django.http import Http404 | ||
from rest_framework.response import Response | ||
from rest_framework.views import APIView | ||
|
||
from eox_nelp.stats import metrics | ||
|
||
User = get_user_model() | ||
|
||
|
||
class GeneralTenantStatsView(APIView): | ||
"""Class view. Handle general tenant stats. | ||
## Usage | ||
The components key depends on the setting API_XBLOCK_TYPES, this should be | ||
a list of strings like the following | ||
``` json | ||
[" problem", "video", "discussion"] | ||
``` | ||
### **GET** /eox-nelp/api/stats/v1/tenant/ | ||
**GET Response Values** | ||
``` json | ||
{ | ||
"learners": 1, | ||
"courses": 3, | ||
"instructors": 2, | ||
"components": { | ||
"discussion": 0, | ||
"drag-and-drop-v2": 0, | ||
"html": 133, | ||
"openassessment": 0, | ||
"problem": 49, | ||
"video": 0 | ||
} | ||
} | ||
``` | ||
""" | ||
|
||
def get(self, request): | ||
"""Return general tenant stats.""" | ||
tenant = request.site.domain | ||
courses = metrics.get_courses_metrics(tenant) | ||
components = {} | ||
|
||
for metric in courses.get("metrics", []): | ||
course_components = metric.get("components", {}) | ||
|
||
for key, value in course_components.items(): | ||
components[key] = components.get(key, 0) + value | ||
|
||
return Response({ | ||
"learners": metrics.get_learners_metric(tenant), | ||
"courses": courses.get("total_courses", 0), | ||
"instructors": metrics.get_instructors_metric(tenant), | ||
"components": components | ||
}) | ||
|
||
|
||
class GeneralCourseStatsView(APIView): | ||
"""Class view that returns a list of course stats or a specific course stats. | ||
## Usage | ||
The components key depends on the setting ALLOWED_VERTICAL_BLOCK_TYPES, this should be | ||
a list of strings like the following | ||
``` json | ||
[" problem", "video", "discussion"] | ||
``` | ||
### **GET** /eox-nelp/api/stats/v1/courses/ | ||
**GET Response Values** | ||
``` json | ||
{ | ||
"total_courses": 4, | ||
"metrics": [ | ||
{ | ||
"id": "course-v1:patata+CS102+2023", | ||
"name": "PROCEDURAL SEDATION AND ANALGESIA COURSE", | ||
"learners": 0, | ||
"instructors": 1, | ||
"sections": 18, | ||
"sub_sections": 144, | ||
"units": 184, | ||
"components": { | ||
"discussion": 0, | ||
"drag-and-drop-v2": 0, | ||
"html": 133, | ||
"openassessment": 0, | ||
"problem": 49, | ||
"video": 0 | ||
} | ||
}, | ||
... | ||
] | ||
} | ||
``` | ||
### **GET** /eox-nelp/api/stats/v1/courses/course-v1:potato+CS102+2023/ | ||
**GET Response Values** | ||
``` json | ||
{ | ||
"id": "course-v1:potato+CS102+2023", | ||
"name": "PROCEDURAL SEDATION AND ANALGESIA COURSE", | ||
"learners": 0, | ||
"instructors": 1, | ||
"sections": 18, | ||
"sub_sections": 144, | ||
"units": 184, | ||
"components": { | ||
"discussion": 0, | ||
"drag-and-drop-v2": 0, | ||
"html": 133, | ||
"openassessment": 0, | ||
"problem": 49, | ||
"video": 0 | ||
} | ||
} | ||
``` | ||
""" | ||
|
||
def get(self, request, course_id=None): | ||
"""Return general course stats.""" | ||
tenant = request.site.domain | ||
|
||
if course_id: | ||
courses = metrics.get_cached_courses(tenant) | ||
course = courses.filter(id=course_id).first() | ||
|
||
if not course: | ||
raise Http404 | ||
|
||
return Response(metrics.get_course_metrics(course.id)) | ||
|
||
return Response(metrics.get_courses_metrics(tenant)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
"""eox-nelp Metrics file. | ||
decorators: | ||
cache_method: Cache the result of the inner method. | ||
""" | ||
from django.conf import settings | ||
from django.core.cache import cache | ||
|
||
|
||
def cache_method(func): | ||
""" | ||
Cache the function result to improve the response time. | ||
Args: | ||
func<function>: Target function to be cached. | ||
Return: | ||
<funtion>: Wrapper function. | ||
""" | ||
def wrapper(*args, **kwargs): | ||
key = f"{func.__name__}.{'-'.join(map(str, args))}.STATS_CACHE_KEY" | ||
result = cache.get(key) | ||
|
||
if result: | ||
return result | ||
|
||
result = func(*args, **kwargs) | ||
|
||
cache.set( | ||
key, | ||
result, | ||
timeout=getattr(settings, "STATS_SETTINGS", {}).get("STATS_TIMEOUT", 3600), | ||
) | ||
|
||
return result | ||
|
||
return wrapper |
Oops, something went wrong.