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
330120bb
Commit
330120bb
authored
Nov 24, 2015
by
Nimisha Asthagiri
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
MA-1712: Update Mobile API to include course_about
parent
a7e21bf1
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
103 additions
and
66 deletions
+103
-66
lms/djangoapps/mobile_api/testutils.py
+7
-3
lms/djangoapps/mobile_api/users/serializers.py
+63
-50
lms/djangoapps/mobile_api/users/tests.py
+22
-11
lms/djangoapps/mobile_api/users/views.py
+11
-2
No files found.
lms/djangoapps/mobile_api/testutils.py
View file @
330120bb
...
...
@@ -139,14 +139,17 @@ class MobileCourseAccessTestMixin(MobileAPIMilestonesMixin):
Subclasses can override verify_success, verify_failure, and init_course_access methods.
"""
ALLOW_ACCESS_TO_UNRELEASED_COURSE
=
False
# pylint: disable=invalid-name
ALLOW_ACCESS_TO_NON_VISIBLE_COURSE
=
False
# pylint: disable=invalid-name
def
verify_success
(
self
,
response
):
"""Base implementation of verifying a successful response."""
self
.
assertEqual
(
response
.
status_code
,
200
)
def
verify_failure
(
self
,
response
):
def
verify_failure
(
self
,
response
,
error_type
=
None
):
"""Base implementation of verifying a failed response."""
self
.
assertEqual
(
response
.
status_code
,
404
)
if
error_type
:
self
.
assertEqual
(
response
.
data
,
error_type
.
to_json
())
def
init_course_access
(
self
,
course_id
=
None
):
"""Base implementation of initializing the user for each test."""
...
...
@@ -201,6 +204,8 @@ class MobileCourseAccessTestMixin(MobileAPIMilestonesMixin):
self
.
init_course_access
()
self
.
course
.
visible_to_staff_only
=
True
self
.
store
.
update_item
(
self
.
course
,
self
.
user
.
id
)
if
self
.
ALLOW_ACCESS_TO_NON_VISIBLE_COURSE
:
should_succeed
=
True
self
.
_verify_response
(
should_succeed
,
VisibilityError
(),
role
)
def
_verify_response
(
self
,
should_succeed
,
error_type
,
role
=
None
):
...
...
@@ -216,5 +221,4 @@ class MobileCourseAccessTestMixin(MobileAPIMilestonesMixin):
if
should_succeed
:
self
.
verify_success
(
response
)
else
:
self
.
verify_failure
(
response
)
self
.
assertEqual
(
response
.
data
,
error_type
.
to_json
())
self
.
verify_failure
(
response
,
error_type
)
lms/djangoapps/mobile_api/users/serializers.py
View file @
330120bb
...
...
@@ -13,70 +13,83 @@ from xmodule.course_module import DEFAULT_START_DATE
class
CourseOverviewField
(
serializers
.
RelatedField
):
"""Custom field to wrap a CourseDescriptor object. Read-only."""
"""
Custom field to wrap a CourseOverview object. Read-only.
"""
def
to_representation
(
self
,
course_overview
):
course_id
=
unicode
(
course_overview
.
id
)
request
=
self
.
context
.
get
(
'request'
,
None
)
if
request
:
video_outline_url
=
reverse
(
'video-summary-list'
,
if
course_overview
.
advertised_start
is
not
None
:
start_type
=
'string'
start_display
=
course_overview
.
advertised_start
elif
course_overview
.
start
!=
DEFAULT_START_DATE
:
start_type
=
'timestamp'
start_display
=
defaultfilters
.
date
(
course_overview
.
start
,
'DATE_FORMAT'
)
else
:
start_type
=
'empty'
start_display
=
None
request
=
self
.
context
.
get
(
'request'
)
return
{
# identifiers
'id'
:
course_id
,
'name'
:
course_overview
.
display_name
,
'number'
:
course_overview
.
display_number_with_default
,
'org'
:
course_overview
.
display_org_with_default
,
# dates
'start'
:
course_overview
.
start
,
'start_display'
:
start_display
,
'start_type'
:
start_type
,
'end'
:
course_overview
.
end
,
# notification info
'subscription_id'
:
course_overview
.
clean_id
(
padding_char
=
'_'
),
# access info
'courseware_access'
:
has_access
(
request
.
user
,
'load_mobile'
,
course_overview
)
.
to_json
(),
# various URLs
'course_image'
:
course_overview
.
course_image_url
,
'course_about'
:
reverse
(
'about_course'
,
kwargs
=
{
'course_id'
:
course_id
},
request
=
request
)
course_updates_url
=
reverse
(
request
=
request
,
)
,
'course_updates'
:
reverse
(
'course-updates-list'
,
kwargs
=
{
'course_id'
:
course_id
},
request
=
request
)
course_handouts_url
=
reverse
(
request
=
request
,
)
,
'course_handouts'
:
reverse
(
'course-handouts-list'
,
kwargs
=
{
'course_id'
:
course_id
},
request
=
request
)
discussion_url
=
reverse
(
request
=
request
,
)
,
'discussion_url'
:
reverse
(
'discussion_course'
,
kwargs
=
{
'course_id'
:
course_id
},
request
=
request
)
if
course_overview
.
is_discussion_tab_enabled
()
else
None
else
:
video_outline_url
=
None
course_updates_url
=
None
course_handouts_url
=
None
discussion_url
=
None
request
=
request
,
)
if
course_overview
.
is_discussion_tab_enabled
()
else
None
,
if
course_overview
.
advertised_start
is
not
None
:
start_type
=
"string"
start_display
=
course_overview
.
advertised_start
elif
course_overview
.
start
!=
DEFAULT_START_DATE
:
start_type
=
"timestamp"
start_display
=
defaultfilters
.
date
(
course_overview
.
start
,
"DATE_FORMAT"
)
else
:
start_type
=
"empty"
start_display
=
None
'video_outline'
:
reverse
(
'video-summary-list'
,
kwargs
=
{
'course_id'
:
course_id
},
request
=
request
,
),
return
{
"id"
:
course_id
,
"name"
:
course_overview
.
display_name
,
"number"
:
course_overview
.
display_number_with_default
,
"org"
:
course_overview
.
display_org_with_default
,
"start"
:
course_overview
.
start
,
"start_display"
:
start_display
,
"start_type"
:
start_type
,
"end"
:
course_overview
.
end
,
"course_image"
:
course_overview
.
course_image_url
,
"social_urls"
:
{
"facebook"
:
course_overview
.
facebook_url
,
# Note: The following 2 should be deprecated.
'social_urls'
:
{
'facebook'
:
course_overview
.
facebook_url
,
},
"latest_updates"
:
{
"video"
:
None
'latest_updates'
:
{
'video'
:
None
},
"video_outline"
:
video_outline_url
,
"course_updates"
:
course_updates_url
,
"course_handouts"
:
course_handouts_url
,
"discussion_url"
:
discussion_url
,
"subscription_id"
:
course_overview
.
clean_id
(
padding_char
=
'_'
),
"courseware_access"
:
has_access
(
request
.
user
,
'load_mobile'
,
course_overview
)
.
to_json
()
if
request
else
None
}
...
...
lms/djangoapps/mobile_api/users/tests.py
View file @
330120bb
...
...
@@ -9,6 +9,7 @@ import pytz
from
django.conf
import
settings
from
django.utils
import
timezone
from
django.template
import
defaultfilters
from
django.test
import
RequestFactory
from
certificates.models
import
CertificateStatuses
from
certificates.tests.factories
import
GeneratedCertificateFactory
...
...
@@ -24,11 +25,11 @@ from util.milestones_helpers import (
)
from
xmodule.course_module
import
DEFAULT_START_DATE
from
xmodule.modulestore.tests.factories
import
ItemFactory
,
CourseFactory
from
util.testing
import
UrlResetMixin
from
..
import
errors
from
..testutils
import
MobileAPITestCase
,
MobileAuthTestMixin
,
MobileAuthUserTestMixin
,
MobileCourseAccessTestMixin
from
.serializers
import
CourseEnrollmentSerializer
from
util.testing
import
UrlResetMixin
class
TestUserDetailApi
(
MobileAPITestCase
,
MobileAuthUserTestMixin
):
...
...
@@ -61,13 +62,14 @@ class TestUserInfoApi(MobileAPITestCase, MobileAuthTestMixin):
@ddt.ddt
class
TestUserEnrollmentApi
(
UrlResetMixin
,
MobileAPITestCase
,
MobileAuthUserTestMixin
):
class
TestUserEnrollmentApi
(
UrlResetMixin
,
MobileAPITestCase
,
MobileAuthUserTestMixin
,
MobileCourseAccessTestMixin
):
"""
Tests for /api/mobile/v0.5/users/<user_name>/course_enrollments/
"""
REVERSE_INFO
=
{
'name'
:
'courseenrollment-detail'
,
'params'
:
[
'username'
]}
ALLOW_ACCESS_TO_UNRELEASED_COURSE
=
True
ALLOW_ACCESS_TO_MILESTONE_COURSE
=
True
ALLOW_ACCESS_TO_NON_VISIBLE_COURSE
=
True
NEXT_WEEK
=
datetime
.
datetime
.
now
(
pytz
.
UTC
)
+
datetime
.
timedelta
(
days
=
7
)
LAST_WEEK
=
datetime
.
datetime
.
now
(
pytz
.
UTC
)
-
datetime
.
timedelta
(
days
=
7
)
ADVERTISED_START
=
"Spring 2016"
...
...
@@ -85,13 +87,15 @@ class TestUserEnrollmentApi(UrlResetMixin, MobileAPITestCase, MobileAuthUserTest
self
.
assertEqual
(
len
(
courses
),
1
)
found_course
=
courses
[
0
][
'course'
]
self
.
assertTrue
(
'video_outline'
in
found_course
)
self
.
assertTrue
(
'course_handouts'
in
found_course
)
self
.
assertIn
(
'courses/{}/about'
.
format
(
self
.
course
.
id
),
found_course
[
'course_about'
])
self
.
assertIn
(
'course_info/{}/updates'
.
format
(
self
.
course
.
id
),
found_course
[
'course_updates'
])
self
.
assertIn
(
'course_info/{}/handouts'
.
format
(
self
.
course
.
id
),
found_course
[
'course_handouts'
])
self
.
assertIn
(
'video_outlines/courses/{}'
.
format
(
self
.
course
.
id
),
found_course
[
'video_outline'
])
self
.
assertEqual
(
found_course
[
'id'
],
unicode
(
self
.
course
.
id
))
self
.
assertEqual
(
courses
[
0
][
'mode'
],
'honor'
)
self
.
assertEqual
(
courses
[
0
][
'course'
][
'subscription_id'
],
self
.
course
.
clean_id
(
padding_char
=
'_'
))
def
verify_failure
(
self
,
response
):
def
verify_failure
(
self
,
response
,
error_type
=
None
):
self
.
assertEqual
(
response
.
status_code
,
200
)
courses
=
response
.
data
self
.
assertEqual
(
len
(
courses
),
0
)
...
...
@@ -380,22 +384,29 @@ class TestCourseEnrollmentSerializer(MobileAPITestCase):
"""
Test the course enrollment serializer
"""
def
test_success
(
self
):
def
setUp
(
self
):
super
(
TestCourseEnrollmentSerializer
,
self
)
.
setUp
()
self
.
login_and_enroll
()
self
.
request
=
RequestFactory
()
.
get
(
'/'
)
self
.
request
.
user
=
self
.
user
serialized
=
CourseEnrollmentSerializer
(
CourseEnrollment
.
enrollments_for_user
(
self
.
user
)[
0
])
.
data
self
.
assertEqual
(
serialized
[
'course'
][
'video_outline'
],
None
)
def
test_success
(
self
):
serialized
=
CourseEnrollmentSerializer
(
CourseEnrollment
.
enrollments_for_user
(
self
.
user
)[
0
],
context
=
{
'request'
:
self
.
request
},
)
.
data
self
.
assertEqual
(
serialized
[
'course'
][
'name'
],
self
.
course
.
display_name
)
self
.
assertEqual
(
serialized
[
'course'
][
'number'
],
self
.
course
.
id
.
course
)
self
.
assertEqual
(
serialized
[
'course'
][
'org'
],
self
.
course
.
id
.
org
)
def
test_with_display_overrides
(
self
):
self
.
login_and_enroll
()
self
.
course
.
display_coursenumber
=
"overridden_number"
self
.
course
.
display_organization
=
"overridden_org"
self
.
store
.
update_item
(
self
.
course
,
self
.
user
.
id
)
serialized
=
CourseEnrollmentSerializer
(
CourseEnrollment
.
enrollments_for_user
(
self
.
user
)[
0
])
.
data
serialized
=
CourseEnrollmentSerializer
(
CourseEnrollment
.
enrollments_for_user
(
self
.
user
)[
0
],
context
=
{
'request'
:
self
.
request
},
)
.
data
self
.
assertEqual
(
serialized
[
'course'
][
'number'
],
self
.
course
.
display_coursenumber
)
self
.
assertEqual
(
serialized
[
'course'
][
'org'
],
self
.
course
.
display_organization
)
lms/djangoapps/mobile_api/users/views.py
View file @
330120bb
...
...
@@ -225,9 +225,15 @@ class UserCourseEnrollmentsList(generics.ListAPIView):
course.
* course: A collection of the following data about the course.
* courseware_access: A JSON representation with access information for the course,
including any access errors.
* course_about: The URL to the course about page.
* course_handouts: The URI to get data for course handouts.
* course_image: The path to the course image.
* course_updates: The URI to get data for course updates.
* discussion_url: The URI to access data for course discussions if
it is enabled, otherwise null.
* end: The end date of the course.
* id: The unique ID of the course.
* latest_updates: Reserved for future use.
...
...
@@ -235,12 +241,15 @@ class UserCourseEnrollmentsList(generics.ListAPIView):
* number: The course number.
* org: The organization that created the course.
* start: The date and time when the course starts.
* start_display:
If start_type is a string, then the advertised_start date for the course.
If start_type is a timestamp, then a formatted date for the start of the course.
If start_type is empty, then the value is None and it indicates that the course has not yet started.
* start_type: One of either "string", "timestamp", or "empty"
* subscription_id: A unique "clean" (alphanumeric with '_') ID of
the course.
* video_outline: The URI to get the list of all videos that the user
can access in the course.
* discussion_url: The URI to access data for course discussions if
it is enabled, otherwise null.
* created: The date the course was created.
* is_active: Whether the course is currently active. Possible values
...
...
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