Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
fb28f68f
Commit
fb28f68f
authored
Sep 11, 2014
by
Zia Fazal
Committed by
Jonathan Piacenti
Aug 20, 2015
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
merged with master
move slash base course id logic in get_course_key
parent
b9b7940a
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
103 additions
and
17 deletions
+103
-17
lms/djangoapps/api_manager/courses/tests.py
+51
-1
lms/djangoapps/api_manager/courses/views.py
+30
-15
lms/djangoapps/api_manager/courseware_access.py
+6
-1
lms/lib/comment_client/thread.py
+16
-0
No files found.
lms/djangoapps/api_manager/courses/tests.py
View file @
fb28f68f
...
@@ -51,7 +51,16 @@ def _fake_get_get_course_social_stats(course_id):
...
@@ -51,7 +51,16 @@ def _fake_get_get_course_social_stats(course_id):
'2'
:
{
'one'
:
'two'
}
'2'
:
{
'one'
:
'two'
}
}
}
@mock.patch
(
"lms.lib.comment_client.user.get_course_social_stats"
,
_fake_get_get_course_social_stats
)
def
_fake_get_course_thread_stats
(
course_id
):
return
{
'num_threads'
:
5
,
'num_active_threads'
:
3
}
@mock.patch
(
"api_manager.courses.views.get_course_social_stats"
,
_fake_get_get_course_social_stats
)
@mock.patch
(
"api_manager.courses.views.get_course_thread_stats"
,
_fake_get_course_thread_stats
)
@override_settings
(
MODULESTORE
=
TEST_DATA_MIXED_MODULESTORE
)
@override_settings
(
MODULESTORE
=
TEST_DATA_MIXED_MODULESTORE
)
@override_settings
(
EDX_API_KEY
=
TEST_API_KEY
)
@override_settings
(
EDX_API_KEY
=
TEST_API_KEY
)
@mock.patch.dict
(
"django.conf.settings.FEATURES"
,
{
'ENFORCE_PASSWORD_POLICY'
:
False
,
@mock.patch.dict
(
"django.conf.settings.FEATURES"
,
{
'ENFORCE_PASSWORD_POLICY'
:
False
,
...
@@ -208,6 +217,13 @@ class CoursesApiTests(TestCase):
...
@@ -208,6 +217,13 @@ class CoursesApiTests(TestCase):
grade_dict
=
{
'value'
:
points_scored
,
'max_value'
:
points_possible
,
'user_id'
:
user
.
id
}
grade_dict
=
{
'value'
:
points_scored
,
'max_value'
:
points_possible
,
'user_id'
:
user
.
id
}
module
.
system
.
publish
(
module
,
'grade'
,
grade_dict
)
module
.
system
.
publish
(
module
,
'grade'
,
grade_dict
)
for
j
,
user
in
enumerate
(
self
.
users
):
StudentModuleFactory
.
create
(
course_id
=
self
.
course
.
id
,
module_type
=
'sequential'
,
module_state_key
=
self
.
item
.
location
,
)
self
.
test_course_id
=
unicode
(
self
.
course
.
id
)
self
.
test_course_id
=
unicode
(
self
.
course
.
id
)
self
.
test_bogus_course_id
=
'i4x://foo/bar/baz'
self
.
test_bogus_course_id
=
'i4x://foo/bar/baz'
self
.
test_course_name
=
self
.
course
.
display_name
self
.
test_course_name
=
self
.
course
.
display_name
...
@@ -1736,6 +1752,7 @@ class CoursesApiTests(TestCase):
...
@@ -1736,6 +1752,7 @@ class CoursesApiTests(TestCase):
def
test_courses_data_metrics
(
self
):
def
test_courses_data_metrics
(
self
):
test_uri
=
self
.
base_courses_uri
+
'/'
+
self
.
test_course_id
+
'/users'
test_uri
=
self
.
base_courses_uri
+
'/'
+
self
.
test_course_id
+
'/users'
completion_uri
=
'{}/{}/completions/'
.
format
(
self
.
base_courses_uri
,
unicode
(
self
.
course
.
id
))
test_user_uri
=
self
.
base_users_uri
test_user_uri
=
self
.
base_users_uri
users_to_add
=
5
users_to_add
=
5
for
i
in
xrange
(
0
,
users_to_add
):
for
i
in
xrange
(
0
,
users_to_add
):
...
@@ -1753,11 +1770,44 @@ class CoursesApiTests(TestCase):
...
@@ -1753,11 +1770,44 @@ class CoursesApiTests(TestCase):
response
=
self
.
do_post
(
test_uri
,
post_data
)
response
=
self
.
do_post
(
test_uri
,
post_data
)
self
.
assertEqual
(
response
.
status_code
,
201
)
self
.
assertEqual
(
response
.
status_code
,
201
)
#create an organization
data
=
{
'name'
:
'Test Organization'
,
'display_name'
:
'Test Org Display Name'
,
'users'
:
[
created_user_id
]
}
response
=
self
.
do_post
(
self
.
base_organizations_uri
,
data
)
self
.
assertEqual
(
response
.
status_code
,
201
)
org_id
=
response
.
data
[
'id'
]
for
i
in
xrange
(
1
,
5
):
local_content_name
=
'Video_Sequence{}'
.
format
(
i
)
local_content
=
ItemFactory
.
create
(
category
=
"videosequence"
,
parent_location
=
self
.
chapter
.
location
,
data
=
self
.
test_data
,
display_name
=
local_content_name
)
content_id
=
unicode
(
local_content
.
scope_ids
.
usage_id
)
completions_data
=
{
'content_id'
:
content_id
,
'user_id'
:
created_user_id
,
'stage'
:
None
}
response
=
self
.
do_post
(
completion_uri
,
completions_data
)
self
.
assertEqual
(
response
.
status_code
,
201
)
# get course metrics
# get course metrics
course_metrics_uri
=
'{}/{}/metrics/'
.
format
(
self
.
base_courses_uri
,
self
.
test_course_id
)
course_metrics_uri
=
'{}/{}/metrics/'
.
format
(
self
.
base_courses_uri
,
self
.
test_course_id
)
response
=
self
.
do_get
(
course_metrics_uri
)
response
=
self
.
do_get
(
course_metrics_uri
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
data
[
'users_enrolled'
],
users_to_add
+
USER_COUNT
)
self
.
assertEqual
(
response
.
data
[
'users_enrolled'
],
users_to_add
+
USER_COUNT
)
self
.
assertEqual
(
response
.
data
[
'users_started'
],
1
)
self
.
assertIsNotNone
(
response
.
data
[
'grade_cutoffs'
])
# get course metrics by organization
course_metrics_uri
=
'{}/{}/metrics/?organization={}'
.
format
(
self
.
base_courses_uri
,
self
.
test_course_id
,
org_id
)
response
=
self
.
do_get
(
course_metrics_uri
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
data
[
'users_enrolled'
],
1
)
self
.
assertEqual
(
response
.
data
[
'users_started'
],
1
)
# test with bogus course
# test with bogus course
course_metrics_uri
=
'{}/{}/metrics/'
.
format
(
self
.
base_courses_uri
,
self
.
test_bogus_course_id
)
course_metrics_uri
=
'{}/{}/metrics/'
.
format
(
self
.
base_courses_uri
,
self
.
test_bogus_course_id
)
...
...
lms/djangoapps/api_manager/courses/views.py
View file @
fb28f68f
...
@@ -29,7 +29,8 @@ from student.roles import CourseRole, CourseAccessRole, CourseInstructorRole, Co
...
@@ -29,7 +29,8 @@ from student.roles import CourseRole, CourseAccessRole, CourseInstructorRole, Co
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore.django
import
modulestore
from
api_manager.courseware_access
import
get_course
,
get_course_child
,
get_course_leaf_nodes
,
get_course_key
,
course_exists
,
get_modulestore
from
api_manager.courseware_access
import
get_course
,
get_course_child
,
get_course_leaf_nodes
,
get_course_key
,
\
course_exists
,
get_modulestore
from
api_manager.models
import
CourseGroupRelationship
,
CourseContentGroupRelationship
,
GroupProfile
,
\
from
api_manager.models
import
CourseGroupRelationship
,
CourseContentGroupRelationship
,
GroupProfile
,
\
CourseModuleCompletion
CourseModuleCompletion
from
api_manager.permissions
import
SecureAPIView
,
SecureListAPIView
from
api_manager.permissions
import
SecureAPIView
,
SecureListAPIView
...
@@ -41,6 +42,7 @@ from .serializers import CourseModuleCompletionSerializer, CourseSerializer
...
@@ -41,6 +42,7 @@ from .serializers import CourseModuleCompletionSerializer, CourseSerializer
from
.serializers
import
GradeSerializer
,
CourseLeadersSerializer
,
CourseCompletionsLeadersSerializer
from
.serializers
import
GradeSerializer
,
CourseLeadersSerializer
,
CourseCompletionsLeadersSerializer
from
lms.lib.comment_client.user
import
get_course_social_stats
from
lms.lib.comment_client.user
import
get_course_social_stats
from
lms.lib.comment_client.thread
import
get_course_thread_stats
from
lms.lib.comment_client.utils
import
CommentClientRequestError
from
lms.lib.comment_client.utils
import
CommentClientRequestError
from
opaque_keys.edx.keys
import
CourseKey
from
opaque_keys.edx.keys
import
CourseKey
...
@@ -1476,8 +1478,9 @@ class CoursesProjectList(SecureListAPIView):
...
@@ -1476,8 +1478,9 @@ class CoursesProjectList(SecureListAPIView):
class
CoursesMetrics
(
SecureAPIView
):
class
CoursesMetrics
(
SecureAPIView
):
"""
"""
### The CoursesMetrics view allows clients to retrieve a list of Metrics for the specified Course
### The CoursesMetrics view allows clients to retrieve a list of Metrics for the specified Course
- URI: ```/api/courses/{course_id}/metrics/```
- URI: ```/api/courses/{course_id}/metrics/
?organization={organization_id}
```
- GET: Returns a JSON representation (array) of the set of course metrics
- GET: Returns a JSON representation (array) of the set of course metrics
- metrics can be filtered by organization by adding organization parameter to GET request
### Use Cases/Notes:
### Use Cases/Notes:
* Example: Display number of users enrolled in a given course
* Example: Display number of users enrolled in a given course
"""
"""
...
@@ -1488,11 +1491,31 @@ class CoursesMetrics(SecureAPIView):
...
@@ -1488,11 +1491,31 @@ class CoursesMetrics(SecureAPIView):
"""
"""
if
not
course_exists
(
request
,
request
.
user
,
course_id
):
if
not
course_exists
(
request
,
request
.
user
,
course_id
):
return
Response
({},
status
=
status
.
HTTP_404_NOT_FOUND
)
return
Response
({},
status
=
status
.
HTTP_404_NOT_FOUND
)
course_key
=
get_course_key
(
course_id
)
course_descriptor
,
course_key
,
course_content
=
get_course
(
request
,
request
.
user
,
course_id
)
users_enrolled
=
CourseEnrollment
.
num_enrolled_in
(
course_key
)
slash_course_id
=
get_course_key
(
course_id
,
slashseparated
=
True
)
exclude_users
=
_get_aggregate_exclusion_user_ids
(
course_key
)
users_enrolled_qs
=
CourseEnrollment
.
users_enrolled_in
(
course_key
)
.
exclude
(
id__in
=
exclude_users
)
organization
=
request
.
QUERY_PARAMS
.
get
(
'organization'
,
None
)
if
organization
:
users_enrolled_qs
=
users_enrolled_qs
.
filter
(
organizations
=
organization
)
users_started_qs
=
CourseModuleCompletion
.
objects
.
filter
(
course_id
=
course_id
)
.
exclude
(
user_id__in
=
exclude_users
)
if
organization
:
users_started_qs
=
users_started_qs
.
filter
(
user__organizations
=
organization
)
data
=
{
data
=
{
'users_enrolled'
:
users_enrolled
'users_enrolled'
:
users_enrolled_qs
.
count
(),
'users_started'
:
users_started_qs
.
values
(
'user'
)
.
distinct
()
.
count
(),
'grade_cutoffs'
:
course_descriptor
.
grading_policy
[
'GRADE_CUTOFFS'
]
}
}
thread_stats
=
{}
try
:
thread_stats
=
get_course_thread_stats
(
slash_course_id
)
except
CommentClientRequestError
,
e
:
data
=
{
"err_msg"
:
str
(
e
)
}
return
Response
(
data
,
status
=
status
.
HTTP_500_INTERNAL_SERVER_ERROR
)
data
.
update
(
thread_stats
)
return
Response
(
data
,
status
=
status
.
HTTP_200_OK
)
return
Response
(
data
,
status
=
status
.
HTTP_200_OK
)
...
@@ -1633,18 +1656,10 @@ class CoursesMetricsSocial(SecureListAPIView):
...
@@ -1633,18 +1656,10 @@ class CoursesMetricsSocial(SecureListAPIView):
def
get
(
self
,
request
,
course_id
):
# pylint: disable=W0613
def
get
(
self
,
request
,
course_id
):
# pylint: disable=W0613
try
:
try
:
# be robust to the try of course_id we get from caller
slash_course_id
=
get_course_key
(
course_id
,
slashseparated
=
True
)
try
:
# assume new style
course_key
=
CourseKey
.
from_string
(
course_id
)
slash_course_id
=
course_key
.
to_deprecated_string
()
except
:
# assume course_id passed in is legacy format
slash_course_id
=
course_id
# the forum service expects the legacy slash separated string format
# the forum service expects the legacy slash separated string format
data
=
get_course_social_stats
(
slash_course_id
)
data
=
get_course_social_stats
(
slash_course_id
)
course_key
=
get_course_key
(
course_id
)
# remove any excluded users from the aggregate
# remove any excluded users from the aggregate
exclude_users
=
_get_aggregate_exclusion_user_ids
(
course_key
)
exclude_users
=
_get_aggregate_exclusion_user_ids
(
course_key
)
...
...
lms/djangoapps/api_manager/courseware_access.py
View file @
fb28f68f
...
@@ -67,7 +67,7 @@ def get_course_leaf_nodes(course_key, detached_categories):
...
@@ -67,7 +67,7 @@ def get_course_leaf_nodes(course_key, detached_categories):
return
nodes
return
nodes
def
get_course_key
(
course_id
):
def
get_course_key
(
course_id
,
slashseparated
=
False
):
try
:
try
:
course_key
=
CourseKey
.
from_string
(
course_id
)
course_key
=
CourseKey
.
from_string
(
course_id
)
except
InvalidKeyError
:
except
InvalidKeyError
:
...
@@ -75,6 +75,11 @@ def get_course_key(course_id):
...
@@ -75,6 +75,11 @@ def get_course_key(course_id):
course_key
=
SlashSeparatedCourseKey
.
from_deprecated_string
(
course_id
)
course_key
=
SlashSeparatedCourseKey
.
from_deprecated_string
(
course_id
)
except
InvalidKeyError
:
except
InvalidKeyError
:
course_key
=
None
course_key
=
None
if
slashseparated
:
try
:
course_key
=
course_key
.
to_deprecated_string
()
except
:
course_key
=
course_id
return
course_key
return
course_key
...
...
lms/lib/comment_client/thread.py
View file @
fb28f68f
...
@@ -198,6 +198,18 @@ class Thread(models.Model):
...
@@ -198,6 +198,18 @@ class Thread(models.Model):
self
.
_update_from_response
(
response
)
self
.
_update_from_response
(
response
)
def
get_course_thread_stats
(
course_id
):
"""
Helper method to get threads stats by course
"""
url
=
_url_for_course_thread_stats
(
course_id
)
response
=
perform_request
(
'get'
,
url
)
return
response
def
_url_for_flag_abuse_thread
(
thread_id
):
def
_url_for_flag_abuse_thread
(
thread_id
):
return
"{prefix}/threads/{thread_id}/abuse_flag"
.
format
(
prefix
=
settings
.
PREFIX
,
thread_id
=
thread_id
)
return
"{prefix}/threads/{thread_id}/abuse_flag"
.
format
(
prefix
=
settings
.
PREFIX
,
thread_id
=
thread_id
)
...
@@ -212,3 +224,7 @@ def _url_for_pin_thread(thread_id):
...
@@ -212,3 +224,7 @@ def _url_for_pin_thread(thread_id):
def
_url_for_un_pin_thread
(
thread_id
):
def
_url_for_un_pin_thread
(
thread_id
):
return
"{prefix}/threads/{thread_id}/unpin"
.
format
(
prefix
=
settings
.
PREFIX
,
thread_id
=
thread_id
)
return
"{prefix}/threads/{thread_id}/unpin"
.
format
(
prefix
=
settings
.
PREFIX
,
thread_id
=
thread_id
)
def
_url_for_course_thread_stats
(
course_id
):
return
"{prefix}/courses/{course_id}/stats"
.
format
(
prefix
=
settings
.
PREFIX
,
course_id
=
course_id
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment