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
3af769b4
Commit
3af769b4
authored
Aug 01, 2016
by
Kevin Kim
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Update time zone dropdown in account settings based on user country
parent
95b16dba
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
244 additions
and
35 deletions
+244
-35
lms/static/js/spec/student_account/account_settings_factory_spec.js
+7
-0
lms/static/js/spec/student_account/account_settings_fields_spec.js
+68
-1
lms/static/js/spec/student_account/helpers.js
+6
-0
lms/static/js/student_account/views/account_settings_factory.js
+19
-3
lms/static/js/student_account/views/account_settings_fields.js
+61
-0
lms/static/js/views/fields.js
+22
-3
lms/templates/fields/field_dropdown.underscore
+7
-2
lms/templates/fields/field_dropdown_account.underscore
+7
-2
openedx/core/djangoapps/user_api/preferences/api.py
+32
-4
openedx/core/djangoapps/user_api/preferences/tests/test_api.py
+13
-5
openedx/core/djangoapps/user_api/serializers.py
+2
-15
No files found.
lms/static/js/spec/student_account/account_settings_factory_spec.js
View file @
3af769b4
...
...
@@ -108,6 +108,11 @@ define(['backbone',
request
=
requests
[
1
];
expect
(
request
.
method
).
toBe
(
'GET'
);
expect
(
request
.
url
).
toBe
(
'/user_api/v1/preferences/time_zones/?country_code=1'
);
AjaxHelpers
.
respondWithJson
(
requests
,
Helpers
.
TIME_ZONE_RESPONSE
);
request
=
requests
[
2
];
expect
(
request
.
method
).
toBe
(
'GET'
);
expect
(
request
.
url
).
toBe
(
Helpers
.
USER_PREFERENCES_API_URL
);
AjaxHelpers
.
respondWithError
(
requests
,
500
);
...
...
@@ -126,6 +131,7 @@ define(['backbone',
Helpers
.
expectSettingsSectionsButNotFieldsToBeRendered
(
accountSettingsView
);
AjaxHelpers
.
respondWithJson
(
requests
,
Helpers
.
createAccountSettingsData
());
AjaxHelpers
.
respondWithJson
(
requests
,
Helpers
.
TIME_ZONE_RESPONSE
);
AjaxHelpers
.
respondWithJson
(
requests
,
Helpers
.
createUserPreferencesData
());
Helpers
.
expectLoadingIndicatorIsVisible
(
accountSettingsView
,
false
);
...
...
@@ -141,6 +147,7 @@ define(['backbone',
var
accountSettingsView
=
createAccountSettingsPage
();
AjaxHelpers
.
respondWithJson
(
requests
,
Helpers
.
createAccountSettingsData
());
AjaxHelpers
.
respondWithJson
(
requests
,
Helpers
.
TIME_ZONE_RESPONSE
);
AjaxHelpers
.
respondWithJson
(
requests
,
Helpers
.
createUserPreferencesData
());
AjaxHelpers
.
respondWithJson
(
requests
,
{});
// Page viewed analytics event
...
...
lms/static/js/spec/student_account/account_settings_fields_spec.js
View file @
3af769b4
...
...
@@ -45,7 +45,74 @@ define(['backbone',
);
});
it
(
'sends request to /i18n/setlang/ after changing language preference in LanguagePreferenceFieldView'
,
function
()
{
it
(
'update time zone dropdown after country dropdown changes'
,
function
()
{
var
baseSelector
=
'.u-field-value > select'
;
var
groupsSelector
=
baseSelector
+
'> optgroup'
;
var
groupOptionsSelector
=
groupsSelector
+
'> option'
;
var
timeZoneData
=
FieldViewsSpecHelpers
.
createFieldData
(
AccountSettingsFieldViews
.
TimeZoneFieldView
,
{
valueAttribute
:
'time_zone'
,
groupOptions
:
[{
groupTitle
:
gettext
(
'All Time Zones'
),
selectOptions
:
FieldViewsSpecHelpers
.
SELECT_OPTIONS
}],
persistChanges
:
true
,
required
:
true
});
var
countryData
=
FieldViewsSpecHelpers
.
createFieldData
(
AccountSettingsFieldViews
.
DropdownFieldView
,
{
valueAttribute
:
'country'
,
options
:
[[
'KY'
,
'Cayman Islands'
],
[
'CA'
,
'Canada'
],
[
'GY'
,
'Guyana'
]],
persistChanges
:
true
});
var
countryChange
=
{
country
:
'GY'
};
var
timeZoneChange
=
{
time_zone
:
'Pacific/Kosrae'
};
var
timeZoneView
=
new
AccountSettingsFieldViews
.
TimeZoneFieldView
(
timeZoneData
).
render
();
var
countryView
=
new
AccountSettingsFieldViews
.
DropdownFieldView
(
countryData
).
render
();
requests
=
AjaxHelpers
.
requests
(
this
);
timeZoneView
.
listenToCountryView
(
countryView
);
// expect time zone dropdown to have single subheader ('All Time Zones')
expect
(
timeZoneView
.
$
(
groupsSelector
).
length
).
toBe
(
1
);
expect
(
timeZoneView
.
$
(
groupOptionsSelector
).
length
).
toBe
(
3
);
expect
(
timeZoneView
.
$
(
groupOptionsSelector
)[
0
].
value
).
toBe
(
FieldViewsSpecHelpers
.
SELECT_OPTIONS
[
0
][
0
]);
// change country
countryView
.
$
(
baseSelector
).
val
(
countryChange
[
countryData
.
valueAttribute
]).
change
();
FieldViewsSpecHelpers
.
expectAjaxRequestWithData
(
requests
,
countryChange
);
AjaxHelpers
.
respondWithJson
(
requests
,
{
success
:
'true'
});
AjaxHelpers
.
expectRequest
(
requests
,
'GET'
,
'/user_api/v1/preferences/time_zones/?country_code=GY'
);
AjaxHelpers
.
respondWithJson
(
requests
,
[
{
time_zone
:
'America/Guyana'
,
description
:
'America/Guyana (ECT, UTC-0500)'
},
{
time_zone
:
'Pacific/Kosrae'
,
description
:
'Pacific/Kosrae (KOST, UTC+1100)'
}
]);
// expect time zone dropdown to have two subheaders (country/all time zone sub-headers) with new values
expect
(
timeZoneView
.
$
(
groupsSelector
).
length
).
toBe
(
2
);
expect
(
timeZoneView
.
$
(
groupOptionsSelector
).
length
).
toBe
(
5
);
expect
(
timeZoneView
.
$
(
groupOptionsSelector
)[
0
].
value
).
toBe
(
'America/Guyana'
);
// select time zone option from option
timeZoneView
.
$
(
baseSelector
).
val
(
timeZoneChange
[
timeZoneData
.
valueAttribute
]).
change
();
FieldViewsSpecHelpers
.
expectAjaxRequestWithData
(
requests
,
timeZoneChange
);
AjaxHelpers
.
respondWithJson
(
requests
,
{
success
:
'true'
});
timeZoneView
.
render
();
// expect time zone dropdown to have three subheaders (currently selected/country/all time zones)
expect
(
timeZoneView
.
$
(
groupsSelector
).
length
).
toBe
(
3
);
expect
(
timeZoneView
.
$
(
groupOptionsSelector
).
length
).
toBe
(
6
);
expect
(
timeZoneView
.
$
(
groupOptionsSelector
)[
0
].
value
).
toBe
(
'Pacific/Kosrae'
);
});
it
(
'sends request to /i18n/setlang/ after changing language in LanguagePreferenceFieldView'
,
function
()
{
requests
=
AjaxHelpers
.
requests
(
this
);
var
selector
=
'.u-field-value > select'
;
...
...
lms/static/js/spec/student_account/helpers.js
View file @
3af769b4
...
...
@@ -49,6 +49,11 @@ define(['underscore'], function(_) {
[
'3'
,
'Option 3'
]
];
var
TIME_ZONE_RESPONSE
=
[{
time_zone
:
'America/Guyana'
,
description
:
'America/Guyana (ECT, UTC-0500)'
}];
var
IMAGE_MAX_BYTES
=
1024
*
1024
;
var
IMAGE_MIN_BYTES
=
100
;
...
...
@@ -123,6 +128,7 @@ define(['underscore'], function(_) {
createAccountSettingsData
:
createAccountSettingsData
,
createUserPreferencesData
:
createUserPreferencesData
,
FIELD_OPTIONS
:
FIELD_OPTIONS
,
TIME_ZONE_RESPONSE
:
TIME_ZONE_RESPONSE
,
expectLoadingIndicatorIsVisible
:
expectLoadingIndicatorIsVisible
,
expectLoadingErrorIsVisible
:
expectLoadingErrorIsVisible
,
expectElementContainsField
:
expectElementContainsField
,
...
...
lms/static/js/student_account/views/account_settings_factory.js
View file @
3af769b4
...
...
@@ -20,7 +20,7 @@
)
{
var
accountSettingsElement
,
userAccountModel
,
userPreferencesModel
,
aboutSectionsData
,
accountsSectionData
,
ordersSectionData
,
accountSettingsView
,
showAccountSettingsPage
,
showLoadingError
,
orderNumber
;
showLoadingError
,
orderNumber
,
getUserField
,
userFields
,
timeZoneDropdownField
,
countryDropdownField
;
accountSettingsElement
=
$
(
'.wrapper-account-settings'
);
...
...
@@ -110,7 +110,7 @@
})
},
{
view
:
new
AccountSettingsFieldViews
.
Dropdown
FieldView
({
view
:
new
AccountSettingsFieldViews
.
TimeZone
FieldView
({
model
:
userPreferencesModel
,
required
:
true
,
title
:
gettext
(
'Time Zone'
),
...
...
@@ -120,7 +120,10 @@
'time zone here, course dates, including assignment deadlines, are displayed in '
+
'Coordinated Universal Time (UTC).'
),
options
:
fieldsData
.
time_zone
.
options
,
groupOptions
:
[{
groupTitle
:
gettext
(
'All Time Zones'
),
selectOptions
:
fieldsData
.
time_zone
.
options
}],
persistChanges
:
true
})
}
...
...
@@ -169,6 +172,19 @@
}
];
// set TimeZoneField to listen to CountryField
getUserField
=
function
(
list
,
search
)
{
return
_
.
find
(
list
,
function
(
field
)
{
return
field
.
view
.
options
.
valueAttribute
===
search
;
}).
view
;
};
userFields
=
_
.
find
(
aboutSectionsData
,
function
(
section
)
{
return
section
.
title
===
gettext
(
'Basic Account Information'
);
}).
fields
;
timeZoneDropdownField
=
getUserField
(
userFields
,
'time_zone'
);
countryDropdownField
=
getUserField
(
userFields
,
'country'
);
timeZoneDropdownField
.
listenToCountryView
(
countryDropdownField
);
accountsSectionData
=
[
{
title
:
gettext
(
'Linked Accounts'
),
...
...
lms/static/js/student_account/views/account_settings_fields.js
View file @
3af769b4
...
...
@@ -77,6 +77,67 @@
}
}),
TimeZoneFieldView
:
FieldViews
.
DropdownFieldView
.
extend
({
fieldTemplate
:
field_dropdown_account_template
,
initialize
:
function
(
options
)
{
this
.
options
=
_
.
extend
({},
options
);
_
.
bindAll
(
this
,
'listenToCountryView'
,
'updateCountrySubheader'
,
'replaceOrAddGroupOption'
);
this
.
_super
(
options
);
// eslint-disable-line no-underscore-dangle
},
listenToCountryView
:
function
(
view
)
{
this
.
listenTo
(
view
.
model
,
'change:country'
,
this
.
updateCountrySubheader
);
},
updateCountrySubheader
:
function
(
user
)
{
var
view
=
this
;
$
.
ajax
({
type
:
'GET'
,
url
:
'/user_api/v1/preferences/time_zones/'
,
data
:
{
country_code
:
user
.
attributes
.
country
},
success
:
function
(
data
)
{
var
countryTimeZones
=
$
.
map
(
data
,
function
(
timeZoneInfo
)
{
return
[[
timeZoneInfo
.
time_zone
,
timeZoneInfo
.
description
]];
});
view
.
replaceOrAddGroupOption
(
'Country Time Zones'
,
countryTimeZones
);
view
.
render
();
}
});
},
updateValueInField
:
function
()
{
var
options
;
if
(
this
.
modelValue
())
{
options
=
[[
this
.
modelValue
(),
this
.
displayValue
(
this
.
modelValue
())]];
this
.
replaceOrAddGroupOption
(
'Currently Selected Time Zone'
,
options
);
}
this
.
_super
();
// eslint-disable-line no-underscore-dangle
},
replaceOrAddGroupOption
:
function
(
title
,
options
)
{
var
groupOption
=
{
groupTitle
:
gettext
(
title
),
selectOptions
:
options
};
var
index
=
_
.
findIndex
(
this
.
options
.
groupOptions
,
function
(
group
)
{
return
group
.
groupTitle
===
gettext
(
title
);
});
if
(
index
>=
0
)
{
this
.
options
.
groupOptions
[
index
]
=
groupOption
;
}
else
{
this
.
options
.
groupOptions
.
unshift
(
groupOption
);
}
}
}),
PasswordFieldView
:
FieldViews
.
LinkFieldView
.
extend
({
fieldType
:
'button'
,
fieldTemplate
:
field_link_account_template
,
...
...
lms/static/js/views/fields.js
View file @
3af769b4
...
...
@@ -369,7 +369,8 @@
},
initialize
:
function
(
options
)
{
_
.
bindAll
(
this
,
'render'
,
'optionForValue'
,
'fieldValue'
,
'displayValue'
,
'updateValueInField'
,
'saveValue'
);
_
.
bindAll
(
this
,
'render'
,
'optionForValue'
,
'fieldValue'
,
'displayValue'
,
'updateValueInField'
,
'saveValue'
,
'createGroupOptions'
);
this
.
_super
(
options
);
this
.
listenTo
(
this
.
model
,
'change:'
+
this
.
options
.
valueAttribute
,
this
.
updateValueInField
);
...
...
@@ -385,7 +386,7 @@
titleVisible
:
this
.
options
.
titleVisible
!==
undefined
?
this
.
options
.
titleVisible
:
true
,
iconName
:
this
.
options
.
iconName
,
showBlankOption
:
(
!
this
.
options
.
required
||
!
this
.
modelValueIsSet
()),
selectOptions
:
this
.
options
.
options
,
groupOptions
:
this
.
createGroupOptions
()
,
message
:
this
.
helpMessage
}));
this
.
delegateEvents
();
...
...
@@ -407,7 +408,17 @@
},
optionForValue
:
function
(
value
)
{
return
_
.
find
(
this
.
options
.
options
,
function
(
option
)
{
return
option
[
0
]
===
value
;
});
var
options
=
[];
if
(
_
.
isUndefined
(
this
.
options
.
groupOptions
))
{
return
_
.
find
(
this
.
options
.
options
,
function
(
option
)
{
return
option
[
0
]
===
value
;
});
}
else
{
_
.
each
(
this
.
options
.
groupOptions
,
function
(
groupOption
)
{
options
=
options
.
concat
(
groupOption
.
selectOptions
);
});
return
_
.
find
(
options
,
function
(
option
)
{
return
option
[
0
]
===
value
;
});
}
},
fieldValue
:
function
()
{
...
...
@@ -483,6 +494,14 @@
if
(
this
.
editable
!==
'never'
)
{
this
.
$
(
'.u-field-value select'
).
prop
(
'disabled'
,
disable
);
}
},
createGroupOptions
:
function
()
{
return
!
(
_
.
isUndefined
(
this
.
options
.
groupOptions
))
?
this
.
options
.
groupOptions
:
[{
groupTitle
:
null
,
selectOptions
:
this
.
options
.
options
}];
}
});
...
...
lms/templates/fields/field_dropdown.underscore
View file @
3af769b4
...
...
@@ -23,8 +23,13 @@
<% if (showBlankOption) { %>
<option value=""></option>
<% } %>
<% _.each(selectOptions, function(selectOption) { %>
<option value="<%- selectOption[0] %>"><%- selectOption[1] %></option>
<% _.each(groupOptions, function(groupOption) { %>
<% if (groupOption.groupTitle != null) { %>
<optgroup label="<%- groupOption.groupTitle %>">
<% } %>
<% _.each(groupOption.selectOptions, function(selectOption) { %>
<option value="<%- selectOption[0] %>"><%- selectOption[1] %></option>
<% }); %>
<% }); %>
</select>
<button class="u-field-value-display">
...
...
lms/templates/fields/field_dropdown_account.underscore
View file @
3af769b4
...
...
@@ -23,8 +23,13 @@
<% if (showBlankOption) { %>
<option value=""></option>
<% } %>
<% _.each(selectOptions, function(selectOption) { %>
<option value="<%- selectOption[0] %>"><%- selectOption[1] %></option>
<% _.each(groupOptions, function(groupOption) { %>
<% if (groupOption.groupTitle != null) { %>
<optgroup label="<%- groupOption.groupTitle %>">
<% } %>
<% _.each(groupOption.selectOptions, function(selectOption) { %>
<option value="<%- selectOption[0] %>"><%- selectOption[1] %></option>
<% }); %>
<% }); %>
</select>
<span class="icon-caret-down" aria-hidden="true"></span>
...
...
openedx/core/djangoapps/user_api/preferences/api.py
View file @
3af769b4
...
...
@@ -12,6 +12,7 @@ from django.db import IntegrityError
from
django.utils.translation
import
ugettext
as
_
from
django.utils.translation
import
ugettext_noop
from
openedx.core.lib.time_zone_utils
import
get_display_time_zone
from
pytz
import
common_timezones
,
common_timezones_set
,
country_timezones
from
student.models
import
User
,
UserProfile
from
request_cache
import
get_request_or_stub
...
...
@@ -422,8 +423,8 @@ def _create_preference_update_error(preference_key, preference_value, error):
def
get_country_time_zones
(
country_code
=
None
):
"""
Returns a
list of time zones commonly used in given country
or list of all time zones, if country code is None.
Returns a
sorted list of time zones commonly used in given
country
or list of all time zones, if country code is None.
Arguments:
country_code (str): ISO 3166-1 Alpha-2 country code
...
...
@@ -432,7 +433,34 @@ def get_country_time_zones(country_code=None):
CountryCodeError: the given country code is invalid
"""
if
country_code
is
None
:
return
common_timezones
return
_get_sorted_time_zone_list
(
common_timezones
)
if
country_code
.
upper
()
in
set
(
countries
.
alt_codes
):
return
country_timezones
(
country_code
)
return
_get_sorted_time_zone_list
(
country_timezones
(
country_code
)
)
raise
CountryCodeError
def
_get_sorted_time_zone_list
(
time_zone_list
):
"""
Returns a list of time zone dictionaries sorted by their display values
:param time_zone_list (list): pytz time zone list
"""
return
sorted
(
[
_get_time_zone_dictionary
(
time_zone
)
for
time_zone
in
time_zone_list
],
key
=
lambda
tz_dict
:
tz_dict
[
'description'
]
)
def
_get_time_zone_dictionary
(
time_zone_name
):
"""
Returns a dictionary of time zone information:
* time_zone: Name of pytz time zone
* description: Display version of time zone [e.g. US/Pacific (PST, UTC-0800)]
:param time_zone_name (str): Name of pytz time zone
"""
return
{
'time_zone'
:
time_zone_name
,
'description'
:
get_display_time_zone
(
time_zone_name
),
}
openedx/core/djangoapps/user_api/preferences/tests/test_api.py
View file @
3af769b4
...
...
@@ -15,6 +15,7 @@ from django.test import TestCase
from
django.test.utils
import
override_settings
from
dateutil.parser
import
parse
as
parse_datetime
from
openedx.core.lib.time_zone_utils
import
get_display_time_zone
from
student.tests.factories
import
UserFactory
from
xmodule.modulestore.tests.django_utils
import
ModuleStoreTestCase
...
...
@@ -445,13 +446,20 @@ class CountryTimeZoneTest(TestCase):
Test cases to validate country code api functionality
"""
@ddt.data
((
'
NZ'
,
[
'Pacific/Auckland'
,
'Pacific/Chatham
'
]),
(
None
,
common_timezones
))
@ddt.data
((
'
ES'
,
[
'Africa/Ceuta'
,
'Atlantic/Canary'
,
'Europe/Madrid
'
]),
(
None
,
common_timezones
[:
10
]
))
@ddt.unpack
def
test_get_country_time_zones
(
self
,
country_code
,
expected_time_zones
):
"""Verify that list of common country time zones are returned"""
country_time_zones
=
get_country_time_zones
(
country_code
)
self
.
assertEqual
(
country_time_zones
,
expected_time_zones
)
"""Verify that list of common country time zones dictionaries is returned"""
expected_dict
=
[
{
'time_zone'
:
time_zone
,
'description'
:
get_display_time_zone
(
time_zone
)
}
for
time_zone
in
expected_time_zones
]
country_time_zones_dicts
=
get_country_time_zones
(
country_code
)[:
10
]
self
.
assertEqual
(
country_time_zones_dicts
,
expected_dict
)
def
test_country_code_errors
(
self
):
"""Verify that country code error is raised for invalid country code"""
...
...
openedx/core/djangoapps/user_api/serializers.py
View file @
3af769b4
...
...
@@ -4,7 +4,6 @@ Django REST Framework serializers for the User API application
from
django.contrib.auth.models
import
User
from
rest_framework
import
serializers
from
openedx.core.lib.time_zone_utils
import
get_display_time_zone
from
student.models
import
UserProfile
from
.models
import
UserPreference
...
...
@@ -89,17 +88,5 @@ class CountryTimeZoneSerializer(serializers.Serializer): # pylint: disable=abst
"""
Serializer that generates a list of common time zones for a country
"""
time_zone
=
serializers
.
SerializerMethodField
()
description
=
serializers
.
SerializerMethodField
()
def
get_time_zone
(
self
,
time_zone_name
):
"""
Returns inputted time zone name
"""
return
time_zone_name
def
get_description
(
self
,
time_zone_name
):
"""
Returns the display version of time zone [e.g. US/Pacific (PST, UTC-0800)]
"""
return
get_display_time_zone
(
time_zone_name
)
time_zone
=
serializers
.
CharField
()
description
=
serializers
.
CharField
()
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