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
c375f666
Commit
c375f666
authored
Feb 24, 2015
by
Lee Newton
Committed by
Nimisha Asthagiri
Mar 02, 2015
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
bnotions contributions to mobile api
parent
ecbfe5c9
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
34 changed files
with
1223 additions
and
1 deletions
+1223
-1
lms/djangoapps/mobile_api/social_facebook/__init__.py
+42
-0
lms/djangoapps/mobile_api/social_facebook/courses/__init__.py
+3
-0
lms/djangoapps/mobile_api/social_facebook/courses/models.py
+3
-0
lms/djangoapps/mobile_api/social_facebook/courses/serializers.py
+11
-0
lms/djangoapps/mobile_api/social_facebook/courses/tests.py
+147
-0
lms/djangoapps/mobile_api/social_facebook/courses/urls.py
+15
-0
lms/djangoapps/mobile_api/social_facebook/courses/views.py
+64
-0
lms/djangoapps/mobile_api/social_facebook/friends/__init__.py
+3
-0
lms/djangoapps/mobile_api/social_facebook/friends/models.py
+3
-0
lms/djangoapps/mobile_api/social_facebook/friends/serializers.py
+11
-0
lms/djangoapps/mobile_api/social_facebook/friends/tests.py
+0
-0
lms/djangoapps/mobile_api/social_facebook/friends/urls.py
+16
-0
lms/djangoapps/mobile_api/social_facebook/friends/views.py
+71
-0
lms/djangoapps/mobile_api/social_facebook/groups/__init__.py
+3
-0
lms/djangoapps/mobile_api/social_facebook/groups/models.py
+3
-0
lms/djangoapps/mobile_api/social_facebook/groups/serializers.py
+30
-0
lms/djangoapps/mobile_api/social_facebook/groups/tests.py
+199
-0
lms/djangoapps/mobile_api/social_facebook/groups/urls.py
+20
-0
lms/djangoapps/mobile_api/social_facebook/groups/views.py
+143
-0
lms/djangoapps/mobile_api/social_facebook/models.py
+3
-0
lms/djangoapps/mobile_api/social_facebook/preferences/__init__.py
+3
-0
lms/djangoapps/mobile_api/social_facebook/preferences/models.py
+3
-0
lms/djangoapps/mobile_api/social_facebook/preferences/serializers.py
+11
-0
lms/djangoapps/mobile_api/social_facebook/preferences/tests.py
+68
-0
lms/djangoapps/mobile_api/social_facebook/preferences/urls.py
+14
-0
lms/djangoapps/mobile_api/social_facebook/preferences/views.py
+52
-0
lms/djangoapps/mobile_api/social_facebook/test_utils.py
+184
-0
lms/djangoapps/mobile_api/social_facebook/urls.py
+11
-0
lms/djangoapps/mobile_api/social_facebook/utils.py
+68
-0
lms/djangoapps/mobile_api/urls.py
+7
-1
lms/envs/aws.py
+5
-0
lms/envs/common.py
+1
-0
lms/envs/test.py
+5
-0
requirements/edx/base.txt
+1
-0
No files found.
lms/djangoapps/mobile_api/social_facebook/__init__.py
0 → 100644
View file @
c375f666
"""
Social Facebook API
"""
# TODO
# There are still some performance and scalability issues that should be
# addressed for the various endpoints in this social_facebook djangoapp.
#
# For the Courses and Friends API:
# For both endpoints, we are retrieving the same data from the Facebook server.
# We are then simply organizing and filtering that data differently for each endpoint.
#
# Here are 3 ideas that can be explored further:
#
# Option 1. The app can just call one endpoint that provides a mapping between CourseIDs and Friends,
# and then cache that data once. The reverse map from Friends to CourseIDs can then be created on the app side.
#
# Option 2. The app once again calls just one endpoint (since the same data is computed for both),
# and caches the data once. The difference from #1 is that the server does the computation of the reverse-map and
# sends both maps down to the client. It's a tradeoff between bandwidth and client-side computation. So the payload
# could be something like:
#
# {
# courses: [
# {course_id: "c/ourse/1", friend_indices: [1, 2, 3]},
# {course_id: "c/ourse/2", friend_indices: [3, 4, 5]},
# ..
# ],
# friends: [
# {username: "friend1", facebook_id: "xxx", course_indices: [2, 7, 9]},
# {username: "friend2", facebook_id: "yyy", course_indices: [1, 4, 3]},
# ...
# ]
# }
#
# Option 3. Alternatively, continue to have separate endpoints, but have both endpoints call the same underlying method
# with a built-in cache.
#
# All 3 options can make use of a common cache of results from FB.
#
# At a minimum, some performance/load testing would need to be done
# so we have an idea of these endpoints' limitations and thresholds.
lms/djangoapps/mobile_api/social_facebook/courses/__init__.py
0 → 100644
View file @
c375f666
"""
Courses API
"""
lms/djangoapps/mobile_api/social_facebook/courses/models.py
0 → 100644
View file @
c375f666
"""
A models.py is required to make this an app (until we move to Django 1.7)
"""
lms/djangoapps/mobile_api/social_facebook/courses/serializers.py
0 → 100644
View file @
c375f666
"""
Serializer for courses API
"""
from
rest_framework
import
serializers
class
CoursesWithFriendsSerializer
(
serializers
.
Serializer
):
"""
Serializes oauth token for facebook groups request
"""
oauth_token
=
serializers
.
CharField
(
required
=
True
)
lms/djangoapps/mobile_api/social_facebook/courses/tests.py
0 → 100644
View file @
c375f666
# pylint: disable=E1101, W0201
"""
Tests for Courses
"""
import
httpretty
import
json
from
django.core.urlresolvers
import
reverse
from
xmodule.modulestore.tests.factories
import
CourseFactory
from
opaque_keys.edx.keys
import
CourseKey
from
..test_utils
import
SocialFacebookTestCase
class
TestCourses
(
SocialFacebookTestCase
):
"""
Tests for /api/mobile/v0.5/courses/...
"""
def
setUp
(
self
):
super
(
TestCourses
,
self
)
.
setUp
()
self
.
course
=
CourseFactory
.
create
(
mobile_available
=
True
)
@httpretty.activate
def
test_one_course_with_friends
(
self
):
self
.
user_create_and_signin
(
1
)
self
.
link_edx_account_to_social
(
self
.
users
[
1
],
self
.
BACKEND
,
self
.
USERS
[
1
][
'FB_ID'
])
self
.
set_sharing_preferences
(
self
.
users
[
1
],
True
)
self
.
set_facebook_interceptor_for_friends
(
{
'data'
:
[{
'name'
:
self
.
USERS
[
1
][
'USERNAME'
],
'id'
:
self
.
USERS
[
1
][
'FB_ID'
]}]}
)
self
.
enroll_in_course
(
self
.
users
[
1
],
self
.
course
)
url
=
reverse
(
'courses-with-friends'
)
response
=
self
.
client
.
get
(
url
,
{
'oauth_token'
:
self
.
_FB_USER_ACCESS_TOKEN
})
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
self
.
course
.
id
,
CourseKey
.
from_string
(
response
.
data
[
0
][
'course'
][
'id'
]))
# pylint: disable=E1101
@httpretty.activate
def
test_two_courses_with_friends
(
self
):
self
.
user_create_and_signin
(
1
)
self
.
link_edx_account_to_social
(
self
.
users
[
1
],
self
.
BACKEND
,
self
.
USERS
[
1
][
'FB_ID'
])
self
.
set_sharing_preferences
(
self
.
users
[
1
],
True
)
self
.
enroll_in_course
(
self
.
users
[
1
],
self
.
course
)
self
.
course_2
=
CourseFactory
.
create
(
mobile_available
=
True
)
self
.
enroll_in_course
(
self
.
users
[
1
],
self
.
course_2
)
self
.
set_facebook_interceptor_for_friends
(
{
'data'
:
[{
'name'
:
self
.
USERS
[
2
][
'USERNAME'
],
'id'
:
self
.
USERS
[
1
][
'FB_ID'
]}]}
)
url
=
reverse
(
'courses-with-friends'
)
response
=
self
.
client
.
get
(
url
,
{
'oauth_token'
:
self
.
_FB_USER_ACCESS_TOKEN
})
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
self
.
course
.
id
,
CourseKey
.
from_string
(
response
.
data
[
0
][
'course'
][
'id'
]))
# pylint: disable=E1101
self
.
assertEqual
(
self
.
course_2
.
id
,
CourseKey
.
from_string
(
response
.
data
[
1
][
'course'
][
'id'
]))
# pylint: disable=E1101
@httpretty.activate
def
test_three_courses_but_only_two_unique
(
self
):
self
.
user_create_and_signin
(
1
)
self
.
link_edx_account_to_social
(
self
.
users
[
1
],
self
.
BACKEND
,
self
.
USERS
[
1
][
'FB_ID'
])
self
.
set_sharing_preferences
(
self
.
users
[
1
],
True
)
self
.
course_2
=
CourseFactory
.
create
(
mobile_available
=
True
)
self
.
enroll_in_course
(
self
.
users
[
1
],
self
.
course_2
)
self
.
enroll_in_course
(
self
.
users
[
1
],
self
.
course
)
self
.
user_create_and_signin
(
2
)
self
.
link_edx_account_to_social
(
self
.
users
[
2
],
self
.
BACKEND
,
self
.
USERS
[
2
][
'FB_ID'
])
self
.
set_sharing_preferences
(
self
.
users
[
2
],
True
)
# Enroll another user in course_2
self
.
enroll_in_course
(
self
.
users
[
2
],
self
.
course_2
)
self
.
set_facebook_interceptor_for_friends
(
{
'data'
:
[
{
'name'
:
self
.
USERS
[
1
][
'USERNAME'
],
'id'
:
self
.
USERS
[
1
][
'FB_ID'
]},
{
'name'
:
self
.
USERS
[
2
][
'USERNAME'
],
'id'
:
self
.
USERS
[
2
][
'FB_ID'
]},
]}
)
url
=
reverse
(
'courses-with-friends'
)
response
=
self
.
client
.
get
(
url
,
{
'oauth_token'
:
self
.
_FB_USER_ACCESS_TOKEN
})
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
self
.
course
.
id
,
CourseKey
.
from_string
(
response
.
data
[
0
][
'course'
][
'id'
]))
# pylint: disable=E1101
self
.
assertEqual
(
self
.
course_2
.
id
,
CourseKey
.
from_string
(
response
.
data
[
1
][
'course'
][
'id'
]))
# pylint: disable=E1101
# Assert that only two courses are returned
self
.
assertEqual
(
len
(
response
.
data
),
2
)
# pylint: disable=E1101
@httpretty.activate
def
test_two_courses_with_two_friends_on_different_paged_results
(
self
):
self
.
user_create_and_signin
(
1
)
self
.
link_edx_account_to_social
(
self
.
users
[
1
],
self
.
BACKEND
,
self
.
USERS
[
1
][
'FB_ID'
])
self
.
set_sharing_preferences
(
self
.
users
[
1
],
True
)
self
.
enroll_in_course
(
self
.
users
[
1
],
self
.
course
)
self
.
user_create_and_signin
(
2
)
self
.
link_edx_account_to_social
(
self
.
users
[
2
],
self
.
BACKEND
,
self
.
USERS
[
2
][
'FB_ID'
])
self
.
set_sharing_preferences
(
self
.
users
[
2
],
True
)
self
.
course_2
=
CourseFactory
.
create
(
mobile_available
=
True
)
self
.
enroll_in_course
(
self
.
users
[
2
],
self
.
course_2
)
self
.
set_facebook_interceptor_for_friends
(
{
'data'
:
[{
'name'
:
self
.
USERS
[
1
][
'USERNAME'
],
'id'
:
self
.
USERS
[
1
][
'FB_ID'
]}],
"paging"
:
{
"next"
:
"https://graph.facebook.com/v2.2/me/friends/next"
},
"summary"
:
{
"total_count"
:
652
}
}
)
# Set the interceptor for the paged
httpretty
.
register_uri
(
httpretty
.
GET
,
"https://graph.facebook.com/v2.2/me/friends/next"
,
body
=
json
.
dumps
(
{
"data"
:
[{
'name'
:
self
.
USERS
[
2
][
'USERNAME'
],
'id'
:
self
.
USERS
[
2
][
'FB_ID'
]}],
"paging"
:
{
"previous"
:
"https://graph.facebook.com/v2.2/10154805434030300/friends?limit=25&offset=25"
},
"summary"
:
{
"total_count"
:
652
}
}
),
status
=
201
)
url
=
reverse
(
'courses-with-friends'
)
response
=
self
.
client
.
get
(
url
,
{
'oauth_token'
:
self
.
_FB_USER_ACCESS_TOKEN
})
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
self
.
course
.
id
,
CourseKey
.
from_string
(
response
.
data
[
0
][
'course'
][
'id'
]))
# pylint: disable=E1101
self
.
assertEqual
(
self
.
course_2
.
id
,
CourseKey
.
from_string
(
response
.
data
[
1
][
'course'
][
'id'
]))
# pylint: disable=E1101
@httpretty.activate
def
test_no_courses_with_friends_because_sharing_pref_off
(
self
):
self
.
user_create_and_signin
(
1
)
self
.
link_edx_account_to_social
(
self
.
users
[
1
],
self
.
BACKEND
,
self
.
USERS
[
1
][
'FB_ID'
])
self
.
set_sharing_preferences
(
self
.
users
[
1
],
False
)
self
.
set_facebook_interceptor_for_friends
(
{
'data'
:
[{
'name'
:
self
.
USERS
[
1
][
'USERNAME'
],
'id'
:
self
.
USERS
[
1
][
'FB_ID'
]}]}
)
self
.
enroll_in_course
(
self
.
users
[
1
],
self
.
course
)
url
=
reverse
(
'courses-with-friends'
)
response
=
self
.
client
.
get
(
url
,
{
'oauth_token'
:
self
.
_FB_USER_ACCESS_TOKEN
})
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
len
(
response
.
data
),
0
)
@httpretty.activate
def
test_no_courses_with_friends_because_no_auth_token
(
self
):
self
.
user_create_and_signin
(
1
)
self
.
link_edx_account_to_social
(
self
.
users
[
1
],
self
.
BACKEND
,
self
.
USERS
[
1
][
'FB_ID'
])
self
.
set_sharing_preferences
(
self
.
users
[
1
],
False
)
self
.
set_facebook_interceptor_for_friends
(
{
'data'
:
[{
'name'
:
self
.
USERS
[
1
][
'USERNAME'
],
'id'
:
self
.
USERS
[
1
][
'FB_ID'
]}]}
)
self
.
enroll_in_course
(
self
.
users
[
1
],
self
.
course
)
url
=
reverse
(
'courses-with-friends'
)
response
=
self
.
client
.
get
(
url
)
self
.
assertEqual
(
response
.
status_code
,
400
)
lms/djangoapps/mobile_api/social_facebook/courses/urls.py
0 → 100644
View file @
c375f666
"""
URLs for courses API
"""
from
django.conf.urls
import
patterns
,
url
from
.views
import
CoursesWithFriends
urlpatterns
=
patterns
(
'mobile_api.social_facebook.courses.views'
,
url
(
r'^friends$'
,
CoursesWithFriends
.
as_view
(),
name
=
'courses-with-friends'
),
)
lms/djangoapps/mobile_api/social_facebook/courses/views.py
0 → 100644
View file @
c375f666
"""
Views for courses info API
"""
from
rest_framework
import
generics
,
status
from
rest_framework.response
import
Response
from
courseware.access
import
is_mobile_available_for_user
from
student.models
import
CourseEnrollment
from
lms.djangoapps.mobile_api.social_facebook.courses
import
serializers
from
...users.serializers
import
CourseEnrollmentSerializer
from
...utils
import
mobile_view
from
..utils
import
get_friends_from_facebook
,
get_linked_edx_accounts
,
share_with_facebook_friends
@mobile_view
()
class
CoursesWithFriends
(
generics
.
ListAPIView
):
"""
**Use Case**
API endpoint for retrieving all the courses that a user's friends are in.
Note that only friends that allow their courses to be shared will be included.
**Example request**
GET /api/mobile/v0.5/social/facebook/courses/friends
**Response Values**
See UserCourseEnrollmentsList in lms/djangoapps/mobile_api/users for the structure of the response values.
"""
serializer_class
=
serializers
.
CoursesWithFriendsSerializer
def
list
(
self
,
request
,
*
args
,
**
kwargs
):
serializer
=
self
.
get_serializer
(
data
=
request
.
GET
,
files
=
request
.
FILES
)
if
not
serializer
.
is_valid
():
return
Response
(
serializer
.
errors
,
status
=
status
.
HTTP_400_BAD_REQUEST
)
# Get friends from Facebook
result
=
get_friends_from_facebook
(
serializer
)
if
type
(
result
)
!=
list
:
return
result
friends_that_are_edx_users
=
get_linked_edx_accounts
(
result
)
# Filter by sharing preferences
users_with_sharing
=
[
friend
for
friend
in
friends_that_are_edx_users
if
share_with_facebook_friends
(
friend
)
]
# Get unique enrollments
enrollments
=
[]
for
friend
in
users_with_sharing
:
query_set
=
CourseEnrollment
.
objects
.
filter
(
user_id
=
friend
[
'edX_id'
]
)
.
exclude
(
course_id__in
=
[
enrollment
.
course_id
for
enrollment
in
enrollments
])
enrollments
.
extend
(
query_set
)
# Get course objects
courses
=
[
enrollment
for
enrollment
in
enrollments
if
enrollment
.
course
and
is_mobile_available_for_user
(
self
.
request
.
user
,
enrollment
.
course
)
]
return
Response
(
CourseEnrollmentSerializer
(
courses
,
context
=
{
'request'
:
request
})
.
data
)
lms/djangoapps/mobile_api/social_facebook/friends/__init__.py
0 → 100644
View file @
c375f666
"""
Friends API
"""
lms/djangoapps/mobile_api/social_facebook/friends/models.py
0 → 100644
View file @
c375f666
"""
A models.py is required to make this an app (until we move to Django 1.7)
"""
lms/djangoapps/mobile_api/social_facebook/friends/serializers.py
0 → 100644
View file @
c375f666
"""
Serializer for Friends API
"""
from
rest_framework
import
serializers
class
FriendsInCourseSerializer
(
serializers
.
Serializer
):
"""
Serializes oauth token for facebook groups request
"""
oauth_token
=
serializers
.
CharField
(
required
=
True
)
lms/djangoapps/mobile_api/social_facebook/friends/tests.py
0 → 100644
View file @
c375f666
This diff is collapsed.
Click to expand it.
lms/djangoapps/mobile_api/social_facebook/friends/urls.py
0 → 100644
View file @
c375f666
"""
URLs for friends API
"""
from
django.conf.urls
import
patterns
,
url
from
django.conf
import
settings
from
.views
import
FriendsInCourse
urlpatterns
=
patterns
(
'mobile_api.social_facebook.friends.views'
,
url
(
r'^course/{}$'
.
format
(
settings
.
COURSE_ID_PATTERN
),
FriendsInCourse
.
as_view
(),
name
=
'friends-in-course'
),
)
lms/djangoapps/mobile_api/social_facebook/friends/views.py
0 → 100644
View file @
c375f666
"""
Views for friends info API
"""
from
rest_framework
import
generics
,
status
from
rest_framework.response
import
Response
from
opaque_keys.edx.keys
import
CourseKey
from
student.models
import
CourseEnrollment
from
...utils
import
mobile_view
from
..utils
import
get_friends_from_facebook
,
get_linked_edx_accounts
,
share_with_facebook_friends
from
lms.djangoapps.mobile_api.social_facebook.friends
import
serializers
@mobile_view
()
class
FriendsInCourse
(
generics
.
ListAPIView
):
"""
**Use Case**
API endpoint that returns all the users friends that are in the course specified.
Note that only friends that allow their courses to be shared will be included.
**Example request**:
GET /api/mobile/v0.5/social/facebook/friends/course/<course_id>
where course_id is in the form of /edX/DemoX/Demo_Course
**Response Values**
{
"friends": [
{
"name": "test",
"id": "12345",
},
...
]
}
"""
serializer_class
=
serializers
.
FriendsInCourseSerializer
def
list
(
self
,
request
,
*
args
,
**
kwargs
):
serializer
=
self
.
get_serializer
(
data
=
request
.
GET
,
files
=
request
.
FILES
)
if
not
serializer
.
is_valid
():
return
Response
(
serializer
.
errors
,
status
=
status
.
HTTP_400_BAD_REQUEST
)
# Get all the user's FB friends
result
=
get_friends_from_facebook
(
serializer
)
if
type
(
result
)
!=
list
:
return
result
def
is_member
(
friend
,
course_key
):
"""
Return true if friend is a member of the course specified by the course_key
"""
return
CourseEnrollment
.
objects
.
filter
(
course_id
=
course_key
,
user_id
=
friend
[
'edX_id'
]
)
.
count
()
==
1
# For each friend check if they are a linked edX user
friends_with_edx_users
=
get_linked_edx_accounts
(
result
)
# Filter by sharing preferences and enrollment in course
course_key
=
CourseKey
.
from_string
(
kwargs
[
'course_id'
])
friends_with_sharing_in_course
=
[
{
'id'
:
friend
[
'id'
],
'name'
:
friend
[
'name'
]}
for
friend
in
friends_with_edx_users
if
share_with_facebook_friends
(
friend
)
and
is_member
(
friend
,
course_key
)
]
return
Response
({
'friends'
:
friends_with_sharing_in_course
})
lms/djangoapps/mobile_api/social_facebook/groups/__init__.py
0 → 100644
View file @
c375f666
"""
Groups API
"""
lms/djangoapps/mobile_api/social_facebook/groups/models.py
0 → 100644
View file @
c375f666
"""
A models.py is required to make this an app (until we move to Django 1.7)
"""
lms/djangoapps/mobile_api/social_facebook/groups/serializers.py
0 → 100644
View file @
c375f666
"""
Serializer for user API
"""
from
rest_framework
import
serializers
from
django.core.validators
import
RegexValidator
class
GroupSerializer
(
serializers
.
Serializer
):
"""
Serializes facebook groups request
"""
name
=
serializers
.
CharField
(
max_length
=
150
)
description
=
serializers
.
CharField
(
max_length
=
200
,
required
=
False
)
privacy
=
serializers
.
ChoiceField
(
choices
=
[(
"open"
,
"open"
),
(
"closed"
,
"closed"
)],
required
=
False
)
class
GroupsMembersSerializer
(
serializers
.
Serializer
):
"""
Serializes facebook invitations request
"""
member_ids
=
serializers
.
CharField
(
required
=
True
,
validators
=
[
RegexValidator
(
regex
=
r'^([\d]+,?)*$'
,
message
=
'A comma separated list of member ids must be provided'
,
code
=
'member_ids error'
),
]
)
lms/djangoapps/mobile_api/social_facebook/groups/tests.py
0 → 100644
View file @
c375f666
"""
Tests for groups
"""
import
httpretty
from
ddt
import
ddt
,
data
from
django.conf
import
settings
from
django.core.urlresolvers
import
reverse
from
courseware.tests.factories
import
UserFactory
from
..test_utils
import
SocialFacebookTestCase
@ddt
class
TestGroups
(
SocialFacebookTestCase
):
"""
Tests for /api/mobile/v0.5/social/facebook/groups/...
"""
def
setUp
(
self
):
super
(
TestGroups
,
self
)
.
setUp
()
self
.
user
=
UserFactory
.
create
()
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
'test'
)
# Group Creation and Deletion Tests
@httpretty.activate
def
test_create_new_open_group
(
self
):
group_id
=
'12345678'
status_code
=
200
self
.
set_facebook_interceptor_for_access_token
()
self
.
set_facebook_interceptor_for_groups
({
'id'
:
group_id
},
status_code
)
url
=
reverse
(
'create-delete-group'
,
kwargs
=
{
'group_id'
:
''
})
response
=
self
.
client
.
post
(
url
,
{
'name'
:
'TheBestGroup'
,
'description'
:
'The group for the best people'
,
'privacy'
:
'open'
}
)
self
.
assertEqual
(
response
.
status_code
,
status_code
)
self
.
assertTrue
(
'id'
in
response
.
data
)
# pylint: disable=E1103
self
.
assertEqual
(
response
.
data
[
'id'
],
group_id
)
# pylint: disable=E1103
@httpretty.activate
def
test_create_new_closed_group
(
self
):
group_id
=
'12345678'
status_code
=
200
self
.
set_facebook_interceptor_for_access_token
()
self
.
set_facebook_interceptor_for_groups
({
'id'
:
group_id
},
status_code
)
# Create new group
url
=
reverse
(
'create-delete-group'
,
kwargs
=
{
'group_id'
:
''
})
response
=
self
.
client
.
post
(
url
,
{
'name'
:
'TheBestGroup'
,
'description'
:
'The group for the best people'
,
'privacy'
:
'closed'
}
)
self
.
assertEqual
(
response
.
status_code
,
status_code
)
self
.
assertTrue
(
'id'
in
response
.
data
)
# pylint: disable=E1103
self
.
assertEqual
(
response
.
data
[
'id'
],
group_id
)
# pylint: disable=E1103
def
test_create_new_group_no_name
(
self
):
url
=
reverse
(
'create-delete-group'
,
kwargs
=
{
'group_id'
:
''
})
response
=
self
.
client
.
post
(
url
,
{})
self
.
assertEqual
(
response
.
status_code
,
400
)
def
test_create_new_group_with_invalid_name
(
self
):
url
=
reverse
(
'create-delete-group'
,
kwargs
=
{
'group_id'
:
''
})
response
=
self
.
client
.
post
(
url
,
{
'invalid_name'
:
'TheBestGroup'
})
self
.
assertEqual
(
response
.
status_code
,
400
)
def
test_create_new_group_with_invalid_privacy
(
self
):
url
=
reverse
(
'create-delete-group'
,
kwargs
=
{
'group_id'
:
''
})
response
=
self
.
client
.
post
(
url
,
{
'name'
:
'TheBestGroup'
,
'privacy'
:
'half_open_half_closed'
}
)
self
.
assertEqual
(
response
.
status_code
,
400
)
@httpretty.activate
def
test_delete_group_that_exists
(
self
):
# Create new group
group_id
=
'12345678'
status_code
=
200
self
.
set_facebook_interceptor_for_access_token
()
self
.
set_facebook_interceptor_for_groups
({
'id'
:
group_id
},
status_code
)
url
=
reverse
(
'create-delete-group'
,
kwargs
=
{
'group_id'
:
''
})
response
=
self
.
client
.
post
(
url
,
{
'name'
:
'TheBestGroup'
,
'description'
:
'The group for the best people'
,
'privacy'
:
'open'
}
)
self
.
assertEqual
(
response
.
status_code
,
status_code
)
self
.
assertTrue
(
'id'
in
response
.
data
)
# pylint: disable=E1103
# delete group
httpretty
.
register_uri
(
httpretty
.
POST
,
'https://graph.facebook.com/{}/{}/groups/{}?access_token=FakeToken&method=delete'
.
format
(
settings
.
FACEBOOK_API_VERSION
,
settings
.
FACEBOOK_APP_ID
,
group_id
),
body
=
'{"success": "true"}'
,
status
=
status_code
)
response
=
self
.
delete_group
(
response
.
data
[
'id'
])
# pylint: disable=E1101
self
.
assertTrue
(
response
.
status_code
,
status_code
)
# pylint: disable=E1101
@httpretty.activate
def
test_delete
(
self
):
group_id
=
'12345678'
status_code
=
400
httpretty
.
register_uri
(
httpretty
.
GET
,
'https://graph.facebook.com/oauth/access_token?client_secret={}&grant_type=client_credentials&client_id={}'
.
format
(
settings
.
FACEBOOK_APP_SECRET
,
settings
.
FACEBOOK_APP_ID
),
body
=
'FakeToken=FakeToken'
,
status
=
200
)
httpretty
.
register_uri
(
httpretty
.
POST
,
'https://graph.facebook.com/{}/{}/groups/{}?access_token=FakeToken&method=delete'
.
format
(
settings
.
FACEBOOK_API_VERSION
,
settings
.
FACEBOOK_APP_ID
,
group_id
),
body
=
'{"error": {"message": "error message"}}'
,
status
=
status_code
)
response
=
self
.
delete_group
(
group_id
)
self
.
assertTrue
(
response
.
status_code
,
status_code
)
# Member addition and Removal tests
@data
(
'1234,,,,5678,,'
,
'this00is00not00a00valid00id'
,
'1234,abc,5678'
,
''
)
def
test_invite_single_member_malformed_member_id
(
self
,
member_id
):
group_id
=
'111111111111111'
response
=
self
.
invite_to_group
(
group_id
,
member_id
)
self
.
assertEqual
(
response
.
status_code
,
400
)
@httpretty.activate
def
test_invite_single_member
(
self
):
group_id
=
'111111111111111'
member_id
=
'44444444444444444'
status_code
=
200
self
.
set_facebook_interceptor_for_access_token
()
self
.
set_facebook_interceptor_for_members
({
'success'
:
'True'
},
status_code
,
group_id
,
member_id
)
response
=
self
.
invite_to_group
(
group_id
,
member_id
)
self
.
assertEqual
(
response
.
status_code
,
status_code
)
self
.
assertTrue
(
'success'
in
response
.
data
[
member_id
])
# pylint: disable=E1103
@httpretty.activate
def
test_invite_multiple_members_successfully
(
self
):
member_ids
=
'222222222222222,333333333333333,44444444444444444'
group_id
=
'111111111111111'
status_code
=
200
self
.
set_facebook_interceptor_for_access_token
()
for
member_id
in
member_ids
.
split
(
','
):
self
.
set_facebook_interceptor_for_members
({
'success'
:
'True'
},
status_code
,
group_id
,
member_id
)
response
=
self
.
invite_to_group
(
group_id
,
member_ids
)
self
.
assertEqual
(
response
.
status_code
,
status_code
)
for
member_id
in
member_ids
.
split
(
','
):
self
.
assertTrue
(
'success'
in
response
.
data
[
member_id
])
# pylint: disable=E1103
@httpretty.activate
def
test_invite_single_member_unsuccessfully
(
self
):
group_id
=
'111111111111111'
member_id
=
'44444444444444444'
status_code
=
400
self
.
set_facebook_interceptor_for_access_token
()
self
.
set_facebook_interceptor_for_members
(
{
'error'
:
{
'message'
:
'error message'
}},
status_code
,
group_id
,
member_id
)
response
=
self
.
invite_to_group
(
group_id
,
member_id
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertTrue
(
'error message'
in
response
.
data
[
member_id
])
# pylint: disable=E1103
@httpretty.activate
def
test_invite_multiple_members_unsuccessfully
(
self
):
member_ids
=
'222222222222222,333333333333333,44444444444444444'
group_id
=
'111111111111111'
status_code
=
400
self
.
set_facebook_interceptor_for_access_token
()
for
member_id
in
member_ids
.
split
(
','
):
self
.
set_facebook_interceptor_for_members
(
{
'error'
:
{
'message'
:
'error message'
}},
status_code
,
group_id
,
member_id
)
response
=
self
.
invite_to_group
(
group_id
,
member_ids
)
self
.
assertEqual
(
response
.
status_code
,
200
)
for
member_id
in
member_ids
.
split
(
','
):
self
.
assertTrue
(
'error message'
in
response
.
data
[
member_id
])
# pylint: disable=E1103
lms/djangoapps/mobile_api/social_facebook/groups/urls.py
0 → 100644
View file @
c375f666
"""
URLs for groups API
"""
from
django.conf.urls
import
patterns
,
url
from
.views
import
Groups
,
GroupsMembers
urlpatterns
=
patterns
(
'mobile_api.social_facebook.groups.views'
,
url
(
r'^(?P<group_id>[\d]*)$'
,
Groups
.
as_view
(),
name
=
'create-delete-group'
),
url
(
r'^(?P<group_id>[\d]+)/member/(?P<member_id>[\d]*,*)$'
,
GroupsMembers
.
as_view
(),
name
=
'add-remove-member'
)
)
lms/djangoapps/mobile_api/social_facebook/groups/views.py
0 → 100644
View file @
c375f666
"""
Views for groups info API
"""
from
rest_framework
import
generics
,
status
,
mixins
from
rest_framework.response
import
Response
from
django.conf
import
settings
import
facebook
from
...utils
import
mobile_view
from
.
import
serializers
@mobile_view
()
class
Groups
(
generics
.
CreateAPIView
,
mixins
.
DestroyModelMixin
):
"""
**Use Case**
An API to Create or Delete course groups.
Note: The Delete is not invoked from the current version of the app
and is used only for testing with facebook dependencies.
**Creation Example request**:
POST /api/mobile/v0.5/social/facebook/groups/
Parameters: name : string,
description : string,
privacy : open/closed
**Creation Response Values**
{"id": group_id}
**Deletion Example request**:
DELETE /api/mobile/v0.5/social/facebook/groups/<group_id>
**Deletion Response Values**
{"success" : "true"}
"""
serializer_class
=
serializers
.
GroupSerializer
def
create
(
self
,
request
,
*
args
,
**
kwargs
):
serializer
=
self
.
get_serializer
(
data
=
request
.
DATA
,
files
=
request
.
FILES
)
if
not
serializer
.
is_valid
():
return
Response
(
serializer
.
errors
,
status
=
status
.
HTTP_400_BAD_REQUEST
)
try
:
app_groups_response
=
facebook_graph_api
()
.
request
(
settings
.
FACEBOOK_API_VERSION
+
'/'
+
settings
.
FACEBOOK_APP_ID
+
"/groups"
,
post_args
=
request
.
POST
.
dict
()
)
return
Response
(
app_groups_response
)
except
facebook
.
GraphAPIError
,
ex
:
return
Response
({
'error'
:
ex
.
result
[
'error'
][
'message'
]},
status
=
status
.
HTTP_400_BAD_REQUEST
)
def
delete
(
self
,
request
,
*
args
,
**
kwargs
):
# pylint: disable=unused-argument
"""
Deletes the course group.
"""
try
:
return
Response
(
facebook_graph_api
()
.
request
(
settings
.
FACEBOOK_API_VERSION
+
'/'
+
settings
.
FACEBOOK_APP_ID
+
"/groups/"
+
kwargs
[
'group_id'
],
post_args
=
{
'method'
:
'delete'
}
)
)
except
facebook
.
GraphAPIError
,
ex
:
return
Response
({
'error'
:
ex
.
result
[
'error'
][
'message'
]},
status
=
status
.
HTTP_400_BAD_REQUEST
)
@mobile_view
()
class
GroupsMembers
(
generics
.
CreateAPIView
,
mixins
.
DestroyModelMixin
):
"""
**Use Case**
An API to Invite and Remove members to a group
Note: The Remove is not invoked from the current version
of the app and is used only for testing with facebook dependencies.
**Invite Example request**:
POST /api/mobile/v0.5/social/facebook/groups/<group_id>/member/
Parameters: members : int,int,int...
**Invite Response Values**
{"member_id" : success/error_message}
A response with each member_id and whether or not the member was added successfully.
If the member was not added successfully the Facebook error message is provided.
**Remove Example request**:
DELETE /api/mobile/v0.5/social/facebook/groups/<group_id>/member/<member_id>
**Remove Response Values**
{"success" : "true"}
"""
serializer_class
=
serializers
.
GroupsMembersSerializer
def
create
(
self
,
request
,
*
args
,
**
kwargs
):
serializer
=
self
.
get_serializer
(
data
=
request
.
DATA
,
files
=
request
.
FILES
)
if
not
serializer
.
is_valid
():
return
Response
(
serializer
.
errors
,
status
=
status
.
HTTP_400_BAD_REQUEST
)
graph
=
facebook_graph_api
()
url
=
settings
.
FACEBOOK_API_VERSION
+
'/'
+
kwargs
[
'group_id'
]
+
"/members"
member_ids
=
serializer
.
object
[
'member_ids'
]
.
split
(
','
)
response
=
{}
for
member_id
in
member_ids
:
try
:
if
'success'
in
graph
.
request
(
url
,
post_args
=
{
'member'
:
member_id
}):
response
[
member_id
]
=
'success'
except
facebook
.
GraphAPIError
,
ex
:
response
[
member_id
]
=
ex
.
result
[
'error'
][
'message'
]
return
Response
(
response
,
status
=
status
.
HTTP_200_OK
)
def
delete
(
self
,
request
,
*
args
,
**
kwargs
):
# pylint: disable=unused-argument
"""
Deletes the member from the course group.
"""
try
:
return
Response
(
facebook_graph_api
()
.
request
(
settings
.
FACEBOOK_API_VERSION
+
'/'
+
kwargs
[
'group_id'
]
+
"/members"
,
post_args
=
{
'method'
:
'delete'
,
'member'
:
kwargs
[
'member_id'
]}
)
)
except
facebook
.
GraphAPIError
,
ex
:
return
Response
({
'error'
:
ex
.
result
[
'error'
][
'message'
]},
status
=
status
.
HTTP_400_BAD_REQUEST
)
def
facebook_graph_api
():
"""
Returns the result from calling Facebook's Graph API with the app's access token.
"""
return
facebook
.
GraphAPI
(
facebook
.
get_app_access_token
(
settings
.
FACEBOOK_APP_ID
,
settings
.
FACEBOOK_APP_SECRET
))
lms/djangoapps/mobile_api/social_facebook/models.py
0 → 100644
View file @
c375f666
"""
A models.py is required to make this an app (until we move to Django 1.7)
"""
lms/djangoapps/mobile_api/social_facebook/preferences/__init__.py
0 → 100644
View file @
c375f666
"""
Users Sharing preferences API
"""
lms/djangoapps/mobile_api/social_facebook/preferences/models.py
0 → 100644
View file @
c375f666
"""
A models.py is required to make this an app (until we move to Django 1.7)
"""
lms/djangoapps/mobile_api/social_facebook/preferences/serializers.py
0 → 100644
View file @
c375f666
"""
Serializer for Share Settings API
"""
from
rest_framework
import
serializers
class
UserSharingSerializar
(
serializers
.
Serializer
):
"""
Serializes user social settings
"""
share_with_facebook_friends
=
serializers
.
BooleanField
(
required
=
True
,
default
=
False
)
lms/djangoapps/mobile_api/social_facebook/preferences/tests.py
0 → 100644
View file @
c375f666
# pylint: disable=no-member
"""
Tests for users sharing preferences
"""
from
django.core.urlresolvers
import
reverse
from
..test_utils
import
SocialFacebookTestCase
class
StudentProfileViewTest
(
SocialFacebookTestCase
):
""" Tests for the student profile views. """
USERNAME
=
u'bnotions'
PASSWORD
=
u'horse'
EMAIL
=
u'horse@bnotions.com'
FULL_NAME
=
u'bnotions horse'
def
setUp
(
self
):
super
(
StudentProfileViewTest
,
self
)
.
setUp
()
self
.
user_create_and_signin
(
1
)
def
assert_shared_value
(
self
,
response
,
expected_value
=
'True'
):
"""
Tests whether the response is successful and whether the
share_with_facebook_friends value is set to the expected value.
"""
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertTrue
(
'share_with_facebook_friends'
in
response
.
data
)
self
.
assertTrue
(
expected_value
in
response
.
data
[
'share_with_facebook_friends'
])
def
test_set_preferences_to_true
(
self
):
url
=
reverse
(
'preferences'
)
response
=
self
.
client
.
post
(
url
,
{
'share_with_facebook_friends'
:
'True'
})
self
.
assert_shared_value
(
response
)
def
test_set_preferences_to_false
(
self
):
url
=
reverse
(
'preferences'
)
response
=
self
.
client
.
post
(
url
,
{
'share_with_facebook_friends'
:
'False'
})
self
.
assert_shared_value
(
response
,
'False'
)
def
test_set_preferences_no_parameters
(
self
):
# Note that if no value is given it will default to False
url
=
reverse
(
'preferences'
)
response
=
self
.
client
.
post
(
url
,
{})
self
.
assert_shared_value
(
response
,
'False'
)
def
test_set_preferences_invalid_parameters
(
self
):
# Note that if no value is given it will default to False
# also in the case of invalid parameters
url
=
reverse
(
'preferences'
)
response
=
self
.
client
.
post
(
url
,
{
'bad_param'
:
'False'
})
self
.
assert_shared_value
(
response
,
'False'
)
def
test_get_preferences_after_setting_them
(
self
):
url
=
reverse
(
'preferences'
)
for
boolean
in
[
'True'
,
'False'
]:
# Set the preference
response
=
self
.
client
.
post
(
url
,
{
'share_with_facebook_friends'
:
boolean
})
self
.
assert_shared_value
(
response
,
boolean
)
# Get the preference
response
=
self
.
client
.
get
(
url
)
self
.
assert_shared_value
(
response
,
boolean
)
def
test_get_preferences_without_setting_them
(
self
):
url
=
reverse
(
'preferences'
)
# Get the preference
response
=
self
.
client
.
get
(
url
)
self
.
assert_shared_value
(
response
,
'False'
)
lms/djangoapps/mobile_api/social_facebook/preferences/urls.py
0 → 100644
View file @
c375f666
"""
URLs for users sharing preferences
"""
from
django.conf.urls
import
patterns
,
url
from
.views
import
UserSharing
urlpatterns
=
patterns
(
'mobile_api.social_facebook.preferences.views'
,
url
(
r'^preferences/$'
,
UserSharing
.
as_view
(),
name
=
'preferences'
),
)
lms/djangoapps/mobile_api/social_facebook/preferences/views.py
0 → 100644
View file @
c375f666
"""
Views for users sharing preferences
"""
from
rest_framework
import
generics
,
status
from
rest_framework.response
import
Response
from
openedx.core.djangoapps.user_api.api.profile
import
preference_info
,
update_preferences
from
...utils
import
mobile_view
from
.
import
serializers
@mobile_view
()
class
UserSharing
(
generics
.
ListCreateAPIView
):
"""
**Use Case**
An API to retrieve or update the users social sharing settings
**GET Example request**:
GET /api/mobile/v0.5/settings/preferences/
**GET Response Values**
{'share_with_facebook_friends': 'True'}
**POST Example request**:
POST /api/mobile/v0.5/settings/preferences/
paramters: share_with_facebook_friends : True
**POST Response Values**
{'share_with_facebook_friends': 'True'}
"""
serializer_class
=
serializers
.
UserSharingSerializar
def
create
(
self
,
request
,
*
args
,
**
kwargs
):
serializer
=
self
.
get_serializer
(
data
=
request
.
DATA
,
files
=
request
.
FILES
)
if
serializer
.
is_valid
():
value
=
serializer
.
object
[
'share_with_facebook_friends'
]
update_preferences
(
request
.
user
.
username
,
share_with_facebook_friends
=
value
)
return
self
.
get
(
request
,
*
args
,
**
kwargs
)
return
Response
(
serializer
.
errors
,
status
=
status
.
HTTP_400_BAD_REQUEST
)
def
get
(
self
,
request
,
*
args
,
**
kwargs
):
preferences
=
preference_info
(
request
.
user
.
username
)
response
=
{
'share_with_facebook_friends'
:
preferences
.
get
(
'share_with_facebook_friends'
,
'False'
)}
return
Response
(
response
)
lms/djangoapps/mobile_api/social_facebook/test_utils.py
0 → 100644
View file @
c375f666
"""
Test utils for Facebook functionality
"""
import
httpretty
import
json
from
rest_framework.test
import
APITestCase
from
django.conf
import
settings
from
django.core.urlresolvers
import
reverse
from
social.apps.django_app.default.models
import
UserSocialAuth
from
student.models
import
CourseEnrollment
from
student.views
import
login_oauth_token
from
openedx.core.djangoapps.user_api.api.profile
import
preference_info
,
update_preferences
from
xmodule.modulestore.tests.django_utils
import
ModuleStoreTestCase
from
courseware.tests.factories
import
UserFactory
class
SocialFacebookTestCase
(
ModuleStoreTestCase
,
APITestCase
):
"""
Base Class for social test cases
"""
USERS
=
{
1
:
{
'USERNAME'
:
"TestUser One"
,
'EMAIL'
:
"test_one@ebnotions.com"
,
'PASSWORD'
:
"edx"
,
'FB_ID'
:
"11111111111111111"
},
2
:
{
'USERNAME'
:
"TestUser Two"
,
'EMAIL'
:
"test_two@ebnotions.com"
,
'PASSWORD'
:
"edx"
,
'FB_ID'
:
"22222222222222222"
},
3
:
{
'USERNAME'
:
"TestUser Three"
,
'EMAIL'
:
"test_three@ebnotions.com"
,
'PASSWORD'
:
"edx"
,
'FB_ID'
:
"33333333333333333"
}
}
BACKEND
=
"facebook"
USER_URL
=
"https://graph.facebook.com/me"
UID_FIELD
=
"id"
_FB_USER_ACCESS_TOKEN
=
'ThisIsAFakeFacebookToken'
users
=
{}
def
setUp
(
self
):
super
(
SocialFacebookTestCase
,
self
)
.
setUp
()
def
set_facebook_interceptor_for_access_token
(
self
):
"""
Facebook interceptor for groups access_token
"""
httpretty
.
register_uri
(
httpretty
.
GET
,
'https://graph.facebook.com/oauth/access_token?client_secret='
+
settings
.
FACEBOOK_APP_SECRET
+
'&grant_type=client_credentials&client_id='
+
settings
.
FACEBOOK_APP_ID
,
body
=
'FakeToken=FakeToken'
,
status
=
200
)
def
set_facebook_interceptor_for_groups
(
self
,
data
,
status
):
"""
Facebook interceptor for groups test
"""
httpretty
.
register_uri
(
httpretty
.
POST
,
'https://graph.facebook.com/'
+
settings
.
FACEBOOK_API_VERSION
+
'/'
+
settings
.
FACEBOOK_APP_ID
+
'/groups'
,
body
=
json
.
dumps
(
data
),
status
=
status
)
def
set_facebook_interceptor_for_members
(
self
,
data
,
status
,
group_id
,
member_id
):
"""
Facebook interceptor for group members tests
"""
httpretty
.
register_uri
(
httpretty
.
POST
,
'https://graph.facebook.com/'
+
settings
.
FACEBOOK_API_VERSION
+
'/'
+
group_id
+
'/members?member='
+
member_id
+
'&access_token=FakeToken'
,
body
=
json
.
dumps
(
data
),
status
=
status
)
def
set_facebook_interceptor_for_friends
(
self
,
data
):
"""
Facebook interceptor for friends tests
"""
httpretty
.
register_uri
(
httpretty
.
GET
,
"https://graph.facebook.com/v2.2/me/friends"
,
body
=
json
.
dumps
(
data
),
status
=
201
)
def
delete_group
(
self
,
group_id
):
"""
Invoke the delete groups view
"""
url
=
reverse
(
'create-delete-group'
,
kwargs
=
{
'group_id'
:
group_id
})
response
=
self
.
client
.
delete
(
url
)
return
response
def
invite_to_group
(
self
,
group_id
,
member_ids
):
"""
Invoke the invite to group view
"""
url
=
reverse
(
'add-remove-member'
,
kwargs
=
{
'group_id'
:
group_id
,
'member_id'
:
''
})
return
self
.
client
.
post
(
url
,
{
'member_ids'
:
member_ids
})
def
remove_from_group
(
self
,
group_id
,
member_id
):
"""
Invoke the remove from group view
"""
url
=
reverse
(
'add-remove-member'
,
kwargs
=
{
'group_id'
:
group_id
,
'member_id'
:
member_id
})
response
=
self
.
client
.
delete
(
url
)
self
.
assertEqual
(
response
.
status_code
,
200
)
def
link_edx_account_to_social
(
self
,
user
,
backend
,
social_uid
):
"""
Register the user to the social auth backend
"""
reverse
(
login_oauth_token
,
kwargs
=
{
"backend"
:
backend
})
UserSocialAuth
.
objects
.
create
(
user
=
user
,
provider
=
backend
,
uid
=
social_uid
)
def
set_sharing_preferences
(
self
,
user
,
boolean_value
):
"""
Sets self.user's share settings to boolean_value
"""
update_preferences
(
user
.
username
,
share_with_facebook_friends
=
boolean_value
)
self
.
assertEqual
(
preference_info
(
user
.
username
)[
'share_with_facebook_friends'
],
unicode
(
boolean_value
))
def
_change_enrollment
(
self
,
action
,
course_id
=
None
,
email_opt_in
=
None
):
"""
Change the student's enrollment status in a course.
Args:
action (string): The action to perform (either "enroll" or "unenroll")
Keyword Args:
course_id (unicode): If provided, use this course ID. Otherwise, use the
course ID created in the setup for this test.
email_opt_in (unicode): If provided, pass this value along as
an additional GET parameter.
"""
if
course_id
is
None
:
course_id
=
unicode
(
self
.
course
.
id
)
params
=
{
'enrollment_action'
:
action
,
'course_id'
:
course_id
}
if
email_opt_in
:
params
[
'email_opt_in'
]
=
email_opt_in
return
self
.
client
.
post
(
reverse
(
'change_enrollment'
),
params
)
def
user_create_and_signin
(
self
,
user_number
):
"""
Create a user and sign them in
"""
self
.
users
[
user_number
]
=
UserFactory
.
create
(
username
=
self
.
USERS
[
user_number
][
'USERNAME'
],
email
=
self
.
USERS
[
user_number
][
'EMAIL'
],
password
=
self
.
USERS
[
user_number
][
'PASSWORD'
]
)
self
.
client
.
login
(
username
=
self
.
USERS
[
user_number
][
'USERNAME'
],
password
=
self
.
USERS
[
user_number
][
'PASSWORD'
])
def
enroll_in_course
(
self
,
user
,
course
):
"""
Enroll a user in the course
"""
resp
=
self
.
_change_enrollment
(
'enroll'
,
course_id
=
course
.
id
)
self
.
assertEqual
(
resp
.
status_code
,
200
)
self
.
assertTrue
(
CourseEnrollment
.
is_enrolled
(
user
,
course
.
id
))
course_mode
,
is_active
=
CourseEnrollment
.
enrollment_mode_for_user
(
user
,
course
.
id
)
self
.
assertTrue
(
is_active
)
self
.
assertEqual
(
course_mode
,
'honor'
)
lms/djangoapps/mobile_api/social_facebook/urls.py
0 → 100644
View file @
c375f666
"""
URLs for Social Facebook
"""
from
django.conf.urls
import
patterns
,
url
,
include
urlpatterns
=
patterns
(
''
,
url
(
r'^courses/'
,
include
(
'mobile_api.social_facebook.courses.urls'
)),
url
(
r'^friends/'
,
include
(
'mobile_api.social_facebook.friends.urls'
)),
url
(
r'^groups/'
,
include
(
'mobile_api.social_facebook.groups.urls'
)),
)
lms/djangoapps/mobile_api/social_facebook/utils.py
0 → 100644
View file @
c375f666
"""
Common utility methods and decorators for Social Facebook APIs.
"""
import
json
import
urllib2
import
facebook
from
django.conf
import
settings
from
rest_framework
import
status
from
rest_framework.response
import
Response
from
social.apps.django_app.default.models
import
UserSocialAuth
from
openedx.core.djangoapps.user_api.api.profile
import
preference_info
# TODO
# The pagination strategy needs to be further flushed out.
# What is the default page size for the facebook Graph API? 25? Is the page size a parameter that can be tweaked?
# If a user has a large number of friends, we would be calling the FB API num_friends/page_size times.
#
# However, on the app, we don't plan to display all those friends anyway.
# If we do, for scalability, the endpoints themselves would need to be paginated.
def
get_pagination
(
friends
):
"""
Get paginated data from FaceBook response
"""
data
=
friends
[
'data'
]
while
'paging'
in
friends
and
'next'
in
friends
[
'paging'
]:
response
=
urllib2
.
urlopen
(
friends
[
'paging'
][
'next'
])
friends
=
json
.
loads
(
response
.
read
())
data
=
data
+
friends
[
'data'
]
return
data
def
get_friends_from_facebook
(
serializer
):
"""
Return a list with the result of a facebook /me/friends call
using the oauth_token contained within the serializer object.
If facebook returns an error, return a response object containing
the error message.
"""
try
:
graph
=
facebook
.
GraphAPI
(
serializer
.
object
[
'oauth_token'
])
friends
=
graph
.
request
(
settings
.
FACEBOOK_API_VERSION
+
"/me/friends"
)
return
get_pagination
(
friends
)
except
facebook
.
GraphAPIError
,
ex
:
return
Response
({
'error'
:
ex
.
result
[
'error'
][
'message'
]},
status
=
status
.
HTTP_400_BAD_REQUEST
)
def
get_linked_edx_accounts
(
data
):
"""
Return a list of friends from the input that are edx users with the
additional attributes of edX_id and edX_username
"""
friends_that_are_edx_users
=
[]
for
friend
in
data
:
query_set
=
UserSocialAuth
.
objects
.
filter
(
uid
=
unicode
(
friend
[
'id'
]))
if
query_set
.
count
()
==
1
:
friend
[
'edX_id'
]
=
query_set
[
0
]
.
user_id
friend
[
'edX_username'
]
=
query_set
[
0
]
.
user
.
username
friends_that_are_edx_users
.
append
(
friend
)
return
friends_that_are_edx_users
def
share_with_facebook_friends
(
friend
):
"""
Return true if the user's share_with_facebook_friends preference is set to true.
"""
share_fb_friends_settings
=
preference_info
(
friend
[
'edX_username'
])
return
share_fb_friends_settings
.
get
(
'share_with_facebook_friends'
,
None
)
==
'True'
lms/djangoapps/mobile_api/urls.py
View file @
c375f666
"""
URLs for mobile API
"""
from
django.conf
import
settings
from
django.conf.urls
import
patterns
,
url
,
include
from
.users.views
import
my_user_info
# Additionally, we include login URLs for the browseable API.
urlpatterns
=
patterns
(
''
,
url
(
r'^users/'
,
include
(
'mobile_api.users.urls'
)),
...
...
@@ -13,3 +13,9 @@ urlpatterns = patterns(
url
(
r'^video_outlines/'
,
include
(
'mobile_api.video_outlines.urls'
)),
url
(
r'^course_info/'
,
include
(
'mobile_api.course_info.urls'
)),
)
if
settings
.
FEATURES
[
"ENABLE_MOBILE_SOCIAL_FACEBOOK_FEATURES"
]:
urlpatterns
+=
(
url
(
r'^social/facebook/'
,
include
(
'mobile_api.social_facebook.urls'
)),
url
(
r'^settings/'
,
include
(
'mobile_api.social_facebook.preferences.urls'
)),
)
lms/envs/aws.py
View file @
c375f666
...
...
@@ -517,3 +517,8 @@ PDF_RECEIPT_COBRAND_LOGO_HEIGHT_MM = ENV_TOKENS.get(
if
FEATURES
.
get
(
'ENABLE_COURSEWARE_SEARCH'
):
# Use ElasticSearch as the search engine herein
SEARCH_ENGINE
=
"search.elastic.ElasticSearchEngine"
# Facebook app
FACEBOOK_API_VERSION
=
AUTH_TOKENS
.
get
(
"FACEBOOK_API_VERSION"
)
FACEBOOK_APP_SECRET
=
AUTH_TOKENS
.
get
(
"FACEBOOK_APP_SECRET"
)
FACEBOOK_APP_ID
=
AUTH_TOKENS
.
get
(
"FACEBOOK_APP_ID"
)
lms/envs/common.py
View file @
c375f666
...
...
@@ -312,6 +312,7 @@ FEATURES = {
# Expose Mobile REST API. Note that if you use this, you must also set
# ENABLE_OAUTH2_PROVIDER to True
'ENABLE_MOBILE_REST_API'
:
False
,
'ENABLE_MOBILE_SOCIAL_FACEBOOK_FEATURES'
:
False
,
# Enable the combined login/registration form
'ENABLE_COMBINED_LOGIN_REGISTRATION'
:
False
,
...
...
lms/envs/test.py
View file @
c375f666
...
...
@@ -263,6 +263,7 @@ FEATURES['ENABLE_OAUTH2_PROVIDER'] = True
########################### External REST APIs #################################
FEATURES
[
'ENABLE_MOBILE_REST_API'
]
=
True
FEATURES
[
'ENABLE_MOBILE_SOCIAL_FACEBOOK_FEATURES'
]
=
True
FEATURES
[
'ENABLE_VIDEO_ABSTRACTION_LAYER_API'
]
=
True
###################### Payment ##############################3
...
...
@@ -460,3 +461,7 @@ FEATURES['ENTRANCE_EXAMS'] = True
FEATURES
[
'ENABLE_COURSEWARE_SEARCH'
]
=
True
# Use MockSearchEngine as the search engine for test scenario
SEARCH_ENGINE
=
"search.tests.mock_search_engine.MockSearchEngine"
FACEBOOK_APP_SECRET
=
"Test"
FACEBOOK_APP_ID
=
"Test"
FACEBOOK_API_VERSION
=
"v2.2"
requirements/edx/base.txt
View file @
c375f666
...
...
@@ -36,6 +36,7 @@ django-method-override==0.1.0
djangorestframework==2.3.14
django==1.4.18
elasticsearch==0.4.5
facebook-sdk==0.4.0
feedparser==5.1.3
firebase-token-generator==1.3.2
# Master pyfs has a bug working with VPC auth. This is a fix. We should switch
...
...
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