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
716c1f74
Commit
716c1f74
authored
Aug 12, 2015
by
Bill DeRusha
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #9237 from edx/bderusha/teams-expand-users-api-fix
Team API include correct info when expanding users
parents
7734674c
09acca2d
Hide whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
364 additions
and
128 deletions
+364
-128
lms/djangoapps/student_profile/test/test_views.py
+2
-1
lms/djangoapps/student_profile/views.py
+4
-7
lms/djangoapps/teams/__init__.py
+12
-0
lms/djangoapps/teams/plugins.py
+1
-2
lms/djangoapps/teams/serializers.py
+13
-4
lms/djangoapps/teams/tests/test_views.py
+106
-18
lms/djangoapps/teams/views.py
+4
-18
lms/djangoapps/verify_student/tests/test_views.py
+3
-1
lms/envs/common.py
+19
-0
openedx/core/djangoapps/user_api/accounts/api.py
+31
-48
openedx/core/djangoapps/user_api/accounts/image_helpers.py
+9
-3
openedx/core/djangoapps/user_api/accounts/serializers.py
+118
-3
openedx/core/djangoapps/user_api/accounts/tests/test_api.py
+31
-15
openedx/core/djangoapps/user_api/accounts/tests/test_views.py
+2
-1
openedx/core/djangoapps/user_api/accounts/views.py
+1
-5
openedx/core/djangoapps/user_api/tests/test_views.py
+8
-2
No files found.
lms/djangoapps/student_profile/test/test_views.py
View file @
716c1f74
...
@@ -40,8 +40,9 @@ class LearnerProfileViewTest(UrlResetMixin, TestCase):
...
@@ -40,8 +40,9 @@ class LearnerProfileViewTest(UrlResetMixin, TestCase):
Verify learner profile page context data.
Verify learner profile page context data.
"""
"""
request
=
RequestFactory
()
.
get
(
'/url'
)
request
=
RequestFactory
()
.
get
(
'/url'
)
request
.
user
=
self
.
user
context
=
learner_profile_context
(
self
.
user
,
self
.
USERNAME
,
self
.
user
.
is_staff
,
request
.
build_absolute_uri
)
context
=
learner_profile_context
(
request
,
self
.
USERNAME
,
self
.
user
.
is_staff
)
self
.
assertEqual
(
self
.
assertEqual
(
context
[
'data'
][
'default_public_account_fields'
],
context
[
'data'
][
'default_public_account_fields'
],
...
...
lms/djangoapps/student_profile/views.py
View file @
716c1f74
...
@@ -40,13 +40,13 @@ def learner_profile(request, username):
...
@@ -40,13 +40,13 @@ def learner_profile(request, username):
try
:
try
:
return
render_to_response
(
return
render_to_response
(
'student_profile/learner_profile.html'
,
'student_profile/learner_profile.html'
,
learner_profile_context
(
request
.
user
,
username
,
request
.
user
.
is_staff
,
request
.
build_absolute_uri
)
learner_profile_context
(
request
,
username
,
request
.
user
.
is_staff
)
)
)
except
(
UserNotAuthorized
,
UserNotFound
,
ObjectDoesNotExist
):
except
(
UserNotAuthorized
,
UserNotFound
,
ObjectDoesNotExist
):
raise
Http404
raise
Http404
def
learner_profile_context
(
logged_in_user
,
profile_username
,
user_is_staff
,
build_absolute_uri_func
):
def
learner_profile_context
(
request
,
profile_username
,
user_is_staff
):
"""Context for the learner profile page.
"""Context for the learner profile page.
Args:
Args:
...
@@ -62,14 +62,11 @@ def learner_profile_context(logged_in_user, profile_username, user_is_staff, bui
...
@@ -62,14 +62,11 @@ def learner_profile_context(logged_in_user, profile_username, user_is_staff, bui
ObjectDoesNotExist: the specified profile_username does not exist.
ObjectDoesNotExist: the specified profile_username does not exist.
"""
"""
profile_user
=
User
.
objects
.
get
(
username
=
profile_username
)
profile_user
=
User
.
objects
.
get
(
username
=
profile_username
)
logged_in_user
=
request
.
user
own_profile
=
(
logged_in_user
.
username
==
profile_username
)
own_profile
=
(
logged_in_user
.
username
==
profile_username
)
account_settings_data
=
get_account_settings
(
logged_in_user
,
profile_username
)
account_settings_data
=
get_account_settings
(
request
,
profile_username
)
# Account for possibly relative URLs.
for
key
,
value
in
account_settings_data
[
'profile_image'
]
.
items
():
if
key
.
startswith
(
PROFILE_IMAGE_KEY_PREFIX
):
account_settings_data
[
'profile_image'
][
key
]
=
build_absolute_uri_func
(
value
)
preferences_data
=
get_user_preferences
(
profile_user
,
profile_username
)
preferences_data
=
get_user_preferences
(
profile_user
,
profile_username
)
...
...
lms/djangoapps/teams/__init__.py
View file @
716c1f74
"""
Defines common methods shared by Teams classes
"""
from
django.conf
import
settings
def
is_feature_enabled
(
course
):
"""
Returns True if the teams feature is enabled.
"""
return
settings
.
FEATURES
.
get
(
'ENABLE_TEAMS'
,
False
)
and
course
.
teams_enabled
lms/djangoapps/teams/plugins.py
View file @
716c1f74
"""
"""
Definition of the course team feature.
Definition of the course team feature.
"""
"""
from
django.utils.translation
import
ugettext_noop
from
django.utils.translation
import
ugettext_noop
from
courseware.tabs
import
EnrolledTab
from
courseware.tabs
import
EnrolledTab
from
.view
s
import
is_feature_enabled
from
team
s
import
is_feature_enabled
class
TeamsTab
(
EnrolledTab
):
class
TeamsTab
(
EnrolledTab
):
...
...
lms/djangoapps/teams/serializers.py
View file @
716c1f74
"""Defines serializers used by the Team API."""
"""Defines serializers used by the Team API."""
from
copy
import
deepcopy
from
django.contrib.auth.models
import
User
from
django.contrib.auth.models
import
User
from
django.db.models
import
Count
from
django.db.models
import
Count
from
django.conf
import
settings
from
rest_framework
import
serializers
from
rest_framework
import
serializers
from
openedx.core.lib.api.serializers
import
CollapsedReferenceSerializer
,
PaginationSerializer
from
openedx.core.lib.api.serializers
import
CollapsedReferenceSerializer
,
PaginationSerializer
from
openedx.core.lib.api.fields
import
ExpandableField
from
openedx.core.lib.api.fields
import
ExpandableField
from
openedx.core.djangoapps.user_api.
serializers
import
User
Serializer
from
openedx.core.djangoapps.user_api.
accounts.serializers
import
UserReadOnly
Serializer
from
.models
import
CourseTeam
,
CourseTeamMembership
from
.models
import
CourseTeam
,
CourseTeamMembership
...
@@ -17,6 +18,10 @@ class UserMembershipSerializer(serializers.ModelSerializer):
...
@@ -17,6 +18,10 @@ class UserMembershipSerializer(serializers.ModelSerializer):
Used for listing team members.
Used for listing team members.
"""
"""
profile_configuration
=
deepcopy
(
settings
.
ACCOUNT_VISIBILITY_CONFIGURATION
)
profile_configuration
[
'shareable_fields'
]
.
append
(
'url'
)
profile_configuration
[
'public_fields'
]
.
append
(
'url'
)
user
=
ExpandableField
(
user
=
ExpandableField
(
collapsed_serializer
=
CollapsedReferenceSerializer
(
collapsed_serializer
=
CollapsedReferenceSerializer
(
model_class
=
User
,
model_class
=
User
,
...
@@ -24,7 +29,7 @@ class UserMembershipSerializer(serializers.ModelSerializer):
...
@@ -24,7 +29,7 @@ class UserMembershipSerializer(serializers.ModelSerializer):
view_name
=
'accounts_api'
,
view_name
=
'accounts_api'
,
read_only
=
True
,
read_only
=
True
,
),
),
expanded_serializer
=
User
Serializer
(
),
expanded_serializer
=
User
ReadOnlySerializer
(
configuration
=
profile_configuration
),
)
)
class
Meta
(
object
):
class
Meta
(
object
):
...
@@ -87,6 +92,10 @@ class CourseTeamCreationSerializer(serializers.ModelSerializer):
...
@@ -87,6 +92,10 @@ class CourseTeamCreationSerializer(serializers.ModelSerializer):
class
MembershipSerializer
(
serializers
.
ModelSerializer
):
class
MembershipSerializer
(
serializers
.
ModelSerializer
):
"""Serializes CourseTeamMemberships with information about both teams and users."""
"""Serializes CourseTeamMemberships with information about both teams and users."""
profile_configuration
=
deepcopy
(
settings
.
ACCOUNT_VISIBILITY_CONFIGURATION
)
profile_configuration
[
'shareable_fields'
]
.
append
(
'url'
)
profile_configuration
[
'public_fields'
]
.
append
(
'url'
)
user
=
ExpandableField
(
user
=
ExpandableField
(
collapsed_serializer
=
CollapsedReferenceSerializer
(
collapsed_serializer
=
CollapsedReferenceSerializer
(
model_class
=
User
,
model_class
=
User
,
...
@@ -94,7 +103,7 @@ class MembershipSerializer(serializers.ModelSerializer):
...
@@ -94,7 +103,7 @@ class MembershipSerializer(serializers.ModelSerializer):
view_name
=
'accounts_api'
,
view_name
=
'accounts_api'
,
read_only
=
True
,
read_only
=
True
,
),
),
expanded_serializer
=
User
Serializer
(
read_only
=
True
)
expanded_serializer
=
User
ReadOnlySerializer
(
configuration
=
profile_configuration
)
)
)
team
=
ExpandableField
(
team
=
ExpandableField
(
collapsed_serializer
=
CollapsedReferenceSerializer
(
collapsed_serializer
=
CollapsedReferenceSerializer
(
...
...
lms/djangoapps/teams/tests/test_views.py
View file @
716c1f74
...
@@ -110,7 +110,7 @@ class TeamAPITestCase(APITestCase, SharedModuleStoreTestCase):
...
@@ -110,7 +110,7 @@ class TeamAPITestCase(APITestCase, SharedModuleStoreTestCase):
@classmethod
@classmethod
def
setUpClass
(
cls
):
def
setUpClass
(
cls
):
super
(
TeamAPITestCase
,
cls
)
.
setUpClass
()
super
(
TeamAPITestCase
,
cls
)
.
setUpClass
()
teams_configuration
=
{
teams_configuration
_1
=
{
'topics'
:
'topics'
:
[
[
{
{
...
@@ -124,9 +124,30 @@ class TeamAPITestCase(APITestCase, SharedModuleStoreTestCase):
...
@@ -124,9 +124,30 @@ class TeamAPITestCase(APITestCase, SharedModuleStoreTestCase):
org
=
'TestX'
,
org
=
'TestX'
,
course
=
'TS101'
,
course
=
'TS101'
,
display_name
=
'Test Course'
,
display_name
=
'Test Course'
,
teams_configuration
=
teams_configuration
teams_configuration
=
teams_configuration_1
)
teams_configuration_2
=
{
'topics'
:
[
{
'id'
:
'topic_5'
,
'name'
:
'Other Interests'
,
'description'
:
'Description for topic 5.'
},
{
'id'
:
'topic_6'
,
'name'
:
'Public Profiles'
,
'description'
:
'Description for topic 6.'
},
]
}
cls
.
test_course_2
=
CourseFactory
.
create
(
org
=
'MIT'
,
course
=
'6.002x'
,
display_name
=
'Circuits'
,
teams_configuration
=
teams_configuration_2
)
)
cls
.
test_course_2
=
CourseFactory
.
create
(
org
=
'MIT'
,
course
=
'6.002x'
,
display_name
=
'Circuits'
)
def
setUp
(
self
):
def
setUp
(
self
):
super
(
TeamAPITestCase
,
self
)
.
setUp
()
super
(
TeamAPITestCase
,
self
)
.
setUp
()
...
@@ -152,6 +173,15 @@ class TeamAPITestCase(APITestCase, SharedModuleStoreTestCase):
...
@@ -152,6 +173,15 @@ class TeamAPITestCase(APITestCase, SharedModuleStoreTestCase):
username
=
'student_enrolled_both_courses_other_team'
username
=
'student_enrolled_both_courses_other_team'
)
)
# Make this student have a public profile
self
.
create_and_enroll_student
(
courses
=
[
self
.
test_course_2
],
username
=
'student_enrolled_public_profile'
)
profile
=
self
.
users
[
'student_enrolled_public_profile'
]
.
profile
profile
.
year_of_birth
=
1970
profile
.
save
()
# 'solar team' is intentionally lower case to test case insensitivity in name ordering
# 'solar team' is intentionally lower case to test case insensitivity in name ordering
self
.
test_team_1
=
CourseTeamFactory
.
create
(
self
.
test_team_1
=
CourseTeamFactory
.
create
(
name
=
u'sólar team'
,
name
=
u'sólar team'
,
...
@@ -162,11 +192,13 @@ class TeamAPITestCase(APITestCase, SharedModuleStoreTestCase):
...
@@ -162,11 +192,13 @@ class TeamAPITestCase(APITestCase, SharedModuleStoreTestCase):
self
.
test_team_3
=
CourseTeamFactory
.
create
(
name
=
'Nuclear Team'
,
course_id
=
self
.
test_course_1
.
id
)
self
.
test_team_3
=
CourseTeamFactory
.
create
(
name
=
'Nuclear Team'
,
course_id
=
self
.
test_course_1
.
id
)
self
.
test_team_4
=
CourseTeamFactory
.
create
(
name
=
'Coal Team'
,
course_id
=
self
.
test_course_1
.
id
,
is_active
=
False
)
self
.
test_team_4
=
CourseTeamFactory
.
create
(
name
=
'Coal Team'
,
course_id
=
self
.
test_course_1
.
id
,
is_active
=
False
)
self
.
test_team_5
=
CourseTeamFactory
.
create
(
name
=
'Another Team'
,
course_id
=
self
.
test_course_2
.
id
)
self
.
test_team_5
=
CourseTeamFactory
.
create
(
name
=
'Another Team'
,
course_id
=
self
.
test_course_2
.
id
)
self
.
test_team_6
=
CourseTeamFactory
.
create
(
name
=
'Public Profile Team'
,
course_id
=
self
.
test_course_2
.
id
,
topic_id
=
'topic_6'
)
for
user
,
course
in
[
for
user
,
course
in
[(
'staff'
,
self
.
test_course_1
),
(
'course_staff'
,
self
.
test_course_1
)]:
(
'staff'
,
self
.
test_course_1
),
(
'course_staff'
,
self
.
test_course_1
),
]:
CourseEnrollment
.
enroll
(
CourseEnrollment
.
enroll
(
self
.
users
[
user
],
course
.
id
,
check_access
=
True
self
.
users
[
user
],
course
.
id
,
check_access
=
True
)
)
...
@@ -174,6 +206,7 @@ class TeamAPITestCase(APITestCase, SharedModuleStoreTestCase):
...
@@ -174,6 +206,7 @@ class TeamAPITestCase(APITestCase, SharedModuleStoreTestCase):
self
.
test_team_1
.
add_user
(
self
.
users
[
'student_enrolled'
])
self
.
test_team_1
.
add_user
(
self
.
users
[
'student_enrolled'
])
self
.
test_team_3
.
add_user
(
self
.
users
[
'student_enrolled_both_courses_other_team'
])
self
.
test_team_3
.
add_user
(
self
.
users
[
'student_enrolled_both_courses_other_team'
])
self
.
test_team_5
.
add_user
(
self
.
users
[
'student_enrolled_both_courses_other_team'
])
self
.
test_team_5
.
add_user
(
self
.
users
[
'student_enrolled_both_courses_other_team'
])
self
.
test_team_6
.
add_user
(
self
.
users
[
'student_enrolled_public_profile'
])
def
create_and_enroll_student
(
self
,
courses
=
None
,
username
=
None
):
def
create_and_enroll_student
(
self
,
courses
=
None
,
username
=
None
):
""" Creates a new student and enrolls that student in the course.
""" Creates a new student and enrolls that student in the course.
...
@@ -305,11 +338,18 @@ class TeamAPITestCase(APITestCase, SharedModuleStoreTestCase):
...
@@ -305,11 +338,18 @@ class TeamAPITestCase(APITestCase, SharedModuleStoreTestCase):
**
kwargs
**
kwargs
)
)
def
verify_expanded_user
(
self
,
user
):
def
verify_expanded_
public_
user
(
self
,
user
):
"""Verifies that fields exist on the returned user json indicating that it is expanded."""
"""Verifies that fields exist on the returned user json indicating that it is expanded."""
for
field
in
[
'
id'
,
'url'
,
'email'
,
'name'
,
'username'
,
'preferenc
es'
]:
for
field
in
[
'
username'
,
'url'
,
'bio'
,
'country'
,
'profile_image'
,
'time_zone'
,
'language_proficienci
es'
]:
self
.
assertIn
(
field
,
user
)
self
.
assertIn
(
field
,
user
)
def
verify_expanded_private_user
(
self
,
user
):
"""Verifies that fields exist on the returned user json indicating that it is expanded."""
for
field
in
[
'username'
,
'url'
,
'profile_image'
]:
self
.
assertIn
(
field
,
user
)
for
field
in
[
'bio'
,
'country'
,
'time_zone'
,
'language_proficiencies'
]:
self
.
assertNotIn
(
field
,
user
)
def
verify_expanded_team
(
self
,
team
):
def
verify_expanded_team
(
self
,
team
):
"""Verifies that fields exist on the returned team json indicating that it is expanded."""
"""Verifies that fields exist on the returned team json indicating that it is expanded."""
for
field
in
[
'id'
,
'name'
,
'is_active'
,
'course_id'
,
'topic_id'
,
'date_created'
,
'description'
]:
for
field
in
[
'id'
,
'name'
,
'is_active'
,
'course_id'
,
'topic_id'
,
'date_created'
,
'description'
]:
...
@@ -347,7 +387,12 @@ class TestListTeamsAPI(TeamAPITestCase):
...
@@ -347,7 +387,12 @@ class TestListTeamsAPI(TeamAPITestCase):
self
.
verify_names
({
'course_id'
:
'no_such_course'
},
400
)
self
.
verify_names
({
'course_id'
:
'no_such_course'
},
400
)
def
test_filter_course_id
(
self
):
def
test_filter_course_id
(
self
):
self
.
verify_names
({
'course_id'
:
self
.
test_course_2
.
id
},
200
,
[
'Another Team'
],
user
=
'staff'
)
self
.
verify_names
(
{
'course_id'
:
self
.
test_course_2
.
id
},
200
,
[
'Another Team'
,
'Public Profile Team'
],
user
=
'staff'
)
def
test_filter_topic_id
(
self
):
def
test_filter_topic_id
(
self
):
self
.
verify_names
({
'course_id'
:
self
.
test_course_1
.
id
,
'topic_id'
:
'topic_0'
},
200
,
[
u'sólar team'
])
self
.
verify_names
({
'course_id'
:
self
.
test_course_1
.
id
,
'topic_id'
:
'topic_0'
},
200
,
[
u'sólar team'
])
...
@@ -386,9 +431,22 @@ class TestListTeamsAPI(TeamAPITestCase):
...
@@ -386,9 +431,22 @@ class TestListTeamsAPI(TeamAPITestCase):
self
.
assertIsNone
(
result
[
'next'
])
self
.
assertIsNone
(
result
[
'next'
])
self
.
assertIsNotNone
(
result
[
'previous'
])
self
.
assertIsNotNone
(
result
[
'previous'
])
def
test_expand_user
(
self
):
def
test_expand_private_user
(
self
):
# Use the default user which is already private because to year_of_birth is set
result
=
self
.
get_teams_list
(
200
,
{
'expand'
:
'user'
,
'topic_id'
:
'topic_0'
})
result
=
self
.
get_teams_list
(
200
,
{
'expand'
:
'user'
,
'topic_id'
:
'topic_0'
})
self
.
verify_expanded_user
(
result
[
'results'
][
0
][
'membership'
][
0
][
'user'
])
self
.
verify_expanded_private_user
(
result
[
'results'
][
0
][
'membership'
][
0
][
'user'
])
def
test_expand_public_user
(
self
):
result
=
self
.
get_teams_list
(
200
,
{
'expand'
:
'user'
,
'topic_id'
:
'topic_6'
,
'course_id'
:
self
.
test_course_2
.
id
},
user
=
'student_enrolled_public_profile'
)
self
.
verify_expanded_public_user
(
result
[
'results'
][
0
][
'membership'
][
0
][
'user'
])
@ddt.ddt
@ddt.ddt
...
@@ -515,9 +573,19 @@ class TestDetailTeamAPI(TeamAPITestCase):
...
@@ -515,9 +573,19 @@ class TestDetailTeamAPI(TeamAPITestCase):
def
test_does_not_exist
(
self
):
def
test_does_not_exist
(
self
):
self
.
get_team_detail
(
'no_such_team'
,
404
)
self
.
get_team_detail
(
'no_such_team'
,
404
)
def
test_expand_user
(
self
):
def
test_expand_private_user
(
self
):
# Use the default user which is already private because to year_of_birth is set
result
=
self
.
get_team_detail
(
self
.
test_team_1
.
team_id
,
200
,
{
'expand'
:
'user'
})
result
=
self
.
get_team_detail
(
self
.
test_team_1
.
team_id
,
200
,
{
'expand'
:
'user'
})
self
.
verify_expanded_user
(
result
[
'membership'
][
0
][
'user'
])
self
.
verify_expanded_private_user
(
result
[
'membership'
][
0
][
'user'
])
def
test_expand_public_user
(
self
):
result
=
self
.
get_team_detail
(
self
.
test_team_6
.
team_id
,
200
,
{
'expand'
:
'user'
},
user
=
'student_enrolled_public_profile'
)
self
.
verify_expanded_public_user
(
result
[
'membership'
][
0
][
'user'
])
@ddt.ddt
@ddt.ddt
...
@@ -715,9 +783,18 @@ class TestListMembershipAPI(TeamAPITestCase):
...
@@ -715,9 +783,18 @@ class TestListMembershipAPI(TeamAPITestCase):
def
test_bad_team_id
(
self
):
def
test_bad_team_id
(
self
):
self
.
get_membership_list
(
404
,
{
'team_id'
:
'no_such_team'
})
self
.
get_membership_list
(
404
,
{
'team_id'
:
'no_such_team'
})
def
test_expand_user
(
self
):
def
test_expand_private_user
(
self
):
# Use the default user which is already private because to year_of_birth is set
result
=
self
.
get_membership_list
(
200
,
{
'team_id'
:
self
.
test_team_1
.
team_id
,
'expand'
:
'user'
})
result
=
self
.
get_membership_list
(
200
,
{
'team_id'
:
self
.
test_team_1
.
team_id
,
'expand'
:
'user'
})
self
.
verify_expanded_user
(
result
[
'results'
][
0
][
'user'
])
self
.
verify_expanded_private_user
(
result
[
'results'
][
0
][
'user'
])
def
test_expand_public_user
(
self
):
result
=
self
.
get_membership_list
(
200
,
{
'team_id'
:
self
.
test_team_6
.
team_id
,
'expand'
:
'user'
},
user
=
'student_enrolled_public_profile'
)
self
.
verify_expanded_public_user
(
result
[
'results'
][
0
][
'user'
])
def
test_expand_team
(
self
):
def
test_expand_team
(
self
):
result
=
self
.
get_membership_list
(
200
,
{
'team_id'
:
self
.
test_team_1
.
team_id
,
'expand'
:
'team'
})
result
=
self
.
get_membership_list
(
200
,
{
'team_id'
:
self
.
test_team_1
.
team_id
,
'expand'
:
'team'
})
...
@@ -842,14 +919,25 @@ class TestDetailMembershipAPI(TeamAPITestCase):
...
@@ -842,14 +919,25 @@ class TestDetailMembershipAPI(TeamAPITestCase):
404
404
)
)
def
test_expand_user
(
self
):
def
test_expand_private_user
(
self
):
# Use the default user which is already private because to year_of_birth is set
result
=
self
.
get_membership_detail
(
result
=
self
.
get_membership_detail
(
self
.
test_team_1
.
team_id
,
self
.
test_team_1
.
team_id
,
self
.
users
[
'student_enrolled'
]
.
username
,
self
.
users
[
'student_enrolled'
]
.
username
,
200
,
200
,
{
'expand'
:
'user'
}
{
'expand'
:
'user'
}
)
)
self
.
verify_expanded_user
(
result
[
'user'
])
self
.
verify_expanded_private_user
(
result
[
'user'
])
def
test_expand_public_user
(
self
):
result
=
self
.
get_membership_detail
(
self
.
test_team_6
.
team_id
,
self
.
users
[
'student_enrolled_public_profile'
]
.
username
,
200
,
{
'expand'
:
'user'
},
user
=
'student_enrolled_public_profile'
)
self
.
verify_expanded_public_user
(
result
[
'user'
])
def
test_expand_team
(
self
):
def
test_expand_team
(
self
):
result
=
self
.
get_membership_detail
(
result
=
self
.
get_membership_detail
(
...
...
lms/djangoapps/teams/views.py
View file @
716c1f74
"""HTTP endpoints for the Teams API."""
"""HTTP endpoints for the Teams API."""
from
django.shortcuts
import
render_to_response
from
django.shortcuts
import
render_to_response
from
courseware.courses
import
get_course_with_access
,
has_access
from
django.http
import
Http404
from
django.http
import
Http404
from
django.conf
import
settings
from
django.conf
import
settings
from
django.core.paginator
import
Paginator
from
django.core.paginator
import
Paginator
from
django.views.generic.base
import
View
from
django.views.generic.base
import
View
import
newrelic.agent
import
newrelic.agent
from
rest_framework.generics
import
GenericAPIView
from
rest_framework.generics
import
GenericAPIView
from
rest_framework.response
import
Response
from
rest_framework.response
import
Response
from
rest_framework.reverse
import
reverse
from
rest_framework.reverse
import
reverse
...
@@ -18,16 +16,11 @@ from rest_framework.authentication import (
...
@@ -18,16 +16,11 @@ from rest_framework.authentication import (
)
)
from
rest_framework
import
status
from
rest_framework
import
status
from
rest_framework
import
permissions
from
rest_framework
import
permissions
from
django.db.models
import
Count
from
django.db.models
import
Count
from
django.contrib.auth.models
import
User
from
django.contrib.auth.models
import
User
from
django_countries
import
countries
from
django_countries
import
countries
from
django.utils.translation
import
ugettext
as
_
from
django.utils.translation
import
ugettext
as
_
from
django.utils.translation
import
ugettext_noop
from
django.utils.translation
import
ugettext_noop
from
student.models
import
CourseEnrollment
,
CourseAccessRole
from
student.roles
import
CourseStaffRole
from
openedx.core.lib.api.parsers
import
MergePatchParser
from
openedx.core.lib.api.parsers
import
MergePatchParser
from
openedx.core.lib.api.permissions
import
IsStaffOrReadOnly
from
openedx.core.lib.api.permissions
import
IsStaffOrReadOnly
from
openedx.core.lib.api.view_utils
import
(
from
openedx.core.lib.api.view_utils
import
(
...
@@ -37,14 +30,15 @@ from openedx.core.lib.api.view_utils import (
...
@@ -37,14 +30,15 @@ from openedx.core.lib.api.view_utils import (
ExpandableFieldViewMixin
ExpandableFieldViewMixin
)
)
from
openedx.core.lib.api.serializers
import
PaginationSerializer
from
openedx.core.lib.api.serializers
import
PaginationSerializer
from
xmodule.modulestore.django
import
modulestore
from
xmodule.modulestore.django
import
modulestore
from
opaque_keys
import
InvalidKeyError
from
opaque_keys
import
InvalidKeyError
from
opaque_keys.edx.keys
import
CourseKey
from
opaque_keys.edx.keys
import
CourseKey
from
courseware.courses
import
get_course_with_access
,
has_access
from
student.models
import
CourseEnrollment
,
CourseAccessRole
from
student.roles
import
CourseStaffRole
from
django_comment_client.utils
import
has_discussion_privileges
from
django_comment_client.utils
import
has_discussion_privileges
from
teams
import
is_feature_enabled
from
.models
import
CourseTeam
,
CourseTeamMembership
from
.models
import
CourseTeam
,
CourseTeamMembership
from
.serializers
import
(
from
.serializers
import
(
CourseTeamSerializer
,
CourseTeamSerializer
,
...
@@ -58,7 +52,6 @@ from .serializers import (
...
@@ -58,7 +52,6 @@ from .serializers import (
from
.errors
import
AlreadyOnTeamInCourse
,
NotEnrolledInCourseForTeam
from
.errors
import
AlreadyOnTeamInCourse
,
NotEnrolledInCourseForTeam
# Constants
TEAM_MEMBERSHIPS_PER_PAGE
=
2
TEAM_MEMBERSHIPS_PER_PAGE
=
2
TOPICS_PER_PAGE
=
12
TOPICS_PER_PAGE
=
12
...
@@ -120,13 +113,6 @@ class TeamsDashboardView(View):
...
@@ -120,13 +113,6 @@ class TeamsDashboardView(View):
return
render_to_response
(
"teams/teams.html"
,
context
)
return
render_to_response
(
"teams/teams.html"
,
context
)
def
is_feature_enabled
(
course
):
"""
Returns True if the teams feature is enabled.
"""
return
settings
.
FEATURES
.
get
(
'ENABLE_TEAMS'
,
False
)
and
course
.
teams_enabled
def
has_team_api_access
(
user
,
course_key
,
access_username
=
None
):
def
has_team_api_access
(
user
,
course_key
,
access_username
=
None
):
"""Returns True if the user has access to the Team API for the course
"""Returns True if the user has access to the Team API for the course
given by `course_key`. The user must either be enrolled in the course,
given by `course_key`. The user must either be enrolled in the course,
...
...
lms/djangoapps/verify_student/tests/test_views.py
View file @
716c1f74
...
@@ -1482,7 +1482,9 @@ class TestSubmitPhotosForVerification(TestCase):
...
@@ -1482,7 +1482,9 @@ class TestSubmitPhotosForVerification(TestCase):
AssertionError
AssertionError
"""
"""
account_settings
=
get_account_settings
(
self
.
user
)
request
=
RequestFactory
()
.
get
(
'/url'
)
request
.
user
=
self
.
user
account_settings
=
get_account_settings
(
request
)
self
.
assertEqual
(
account_settings
[
'name'
],
full_name
)
self
.
assertEqual
(
account_settings
[
'name'
],
full_name
)
def
_get_post_data
(
self
):
def
_get_post_data
(
self
):
...
...
lms/envs/common.py
View file @
716c1f74
...
@@ -2520,6 +2520,25 @@ ACCOUNT_VISIBILITY_CONFIGURATION = {
...
@@ -2520,6 +2520,25 @@ ACCOUNT_VISIBILITY_CONFIGURATION = {
'username'
,
'username'
,
'profile_image'
,
'profile_image'
,
],
],
# The list of account fields that are visible only to staff and users viewing their own profiles
"admin_fields"
:
[
"username"
,
"email"
,
"date_joined"
,
"is_active"
,
"bio"
,
"country"
,
"profile_image"
,
"language_proficiencies"
,
"name"
,
"gender"
,
"goals"
,
"year_of_birth"
,
"level_of_education"
,
"mailing_address"
,
"requires_parental_consent"
,
]
}
}
# E-Commerce API Configuration
# E-Commerce API Configuration
...
...
openedx/core/djangoapps/user_api/accounts/api.py
View file @
716c1f74
...
@@ -22,84 +22,67 @@ from ..helpers import intercept_errors
...
@@ -22,84 +22,67 @@ from ..helpers import intercept_errors
from
..models
import
UserPreference
from
..models
import
UserPreference
from
.
import
(
from
.
import
(
ACCOUNT_VISIBILITY_PREF_KEY
,
ALL_USERS_VISIBILITY
,
PRIVATE_VISIBILITY
,
ACCOUNT_VISIBILITY_PREF_KEY
,
PRIVATE_VISIBILITY
,
EMAIL_MIN_LENGTH
,
EMAIL_MAX_LENGTH
,
PASSWORD_MIN_LENGTH
,
PASSWORD_MAX_LENGTH
,
EMAIL_MIN_LENGTH
,
EMAIL_MAX_LENGTH
,
PASSWORD_MIN_LENGTH
,
PASSWORD_MAX_LENGTH
,
USERNAME_MIN_LENGTH
,
USERNAME_MAX_LENGTH
USERNAME_MIN_LENGTH
,
USERNAME_MAX_LENGTH
)
)
from
.serializers
import
AccountLegacyProfileSerializer
,
AccountUserSerializer
from
.serializers
import
(
AccountLegacyProfileSerializer
,
AccountUserSerializer
,
UserReadOnlySerializer
)
@intercept_errors
(
UserAPIInternalError
,
ignore_errors
=
[
UserAPIRequestError
])
@intercept_errors
(
UserAPIInternalError
,
ignore_errors
=
[
UserAPIRequestError
])
def
get_account_settings
(
request
ing_user
,
username
=
None
,
configuration
=
None
,
view
=
None
):
def
get_account_settings
(
request
,
username
=
None
,
configuration
=
None
,
view
=
None
):
"""Returns account information for a user serialized as JSON.
"""Returns account information for a user serialized as JSON.
Note:
Note:
If `request
ing_
user.username` != `username`, this method will return differing amounts of information
If `request
.
user.username` != `username`, this method will return differing amounts of information
based on who `request
ing_
user` is and the privacy settings of the user associated with `username`.
based on who `request
.
user` is and the privacy settings of the user associated with `username`.
Args:
Args:
request
ing_user (User): The user requesting the account information. Only the user with username
request
(Request): The request object with account information about the requesting user.
`username` or users with "is_staff" privileges can get full account information.
Only the user with username `username` or users with "is_staff" privileges can get full
Other users will get the account fields that the user has elected to share.
account information.
Other users will get the account fields that the user has elected to share.
username (str): Optional username for the desired account information. If not specified,
username (str): Optional username for the desired account information. If not specified,
`request
ing_
user.username` is assumed.
`request
.
user.username` is assumed.
configuration (dict): an optional configuration specifying which fields in the account
configuration (dict): an optional configuration specifying which fields in the account
can be shared, and the default visibility settings. If not present, the setting value with
can be shared, and the default visibility settings. If not present, the setting value with
key ACCOUNT_VISIBILITY_CONFIGURATION is used.
key ACCOUNT_VISIBILITY_CONFIGURATION is used.
view (str): An optional string allowing "is_staff" users and users requesting their own
view (str): An optional string allowing "is_staff" users and users requesting their own
account information to get just the fields that are shared with everyone. If view is
account information to get just the fields that are shared with everyone. If view is
"shared", only shared account information will be returned, regardless of `request
ing_
user`.
"shared", only shared account information will be returned, regardless of `request
.
user`.
Returns:
Returns:
A dict containing account fields.
A dict containing account fields.
Raises:
Raises:
UserNotFound: no user with username `username` exists (or `request
ing_
user.username` if
UserNotFound: no user with username `username` exists (or `request
.
user.username` if
`username` is not specified)
`username` is not specified)
UserAPIInternalError: the operation failed due to an unexpected error.
UserAPIInternalError: the operation failed due to an unexpected error.
"""
"""
requesting_user
=
request
.
user
if
username
is
None
:
if
username
is
None
:
username
=
requesting_user
.
username
username
=
requesting_user
.
username
has_full_access
=
requesting_user
.
username
==
username
or
requesting_user
.
is_staff
try
:
return_all_fields
=
has_full_access
and
view
!=
'shared'
existing_user
=
User
.
objects
.
get
(
username
=
username
)
except
ObjectDoesNotExist
:
existing_user
,
existing_user_profile
=
_get_user_and_profile
(
username
)
raise
UserNotFound
()
user_serializer
=
AccountUserSerializer
(
existing_user
)
legacy_profile_serializer
=
AccountLegacyProfileSerializer
(
existing_user_profile
)
account_settings
=
dict
(
user_serializer
.
data
,
**
legacy_profile_serializer
.
data
)
if
return_all_fields
:
return
account_settings
if
not
configuration
:
configuration
=
settings
.
ACCOUNT_VISIBILITY_CONFIGURATION
visible_settings
=
{}
profile_visibility
=
_get_profile_visibility
(
existing_user_profile
,
configuration
)
has_full_access
=
requesting_user
.
username
==
username
or
requesting_user
.
is_staff
if
profile_visibility
==
ALL_USERS_VISIBILITY
:
if
has_full_access
and
view
!=
'shared'
:
field_names
=
configuration
.
get
(
'shareable
_fields'
)
admin_fields
=
settings
.
ACCOUNT_VISIBILITY_CONFIGURATION
.
get
(
'admin
_fields'
)
else
:
else
:
field_names
=
configuration
.
get
(
'public_fields'
)
admin_fields
=
None
for
field_name
in
field_names
:
return
UserReadOnlySerializer
(
visible_settings
[
field_name
]
=
account_settings
.
get
(
field_name
,
None
)
existing_user
,
configuration
=
configuration
,
return
visible_settings
custom_fields
=
admin_fields
,
context
=
{
'request'
:
request
}
)
.
data
def
_get_profile_visibility
(
user_profile
,
configuration
):
"""Returns the visibility level for the specified user profile."""
if
user_profile
.
requires_parental_consent
():
return
PRIVATE_VISIBILITY
# Calling UserPreference directly because the requesting user may be different from existing_user
# (and does not have to be is_staff).
profile_privacy
=
UserPreference
.
get_value
(
user_profile
.
user
,
ACCOUNT_VISIBILITY_PREF_KEY
)
return
profile_privacy
if
profile_privacy
else
configuration
.
get
(
'default_visibility'
)
@intercept_errors
(
UserAPIInternalError
,
ignore_errors
=
[
UserAPIRequestError
])
@intercept_errors
(
UserAPIInternalError
,
ignore_errors
=
[
UserAPIRequestError
])
...
...
openedx/core/djangoapps/user_api/accounts/image_helpers.py
View file @
716c1f74
...
@@ -72,7 +72,7 @@ def get_profile_image_names(username):
...
@@ -72,7 +72,7 @@ def get_profile_image_names(username):
return
{
size
:
_get_profile_image_filename
(
name
,
size
)
for
size
in
_PROFILE_IMAGE_SIZES
}
return
{
size
:
_get_profile_image_filename
(
name
,
size
)
for
size
in
_PROFILE_IMAGE_SIZES
}
def
get_profile_image_urls_for_user
(
user
):
def
get_profile_image_urls_for_user
(
user
,
request
=
None
):
"""
"""
Return a dict {size:url} for each profile image for a given user.
Return a dict {size:url} for each profile image for a given user.
Notes:
Notes:
...
@@ -93,13 +93,19 @@ def get_profile_image_urls_for_user(user):
...
@@ -93,13 +93,19 @@ def get_profile_image_urls_for_user(user):
"""
"""
if
user
.
profile
.
has_profile_image
:
if
user
.
profile
.
has_profile_image
:
return
_get_profile_image_urls
(
urls
=
_get_profile_image_urls
(
_make_profile_image_name
(
user
.
username
),
_make_profile_image_name
(
user
.
username
),
get_profile_image_storage
(),
get_profile_image_storage
(),
version
=
user
.
profile
.
profile_image_uploaded_at
.
strftime
(
"
%
s"
),
version
=
user
.
profile
.
profile_image_uploaded_at
.
strftime
(
"
%
s"
),
)
)
else
:
else
:
return
_get_default_profile_image_urls
()
urls
=
_get_default_profile_image_urls
()
if
request
:
for
key
,
value
in
urls
.
items
():
urls
[
key
]
=
request
.
build_absolute_uri
(
value
)
return
urls
def
_get_default_profile_image_urls
():
def
_get_default_profile_image_urls
():
...
...
openedx/core/djangoapps/user_api/accounts/serializers.py
View file @
716c1f74
from
rest_framework
import
serializers
from
rest_framework
import
serializers
from
django.contrib.auth.models
import
User
from
django.contrib.auth.models
import
User
from
django.conf
import
settings
from
django.core.urlresolvers
import
reverse
from
openedx.core.djangoapps.user_api.accounts
import
NAME_MIN_LENGTH
from
openedx.core.djangoapps.user_api.accounts
import
NAME_MIN_LENGTH
from
openedx.core.djangoapps.user_api.serializers
import
ReadOnlyFieldsSerializerMixin
from
openedx.core.djangoapps.user_api.serializers
import
ReadOnlyFieldsSerializerMixin
from
student.models
import
UserProfile
,
LanguageProficiency
from
student.models
import
UserProfile
,
LanguageProficiency
from
..models
import
UserPreference
from
.image_helpers
import
get_profile_image_urls_for_user
from
.image_helpers
import
get_profile_image_urls_for_user
from
.
import
(
ACCOUNT_VISIBILITY_PREF_KEY
,
ALL_USERS_VISIBILITY
,
PRIVATE_VISIBILITY
,
)
PROFILE_IMAGE_KEY_PREFIX
=
'image_url'
PROFILE_IMAGE_KEY_PREFIX
=
'image_url'
...
@@ -32,6 +38,104 @@ class LanguageProficiencySerializer(serializers.ModelSerializer):
...
@@ -32,6 +38,104 @@ class LanguageProficiencySerializer(serializers.ModelSerializer):
return
None
return
None
class
UserReadOnlySerializer
(
serializers
.
Serializer
):
"""
Class that serializes the User model and UserProfile model together.
"""
def
__init__
(
self
,
*
args
,
**
kwargs
):
# Don't pass the 'configuration' arg up to the superclass
self
.
configuration
=
kwargs
.
pop
(
'configuration'
,
None
)
if
not
self
.
configuration
:
self
.
configuration
=
settings
.
ACCOUNT_VISIBILITY_CONFIGURATION
# Don't pass the 'custom_fields' arg up to the superclass
self
.
custom_fields
=
kwargs
.
pop
(
'custom_fields'
,
None
)
super
(
UserReadOnlySerializer
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
def
to_native
(
self
,
user
):
"""
Overwrite to_native to handle custom logic since we are serializing two models as one here
:param user: User object
:return: Dict serialized account
"""
profile
=
user
.
profile
data
=
{
"username"
:
user
.
username
,
"url"
:
self
.
context
.
get
(
'request'
)
.
build_absolute_uri
(
reverse
(
'accounts_api'
,
kwargs
=
{
'username'
:
user
.
username
})
),
"email"
:
user
.
email
,
"date_joined"
:
user
.
date_joined
,
"is_active"
:
user
.
is_active
,
"bio"
:
AccountLegacyProfileSerializer
.
convert_empty_to_None
(
profile
.
bio
),
"country"
:
AccountLegacyProfileSerializer
.
convert_empty_to_None
(
profile
.
country
.
code
),
"profile_image"
:
AccountLegacyProfileSerializer
.
get_profile_image
(
profile
,
user
,
self
.
context
.
get
(
'request'
)
),
"time_zone"
:
None
,
"language_proficiencies"
:
LanguageProficiencySerializer
(
profile
.
language_proficiencies
.
all
(),
many
=
True
)
.
data
,
"name"
:
profile
.
name
,
"gender"
:
AccountLegacyProfileSerializer
.
convert_empty_to_None
(
profile
.
gender
),
"goals"
:
profile
.
goals
,
"year_of_birth"
:
profile
.
year_of_birth
,
"level_of_education"
:
AccountLegacyProfileSerializer
.
convert_empty_to_None
(
profile
.
level_of_education
),
"mailing_address"
:
profile
.
mailing_address
,
"requires_parental_consent"
:
profile
.
requires_parental_consent
(),
}
return
self
.
_filter_fields
(
self
.
_visible_fields
(
profile
,
user
),
data
)
def
_visible_fields
(
self
,
user_profile
,
user
):
"""
Return what fields should be visible based on user settings
:param user_profile: User profile object
:param user: User object
:return: whitelist List of fields to be shown
"""
if
self
.
custom_fields
:
return
self
.
custom_fields
profile_visibility
=
self
.
_get_profile_visibility
(
user_profile
,
user
)
if
profile_visibility
==
ALL_USERS_VISIBILITY
:
return
self
.
configuration
.
get
(
'shareable_fields'
)
else
:
return
self
.
configuration
.
get
(
'public_fields'
)
def
_get_profile_visibility
(
self
,
user_profile
,
user
):
"""Returns the visibility level for the specified user profile."""
if
user_profile
.
requires_parental_consent
():
return
PRIVATE_VISIBILITY
# Calling UserPreference directly because the requesting user may be different from existing_user
# (and does not have to be is_staff).
profile_privacy
=
UserPreference
.
get_value
(
user
,
ACCOUNT_VISIBILITY_PREF_KEY
)
return
profile_privacy
if
profile_privacy
else
self
.
configuration
.
get
(
'default_visibility'
)
def
_filter_fields
(
self
,
field_whitelist
,
serialized_account
):
"""
Filter serialized account Dict to only include whitelisted keys
"""
visible_serialized_account
=
{}
for
field_name
in
field_whitelist
:
visible_serialized_account
[
field_name
]
=
serialized_account
.
get
(
field_name
,
None
)
return
visible_serialized_account
class
AccountUserSerializer
(
serializers
.
HyperlinkedModelSerializer
,
ReadOnlyFieldsSerializerMixin
):
class
AccountUserSerializer
(
serializers
.
HyperlinkedModelSerializer
,
ReadOnlyFieldsSerializerMixin
):
"""
"""
Class that serializes the portion of User model needed for account information.
Class that serializes the portion of User model needed for account information.
...
@@ -47,7 +151,7 @@ class AccountLegacyProfileSerializer(serializers.HyperlinkedModelSerializer, Rea
...
@@ -47,7 +151,7 @@ class AccountLegacyProfileSerializer(serializers.HyperlinkedModelSerializer, Rea
"""
"""
Class that serializes the portion of UserProfile model needed for account information.
Class that serializes the portion of UserProfile model needed for account information.
"""
"""
profile_image
=
serializers
.
SerializerMethodField
(
"get_profile_image"
)
profile_image
=
serializers
.
SerializerMethodField
(
"
_
get_profile_image"
)
requires_parental_consent
=
serializers
.
SerializerMethodField
(
"get_requires_parental_consent"
)
requires_parental_consent
=
serializers
.
SerializerMethodField
(
"get_requires_parental_consent"
)
language_proficiencies
=
LanguageProficiencySerializer
(
many
=
True
,
allow_add_remove
=
True
,
required
=
False
)
language_proficiencies
=
LanguageProficiencySerializer
(
many
=
True
,
allow_add_remove
=
True
,
required
=
False
)
...
@@ -102,10 +206,11 @@ class AccountLegacyProfileSerializer(serializers.HyperlinkedModelSerializer, Rea
...
@@ -102,10 +206,11 @@ class AccountLegacyProfileSerializer(serializers.HyperlinkedModelSerializer, Rea
""" Helper method to convert empty string to None (other values pass through). """
""" Helper method to convert empty string to None (other values pass through). """
return
None
if
value
==
""
else
value
return
None
if
value
==
""
else
value
def
get_profile_image
(
self
,
user_profile
):
@staticmethod
def
get_profile_image
(
user_profile
,
user
,
request
=
None
):
""" Returns metadata about a user's profile image. """
""" Returns metadata about a user's profile image. """
data
=
{
'has_image'
:
user_profile
.
has_profile_image
}
data
=
{
'has_image'
:
user_profile
.
has_profile_image
}
urls
=
get_profile_image_urls_for_user
(
user
_profile
.
user
)
urls
=
get_profile_image_urls_for_user
(
user
,
request
)
data
.
update
({
data
.
update
({
'{image_key_prefix}_{size}'
.
format
(
image_key_prefix
=
PROFILE_IMAGE_KEY_PREFIX
,
size
=
size_display_name
):
url
'{image_key_prefix}_{size}'
.
format
(
image_key_prefix
=
PROFILE_IMAGE_KEY_PREFIX
,
size
=
size_display_name
):
url
for
size_display_name
,
url
in
urls
.
items
()
for
size_display_name
,
url
in
urls
.
items
()
...
@@ -115,3 +220,13 @@ class AccountLegacyProfileSerializer(serializers.HyperlinkedModelSerializer, Rea
...
@@ -115,3 +220,13 @@ class AccountLegacyProfileSerializer(serializers.HyperlinkedModelSerializer, Rea
def
get_requires_parental_consent
(
self
,
user_profile
):
def
get_requires_parental_consent
(
self
,
user_profile
):
""" Returns a boolean representing whether the user requires parental controls. """
""" Returns a boolean representing whether the user requires parental controls. """
return
user_profile
.
requires_parental_consent
()
return
user_profile
.
requires_parental_consent
()
def
_get_profile_image
(
self
,
user_profile
):
"""
Returns metadata about a user's profile image
This protected method delegates to the static 'get_profile_image' method
because 'serializers.SerializerMethodField("_get_profile_image")' will
call the method with a single argument, the user_profile object.
"""
return
AccountLegacyProfileSerializer
.
get_profile_image
(
user_profile
,
user_profile
.
user
)
openedx/core/djangoapps/user_api/accounts/tests/test_api.py
View file @
716c1f74
...
@@ -15,6 +15,7 @@ from student.tests.factories import UserFactory
...
@@ -15,6 +15,7 @@ from student.tests.factories import UserFactory
from
django.conf
import
settings
from
django.conf
import
settings
from
django.contrib.auth.models
import
User
from
django.contrib.auth.models
import
User
from
django.core
import
mail
from
django.core
import
mail
from
django.test.client
import
RequestFactory
from
student.models
import
PendingEmailChange
from
student.models
import
PendingEmailChange
from
student.tests.tests
import
UserSettingsEventTestMixin
from
student.tests.tests
import
UserSettingsEventTestMixin
from
...errors
import
(
from
...errors
import
(
...
@@ -43,21 +44,24 @@ class TestAccountApi(UserSettingsEventTestMixin, TestCase):
...
@@ -43,21 +44,24 @@ class TestAccountApi(UserSettingsEventTestMixin, TestCase):
def
setUp
(
self
):
def
setUp
(
self
):
super
(
TestAccountApi
,
self
)
.
setUp
()
super
(
TestAccountApi
,
self
)
.
setUp
()
self
.
request_factory
=
RequestFactory
()
self
.
table
=
"student_languageproficiency"
self
.
table
=
"student_languageproficiency"
self
.
user
=
UserFactory
.
create
(
password
=
self
.
password
)
self
.
user
=
UserFactory
.
create
(
password
=
self
.
password
)
self
.
default_request
=
self
.
request_factory
.
get
(
"/api/user/v1/accounts/"
)
self
.
default_request
.
user
=
self
.
user
self
.
different_user
=
UserFactory
.
create
(
password
=
self
.
password
)
self
.
different_user
=
UserFactory
.
create
(
password
=
self
.
password
)
self
.
staff_user
=
UserFactory
(
is_staff
=
True
,
password
=
self
.
password
)
self
.
staff_user
=
UserFactory
(
is_staff
=
True
,
password
=
self
.
password
)
self
.
reset_tracker
()
self
.
reset_tracker
()
def
test_get_username_provided
(
self
):
def
test_get_username_provided
(
self
):
"""Test the difference in behavior when a username is supplied to get_account_settings."""
"""Test the difference in behavior when a username is supplied to get_account_settings."""
account_settings
=
get_account_settings
(
self
.
user
)
account_settings
=
get_account_settings
(
self
.
default_request
)
self
.
assertEqual
(
self
.
user
.
username
,
account_settings
[
"username"
])
self
.
assertEqual
(
self
.
user
.
username
,
account_settings
[
"username"
])
account_settings
=
get_account_settings
(
self
.
user
,
username
=
self
.
user
.
username
)
account_settings
=
get_account_settings
(
self
.
default_request
,
username
=
self
.
user
.
username
)
self
.
assertEqual
(
self
.
user
.
username
,
account_settings
[
"username"
])
self
.
assertEqual
(
self
.
user
.
username
,
account_settings
[
"username"
])
account_settings
=
get_account_settings
(
self
.
user
,
username
=
self
.
different_user
.
username
)
account_settings
=
get_account_settings
(
self
.
default_request
,
username
=
self
.
different_user
.
username
)
self
.
assertEqual
(
self
.
different_user
.
username
,
account_settings
[
"username"
])
self
.
assertEqual
(
self
.
different_user
.
username
,
account_settings
[
"username"
])
def
test_get_configuration_provided
(
self
):
def
test_get_configuration_provided
(
self
):
...
@@ -75,29 +79,35 @@ class TestAccountApi(UserSettingsEventTestMixin, TestCase):
...
@@ -75,29 +79,35 @@ class TestAccountApi(UserSettingsEventTestMixin, TestCase):
}
}
# With default configuration settings, email is not shared with other (non-staff) users.
# With default configuration settings, email is not shared with other (non-staff) users.
account_settings
=
get_account_settings
(
self
.
user
,
self
.
different_user
.
username
)
account_settings
=
get_account_settings
(
self
.
default_request
,
self
.
different_user
.
username
)
self
.
assertFalse
(
"email"
in
account_settings
)
self
.
assertFalse
(
"email"
in
account_settings
)
account_settings
=
get_account_settings
(
self
.
user
,
self
.
different_user
.
username
,
configuration
=
config
)
account_settings
=
get_account_settings
(
self
.
default_request
,
self
.
different_user
.
username
,
configuration
=
config
)
self
.
assertEqual
(
self
.
different_user
.
email
,
account_settings
[
"email"
])
self
.
assertEqual
(
self
.
different_user
.
email
,
account_settings
[
"email"
])
def
test_get_user_not_found
(
self
):
def
test_get_user_not_found
(
self
):
"""Test that UserNotFound is thrown if there is no user with username."""
"""Test that UserNotFound is thrown if there is no user with username."""
with
self
.
assertRaises
(
UserNotFound
):
with
self
.
assertRaises
(
UserNotFound
):
get_account_settings
(
self
.
user
,
username
=
"does_not_exist"
)
get_account_settings
(
self
.
default_request
,
username
=
"does_not_exist"
)
self
.
user
.
username
=
"does_not_exist"
self
.
user
.
username
=
"does_not_exist"
request
=
self
.
request_factory
.
get
(
"/api/user/v1/accounts/"
)
request
.
user
=
self
.
user
with
self
.
assertRaises
(
UserNotFound
):
with
self
.
assertRaises
(
UserNotFound
):
get_account_settings
(
self
.
user
)
get_account_settings
(
request
)
def
test_update_username_provided
(
self
):
def
test_update_username_provided
(
self
):
"""Test the difference in behavior when a username is supplied to update_account_settings."""
"""Test the difference in behavior when a username is supplied to update_account_settings."""
update_account_settings
(
self
.
user
,
{
"name"
:
"Mickey Mouse"
})
update_account_settings
(
self
.
user
,
{
"name"
:
"Mickey Mouse"
})
account_settings
=
get_account_settings
(
self
.
user
)
account_settings
=
get_account_settings
(
self
.
default_request
)
self
.
assertEqual
(
"Mickey Mouse"
,
account_settings
[
"name"
])
self
.
assertEqual
(
"Mickey Mouse"
,
account_settings
[
"name"
])
update_account_settings
(
self
.
user
,
{
"name"
:
"Donald Duck"
},
username
=
self
.
user
.
username
)
update_account_settings
(
self
.
user
,
{
"name"
:
"Donald Duck"
},
username
=
self
.
user
.
username
)
account_settings
=
get_account_settings
(
self
.
user
)
account_settings
=
get_account_settings
(
self
.
default_request
)
self
.
assertEqual
(
"Donald Duck"
,
account_settings
[
"name"
])
self
.
assertEqual
(
"Donald Duck"
,
account_settings
[
"name"
])
with
self
.
assertRaises
(
UserNotAuthorized
):
with
self
.
assertRaises
(
UserNotAuthorized
):
...
@@ -171,7 +181,7 @@ class TestAccountApi(UserSettingsEventTestMixin, TestCase):
...
@@ -171,7 +181,7 @@ class TestAccountApi(UserSettingsEventTestMixin, TestCase):
self
.
assertIn
(
"Error thrown from do_email_change_request"
,
context_manager
.
exception
.
developer_message
)
self
.
assertIn
(
"Error thrown from do_email_change_request"
,
context_manager
.
exception
.
developer_message
)
# Verify that the name change happened, even though the attempt to send the email failed.
# Verify that the name change happened, even though the attempt to send the email failed.
account_settings
=
get_account_settings
(
self
.
user
)
account_settings
=
get_account_settings
(
self
.
default_request
)
self
.
assertEqual
(
"Mickey Mouse"
,
account_settings
[
"name"
])
self
.
assertEqual
(
"Mickey Mouse"
,
account_settings
[
"name"
])
@patch
(
'openedx.core.djangoapps.user_api.accounts.serializers.AccountUserSerializer.save'
)
@patch
(
'openedx.core.djangoapps.user_api.accounts.serializers.AccountUserSerializer.save'
)
...
@@ -229,7 +239,9 @@ class AccountSettingsOnCreationTest(TestCase):
...
@@ -229,7 +239,9 @@ class AccountSettingsOnCreationTest(TestCase):
# Retrieve the account settings
# Retrieve the account settings
user
=
User
.
objects
.
get
(
username
=
self
.
USERNAME
)
user
=
User
.
objects
.
get
(
username
=
self
.
USERNAME
)
account_settings
=
get_account_settings
(
user
)
request
=
RequestFactory
()
.
get
(
"/api/user/v1/accounts/"
)
request
.
user
=
user
account_settings
=
get_account_settings
(
request
)
# Expect a date joined field but remove it to simplify the following comparison
# Expect a date joined field but remove it to simplify the following comparison
self
.
assertIsNotNone
(
account_settings
[
'date_joined'
])
self
.
assertIsNotNone
(
account_settings
[
'date_joined'
])
...
@@ -250,8 +262,8 @@ class AccountSettingsOnCreationTest(TestCase):
...
@@ -250,8 +262,8 @@ class AccountSettingsOnCreationTest(TestCase):
'bio'
:
None
,
'bio'
:
None
,
'profile_image'
:
{
'profile_image'
:
{
'has_image'
:
False
,
'has_image'
:
False
,
'image_url_full'
:
'/static/default_50.png'
,
'image_url_full'
:
request
.
build_absolute_uri
(
'/static/default_50.png'
)
,
'image_url_small'
:
'/static/default_10.png'
,
'image_url_small'
:
request
.
build_absolute_uri
(
'/static/default_10.png'
)
,
},
},
'requires_parental_consent'
:
True
,
'requires_parental_consent'
:
True
,
'language_proficiencies'
:
[],
'language_proficiencies'
:
[],
...
@@ -303,18 +315,22 @@ class AccountCreationActivationAndPasswordChangeTest(TestCase):
...
@@ -303,18 +315,22 @@ class AccountCreationActivationAndPasswordChangeTest(TestCase):
u'a'
*
(
PASSWORD_MAX_LENGTH
+
1
)
u'a'
*
(
PASSWORD_MAX_LENGTH
+
1
)
]
]
@unittest.skipUnless
(
settings
.
ROOT_URLCONF
==
'lms.urls'
,
'Test only valid in lms'
)
def
test_activate_account
(
self
):
def
test_activate_account
(
self
):
# Create the account, which is initially inactive
# Create the account, which is initially inactive
activation_key
=
create_account
(
self
.
USERNAME
,
self
.
PASSWORD
,
self
.
EMAIL
)
activation_key
=
create_account
(
self
.
USERNAME
,
self
.
PASSWORD
,
self
.
EMAIL
)
user
=
User
.
objects
.
get
(
username
=
self
.
USERNAME
)
user
=
User
.
objects
.
get
(
username
=
self
.
USERNAME
)
account
=
get_account_settings
(
user
)
request
=
RequestFactory
()
.
get
(
"/api/user/v1/accounts/"
)
request
.
user
=
user
account
=
get_account_settings
(
request
)
self
.
assertEqual
(
self
.
USERNAME
,
account
[
"username"
])
self
.
assertEqual
(
self
.
USERNAME
,
account
[
"username"
])
self
.
assertEqual
(
self
.
EMAIL
,
account
[
"email"
])
self
.
assertEqual
(
self
.
EMAIL
,
account
[
"email"
])
self
.
assertFalse
(
account
[
"is_active"
])
self
.
assertFalse
(
account
[
"is_active"
])
# Activate the account and verify that it is now active
# Activate the account and verify that it is now active
activate_account
(
activation_key
)
activate_account
(
activation_key
)
account
=
get_account_settings
(
user
)
account
=
get_account_settings
(
request
)
self
.
assertTrue
(
account
[
'is_active'
])
self
.
assertTrue
(
account
[
'is_active'
])
def
test_create_account_duplicate_username
(
self
):
def
test_create_account_duplicate_username
(
self
):
...
...
openedx/core/djangoapps/user_api/accounts/tests/test_views.py
View file @
716c1f74
...
@@ -321,11 +321,12 @@ class TestAccountAPI(UserAPITestCase):
...
@@ -321,11 +321,12 @@ class TestAccountAPI(UserAPITestCase):
legacy_profile
.
country
=
""
legacy_profile
.
country
=
""
legacy_profile
.
level_of_education
=
""
legacy_profile
.
level_of_education
=
""
legacy_profile
.
gender
=
""
legacy_profile
.
gender
=
""
legacy_profile
.
bio
=
""
legacy_profile
.
save
()
legacy_profile
.
save
()
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
self
.
test_password
)
self
.
client
.
login
(
username
=
self
.
user
.
username
,
password
=
self
.
test_password
)
response
=
self
.
send_get
(
self
.
client
)
response
=
self
.
send_get
(
self
.
client
)
for
empty_field
in
(
"level_of_education"
,
"gender"
,
"country"
):
for
empty_field
in
(
"level_of_education"
,
"gender"
,
"country"
,
"bio"
):
self
.
assertIsNone
(
response
.
data
[
empty_field
])
self
.
assertIsNone
(
response
.
data
[
empty_field
])
@ddt.data
(
@ddt.data
(
...
...
openedx/core/djangoapps/user_api/accounts/views.py
View file @
716c1f74
...
@@ -146,11 +146,7 @@ class AccountView(APIView):
...
@@ -146,11 +146,7 @@ class AccountView(APIView):
GET /api/user/v1/accounts/{username}/
GET /api/user/v1/accounts/{username}/
"""
"""
try
:
try
:
account_settings
=
get_account_settings
(
request
.
user
,
username
,
view
=
request
.
QUERY_PARAMS
.
get
(
'view'
))
account_settings
=
get_account_settings
(
request
,
username
,
view
=
request
.
QUERY_PARAMS
.
get
(
'view'
))
# Account for possibly relative URLs.
for
key
,
value
in
account_settings
[
'profile_image'
]
.
items
():
if
key
.
startswith
(
PROFILE_IMAGE_KEY_PREFIX
):
account_settings
[
'profile_image'
][
key
]
=
request
.
build_absolute_uri
(
value
)
except
UserNotFound
:
except
UserNotFound
:
return
Response
(
status
=
status
.
HTTP_403_FORBIDDEN
if
request
.
user
.
is_staff
else
status
.
HTTP_404_NOT_FOUND
)
return
Response
(
status
=
status
.
HTTP_403_FORBIDDEN
if
request
.
user
.
is_staff
else
status
.
HTTP_404_NOT_FOUND
)
...
...
openedx/core/djangoapps/user_api/tests/test_views.py
View file @
716c1f74
...
@@ -18,6 +18,7 @@ from django.contrib.auth.models import User
...
@@ -18,6 +18,7 @@ from django.contrib.auth.models import User
from
django.test
import
TestCase
from
django.test
import
TestCase
from
django.test.testcases
import
TransactionTestCase
from
django.test.testcases
import
TransactionTestCase
from
django.test.utils
import
override_settings
from
django.test.utils
import
override_settings
from
django.test.client
import
RequestFactory
from
social.apps.django_app.default.models
import
UserSocialAuth
from
social.apps.django_app.default.models
import
UserSocialAuth
...
@@ -1269,7 +1270,9 @@ class RegistrationViewTest(ThirdPartyAuthTestMixin, ApiTestCase):
...
@@ -1269,7 +1270,9 @@ class RegistrationViewTest(ThirdPartyAuthTestMixin, ApiTestCase):
self
.
assertIn
(
settings
.
EDXMKTG_USER_INFO_COOKIE_NAME
,
self
.
client
.
cookies
)
self
.
assertIn
(
settings
.
EDXMKTG_USER_INFO_COOKIE_NAME
,
self
.
client
.
cookies
)
user
=
User
.
objects
.
get
(
username
=
self
.
USERNAME
)
user
=
User
.
objects
.
get
(
username
=
self
.
USERNAME
)
account_settings
=
get_account_settings
(
user
)
request
=
RequestFactory
()
.
get
(
'/url'
)
request
.
user
=
user
account_settings
=
get_account_settings
(
request
)
self
.
assertEqual
(
self
.
USERNAME
,
account_settings
[
"username"
])
self
.
assertEqual
(
self
.
USERNAME
,
account_settings
[
"username"
])
self
.
assertEqual
(
self
.
EMAIL
,
account_settings
[
"email"
])
self
.
assertEqual
(
self
.
EMAIL
,
account_settings
[
"email"
])
...
@@ -1307,7 +1310,10 @@ class RegistrationViewTest(ThirdPartyAuthTestMixin, ApiTestCase):
...
@@ -1307,7 +1310,10 @@ class RegistrationViewTest(ThirdPartyAuthTestMixin, ApiTestCase):
# Verify the user's account
# Verify the user's account
user
=
User
.
objects
.
get
(
username
=
self
.
USERNAME
)
user
=
User
.
objects
.
get
(
username
=
self
.
USERNAME
)
account_settings
=
get_account_settings
(
user
)
request
=
RequestFactory
()
.
get
(
'/url'
)
request
.
user
=
user
account_settings
=
get_account_settings
(
request
)
self
.
assertEqual
(
account_settings
[
"level_of_education"
],
self
.
EDUCATION
)
self
.
assertEqual
(
account_settings
[
"level_of_education"
],
self
.
EDUCATION
)
self
.
assertEqual
(
account_settings
[
"mailing_address"
],
self
.
ADDRESS
)
self
.
assertEqual
(
account_settings
[
"mailing_address"
],
self
.
ADDRESS
)
self
.
assertEqual
(
account_settings
[
"year_of_birth"
],
int
(
self
.
YEAR_OF_BIRTH
))
self
.
assertEqual
(
account_settings
[
"year_of_birth"
],
int
(
self
.
YEAR_OF_BIRTH
))
...
...
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