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
635c0443
Commit
635c0443
authored
Aug 11, 2016
by
Kevin Kim
Committed by
GitHub
Aug 11, 2016
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #13140 from edx/kkim/country_tz_pref
Time Zone Field Changes Based on Country
parents
3bbf30fe
3af769b4
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 @
635c0443
...
...
@@ -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 @
635c0443
...
...
@@ -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 @
635c0443
...
...
@@ -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 @
635c0443
...
...
@@ -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 @
635c0443
...
...
@@ -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 @
635c0443
...
...
@@ -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 @
635c0443
...
...
@@ -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 @
635c0443
...
...
@@ -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 @
635c0443
...
...
@@ -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 @
635c0443
...
...
@@ -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 @
635c0443
...
...
@@ -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