Commit 50141ffe by Gregory Martin

implement dateutils from edx-ui-toolkit

parent f70b111d
...@@ -4,7 +4,7 @@ module.exports = function(config) { ...@@ -4,7 +4,7 @@ module.exports = function(config) {
config.set({ config.set({
// base path that will be used to resolve all patterns (eg. files, exclude) // base path that will be used to resolve all patterns (eg. files, exclude)
basePath: 'openassessment/xblock/static/js', basePath: 'openassessment/xblock/static/',
plugins: [ plugins: [
...@@ -25,37 +25,56 @@ module.exports = function(config) { ...@@ -25,37 +25,56 @@ module.exports = function(config) {
// list of files / patterns to load in the browser // list of files / patterns to load in the browser
files: [ files: [
'lib/jquery.min.js', 'js/lib/jquery.min.js',
'lib/*.js', 'js/lib/codemirror.js',
'src/oa_shared.js', 'js/lib/jquery.timepicker.min.js',
'src/*.js', 'js/lib/jquery-ui-1.10.4.min.js',
'src/lms/*.js', 'js/lib/underscore-min.js',
'src/studio/*.js', '../../../node_modules/requirejs/require.js',
'spec/test_shared.js', '../../../require-config.js',
'spec/*.js', {
'spec/lms/*.js', pattern: '../../../node_modules/moment-timezone/builds/moment-timezone-with-data.min.js',
'spec/studio/*.js', served: true, included: false
},
{
pattern: '../../../node_modules/moment/min/moment-with-locales.min.js',
served: true, included: false
},
{
pattern: '../../../node_modules/edx-ui-toolkit/src/js/utils/date-utils.js',
served: true, included: false
},
{
pattern: '../../../node_modules/edx-ui-toolkit/src/js/utils/string-utils.js',
served: true, included: false
},
//
'js/src/oa_shared.js',
'js/src/*.js',
'js/src/lms/*.js',
'js/src/studio/*.js',
'js/spec/test_shared.js',
'js/spec/*.js',
'js/spec/lms/*.js',
'js/spec/studio/*.js',
// fixtures // fixtures
{ {
pattern: 'fixtures/*.html', pattern: 'js/fixtures/*.html',
served: true, included: false served: true, included: false
} }
], ],
// list of files to exclude // list of files to exclude
exclude: [ exclude: [
'src/design*.js' 'js/src/design*.js'
], ],
// preprocess matching files before serving them to the browser // preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: { preprocessors: {
'src/*.js': 'coverage', 'js/src/*.js': 'coverage',
'src/lms/*.js': 'coverage', 'js/src/lms/*.js': 'coverage',
'src/studio/*.js': 'coverage' 'js/src/studio/*.js': 'coverage'
}, },
......
...@@ -4,4 +4,4 @@ so import the tasks we want the workers to implement. ...@@ -4,4 +4,4 @@ so import the tasks we want the workers to implement.
""" """
# pylint:disable=W0611 # pylint:disable=W0611
from .worker.training import train_classifiers, reschedule_training_tasks from .worker.training import train_classifiers, reschedule_training_tasks
from .worker.grading import grade_essay, reschedule_grading_tasks from .worker.grading import grade_essay, reschedule_grading_tasks
\ No newline at end of file
...@@ -20,13 +20,17 @@ ...@@ -20,13 +20,17 @@
<span class="step__label">{% trans "Assess Peers" %}</span> <span class="step__label">{% trans "Assess Peers" %}</span>
{% if peer_start %} {% if peer_start %}
<span class="step__deadline"> <span class="step__deadline">
{# Translators: This string displays a date to the user, then tells them the time until that date. Example: "available August 13th, 2014 (in 5 days and 45 minutes)" #} {# Translators: This string displays a date to the user, then tells them the time until that date. Example: "available August 13th, 2014 00:00 UTC (in 5 days and 45 minutes)" #}
{% blocktrans with start_date=peer_start|timezone:time_zone|date:"N j, Y H:i e" time_until=peer_start|timeuntil %}available <span class="date">{{ start_date }} (in {{ time_until }})</span>{% endblocktrans %} {% blocktrans with start_date=peer_start|date:"Y-m-d H:i" time_until=peer_start|timeuntil %}
<span class="date ora-datetime" data-datetime="{{ start_date }}" data-string="available {date} (in {{ time_until }})" data-timezone="{{ user_timezone }}" data-language="{{ user_language }}"></span>
{% endblocktrans %}
</span> </span>
{% elif peer_due %} {% elif peer_due %}
<span class="step__deadline"> <span class="step__deadline">
{# Translators: This string displays a date to the user, then tells them the time until that date. Example: "due August 13th, 2014 (in 5 days and 45 minutes)" #} {# Translators: This string displays a date to the user, then tells them the time until that date. Example: "due August 13th, 2014 00:00 UTC (in 5 days and 45 minutes)" #}
{% blocktrans with due_date=peer_due|timezone:time_zone|date:"N j, Y H:i e" time_until=peer_due|timeuntil %}due <span class="date">{{ due_date }} (in {{ time_until }})</span>{% endblocktrans %} {% blocktrans with due_date=peer_due|date:"Y-m-d H:i" time_until=peer_due|timeuntil %}
<span class="date ora-datetime" data-datetime="{{ due_date }}" data-string="due {date} (in {{ time_until }})" data-timezone="{{ user_timezone }}" data-language="{{ user_language }}"></span>
{% endblocktrans %}
</span> </span>
{% endif %} {% endif %}
</span> </span>
......
...@@ -17,16 +17,20 @@ ...@@ -17,16 +17,20 @@
{% if submission_start %} {% if submission_start %}
<span class="step__deadline"> <span class="step__deadline">
{# Translators: This string displays a date to the user, then tells them the time until that date. Example: "available August 13th, 2014 (in 5 days and 45 minutes)" #} {# Translators: This string displays a date to the user, then tells them the time until that date. Example: "available August 13th, 2014 (in 5 days and 45 minutes)" #}
{% blocktrans with start_date=submission_start|timezone:time_zone|date:"N j, Y H:i e" time_until=submission_start|timeuntil %}available <span class="date">{{ start_date }} (in {{ time_until }})</span>{% endblocktrans %} {% blocktrans with start_date=submission_start|date:"Y-m-d H:i" time_until=submission_start|timeuntil %}
<span class="date ora-datetime" data-datetime="{{ start_date }}" data-string="available {date} (in {{ time_until }})" data-timezone="{{ user_timezone }}" data-language="{{ user_language }}"></span>
{% endblocktrans %}
</span> </span>
{% elif submission_due %} {% elif submission_due %}
<span class="step__deadline"> <span class="step__deadline">
{# Translators: This string displays a date to the user, then tells them the time until that date. Example: "due August 13th, 2014 (in 5 days and 45 minutes)" #} {# Translators: This string displays a date to the user, then tells them the time until that date. Example: "due August 13th, 2014 (in 5 days and 45 minutes)" #}
{% blocktrans with due_date=submission_due|timezone:time_zone|date:"N j, Y H:i e" time_until=submission_due|timeuntil %}due <span class="date">{{ due_date }} (in {{ time_until }})</span>{% endblocktrans %} {% blocktrans with due_date=submission_due|date:"Y-m-d H:i" time_until=submission_due|timeuntil %}
<span class="date ora-datetime" data-datetime="{{ due_date }}" data-string="due {date} (in {{ time_until }})" data-timezone="{{ user_timezone }}" data-language="{{ user_language }}"></span>
{% endblocktrans %}
</span>
{% endif %} {% endif %}
</span> </span>
</button> </button>
{% block title %} {% block title %}
<span class="step__status"> <span class="step__status">
<span class="step__status__value"> <span class="step__status__value">
...@@ -44,7 +48,10 @@ ...@@ -44,7 +48,10 @@
<p> <p>
{% trans "Enter your response to the question." %} {% trans "Enter your response to the question." %}
{% if submission_due %} {% if submission_due %}
{% trans "You can save your progress and return to complete your response at any time before the due date" %} (<span class="step__deadline"><span class="date">{{ submission_due|timezone:time_zone|date:"l, N j, Y H:i e" }}</span></span>). {% trans "You can save your progress and return to complete your response at any time before the due date" %}
(<span class="step__deadline">
<span class="date ora-datetime" data-datetime="{{ submission_due|date:'Y-m-d H:i' }}" data-timezone="{{ user_timezone }}" data-format="longDateTime" data-language="{{ user_language }}"></span>
</span>).
{% else %} {% else %}
{% trans "You can save your progress and return to complete your response at any time." %} {% trans "You can save your progress and return to complete your response at any time." %}
{% endif %} {% endif %}
......
...@@ -32,12 +32,12 @@ ...@@ -32,12 +32,12 @@
{% trans "Your submission was cancelled. " %} {% trans "Your submission was cancelled. " %}
<p> <p>
{% if workflow_cancellation.cancelled_by %} {% if workflow_cancellation.cancelled_by %}
{% blocktrans with removed_datetime=workflow_cancellation.cancelled_at|timezone:time_zone|date:"N j, Y H:i e" removed_by_username=workflow_cancellation.cancelled_by %} {% blocktrans with removed_datetime=workflow_cancellation.cancelled_at|timezone:"UTC"|date:"Y-m-d H:i e" removed_by_username=workflow_cancellation.cancelled_by %}
Your submission has been cancelled by {{ removed_by_username }} on {{ removed_datetime }} Your submission has been cancelled by {{ removed_by_username }} on {{ removed_datetime }}
{% endblocktrans %} {% endblocktrans %}
{% else %} {% else %}
{% blocktrans with removed_datetime=workflow_cancellation.cancelled_at|timezone:time_zone|date:"N j, Y H:i e" %} {% blocktrans with removed_datetime=workflow_cancellation.cancelled_at|timezone:"UTC"|date:"Y-m-d H:i e" %}
Your submission was cancelled on {{ removed_datetime }} Your submission was cancelled on {{ removed_datetime }}
{% endblocktrans %} {% endblocktrans %}
{% endif %} {% endif %}
</p> </p>
......
...@@ -18,12 +18,16 @@ ...@@ -18,12 +18,16 @@
{% if self_start %} {% if self_start %}
<span class="step__deadline"> <span class="step__deadline">
{# Translators: This string displays a date to the user, then tells them the time until that date. Example: "available August 13th, 2014 (in 5 days and 45 minutes)" #} {# Translators: This string displays a date to the user, then tells them the time until that date. Example: "available August 13th, 2014 (in 5 days and 45 minutes)" #}
{% blocktrans with start_date=self_start|timezone:time_zone|date:"N j, Y H:i e" time_until=self_start|timeuntil %}available <span class="date">{{ start_date }} (in {{ time_until }})</span>{% endblocktrans %} {% blocktrans with start_date=self_start|date:"Y-m-d H:i" time_until=self_start|timeuntil %}
<span class="date ora-datetime" data-datetime="{{ start_date }}" data-string="available {date} (in {{ time_until }})" data-timezone="{{ user_timezone }}" data-language="{{ user_language }}"></span>
{% endblocktrans %}
</span> </span>
{% elif self_due %} {% elif self_due %}
<span class="step__deadline"> <span class="step__deadline">
{# Translators: This string displays a date to the user, then tells them the time until that date. Example: "due August 13th, 2014 (in 5 days and 45 minutes)" #} {# Translators: This string displays a date to the user, then tells them the time until that date. Example: "due August 13th, 2014 (in 5 days and 45 minutes)" #}
{% blocktrans with due_date=self_due|timezone:time_zone|date:"N j, Y H:i e" time_until=self_due|timeuntil %}due <span class="date">{{ due_date }} (in {{ time_until }})</span>{% endblocktrans %} {% blocktrans with due_date=self_due|date:"Y-m-d H:i" time_until=self_due|timeuntil %}
<span class="date ora-datetime" data-datetime="{{ due_date }}" data-string="due {date} (in {{ time_until }})" data-timezone="{{ user_timezone }}" data-language="{{ user_language }}"></span>
{% endblocktrans %}
</span> </span>
{% endif %} {% endif %}
</span> </span>
......
...@@ -26,11 +26,11 @@ ...@@ -26,11 +26,11 @@
{% if workflow_cancellation %} {% if workflow_cancellation %}
<p> <p>
{% if workflow_cancellation.cancelled_by %} {% if workflow_cancellation.cancelled_by %}
{% blocktrans with removed_by_username=workflow_cancellation.cancelled_by removed_datetime=workflow_cancellation.cancelled_at|timezone:time_zone|date:"F j, Y H:i e" %} {% blocktrans with removed_by_username=workflow_cancellation.cancelled_by removed_datetime=workflow_cancellation.cancelled_at|timezone:"UTC"|date:"Y-m-d H:i e" %}
Learner submission removed by {{ removed_by_username }} on {{ removed_datetime }} Learner submission removed by {{ removed_by_username }} on {{ removed_datetime }}
{% endblocktrans %} {% endblocktrans %}
{% else %} {% else %}
{% blocktrans with removed_datetime=workflow_cancellation.cancelled_at|timezone:time_zone|date:"F j, Y H:i e" %} {% blocktrans with removed_datetime=workflow_cancellation.cancelled_at|timezone:"UTC"|date:"Y-m-d H:i e" %}
Learner submission removed on {{ removed_datetime }} Learner submission removed on {{ removed_datetime }}
{% endblocktrans %} {% endblocktrans %}
{% endif %} {% endif %}
......
...@@ -17,12 +17,16 @@ ...@@ -17,12 +17,16 @@
{% if training_start %} {% if training_start %}
<span class="step__deadline"> <span class="step__deadline">
{# Translators: This string displays a date to the user, then tells them the time until that date. Example: "available August 13th, 2014 (in 5 days and 45 minutes)" #} {# Translators: This string displays a date to the user, then tells them the time until that date. Example: "available August 13th, 2014 (in 5 days and 45 minutes)" #}
{% blocktrans with start_date=training_start|timezone:time_zone|date:"N j, Y H:i e" time_until=training_start|timeuntil %}available <span class="date">{{ start_date }} (in {{ time_until }})</span>{% endblocktrans %} {% blocktrans with start_date=training_start|date:"Y-m-d H:i" time_until=training_start|timeuntil %}
<span class="date ora-datetime" data-datetime="{{ start_date }}" data-string="available {date} (in {{ time_until }})" data-timezone="{{ user_timezone }}" data-language="{{ user_language }}"></span>
{% endblocktrans %}
</span> </span>
{% elif training_due %} {% elif training_due %}
<span class="step__deadline"> <span class="step__deadline">
{# Translators: This string displays a date to the user, then tells them the time until that date. Example: "due August 13th, 2014 (in 5 days and 45 minutes)" #} {# Translators: This string displays a date to the user, then tells them the time until that date. Example: "due August 13th, 2014 (in 5 days and 45 minutes)" #}
{% blocktrans with due_date=training_due|timezone:time_zone|date:"N j, Y H:i e" time_until=training_due|timeuntil %}due <span class="date">{{ due_date }} (in {{ time_until }})</span>{% endblocktrans %} {% blocktrans with due_date=training_due|date:"Y-m-d H:i" time_until=training_due|timeuntil %}
<span class="date ora-datetime" data-datetime="{{ due_date }}" data-string="due {date} (in {{ time_until }})" data-timezone="{{ user_timezone }}" data-language="{{ user_language }}"></span>
{% endblocktrans %}
</span> </span>
</span> </span>
{% endif %} {% endif %}
......
"""
The Peer Assessment Mixin for all Peer Functionality.
"""
import logging import logging
from webob import Response from webob import Response
...@@ -10,7 +15,8 @@ from openassessment.assessment.errors import ( ...@@ -10,7 +15,8 @@ from openassessment.assessment.errors import (
from openassessment.workflow.errors import AssessmentWorkflowError from openassessment.workflow.errors import AssessmentWorkflowError
from openassessment.xblock.defaults import DEFAULT_RUBRIC_FEEDBACK_TEXT from openassessment.xblock.defaults import DEFAULT_RUBRIC_FEEDBACK_TEXT
from .data_conversion import create_rubric_dict from .data_conversion import create_rubric_dict
from .resolve_dates import DISTANT_FUTURE, get_current_time_zone from .resolve_dates import DISTANT_FUTURE
from .user_data import get_user_preferences
from .data_conversion import clean_criterion_feedback, create_submission_dict, verify_assessment_parameters from .data_conversion import clean_criterion_feedback, create_submission_dict, verify_assessment_parameters
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -161,12 +167,13 @@ class PeerAssessmentMixin(object): ...@@ -161,12 +167,13 @@ class PeerAssessmentMixin(object):
path = 'openassessmentblock/peer/oa_peer_unavailable.html' path = 'openassessmentblock/peer/oa_peer_unavailable.html'
finished = False finished = False
problem_closed, reason, start_date, due_date = self.is_closed(step="peer-assessment") problem_closed, reason, start_date, due_date = self.is_closed(step="peer-assessment")
user_service = self.runtime.service(self, 'user') user_preferences = get_user_preferences(self.runtime.service(self, 'user'))
context_dict = { context_dict = {
"rubric_criteria": self.rubric_criteria_with_labels, "rubric_criteria": self.rubric_criteria_with_labels,
"allow_latex": self.allow_latex, "allow_latex": self.allow_latex,
"time_zone": get_current_time_zone(user_service), "user_timezone": user_preferences['user_timezone'],
"user_language": user_preferences['user_language'],
"xblock_id": self.get_xblock_id(), "xblock_id": self.get_xblock_id(),
} }
......
...@@ -24,18 +24,6 @@ DISTANT_PAST = dt.datetime(dt.MINYEAR, 1, 1, tzinfo=pytz.utc) ...@@ -24,18 +24,6 @@ DISTANT_PAST = dt.datetime(dt.MINYEAR, 1, 1, tzinfo=pytz.utc)
DISTANT_FUTURE = dt.datetime(dt.MAXYEAR, 1, 1, tzinfo=pytz.utc) DISTANT_FUTURE = dt.datetime(dt.MAXYEAR, 1, 1, tzinfo=pytz.utc)
def get_current_time_zone(user_service):
"""
Returns the preferred time zone for the current user, if specified, or UTC if not
:param user_service: XblockUserService
"""
user_preferences = user_service.get_current_user().opt_attrs.get('edx-platform.user_preferences')
if user_preferences is None:
return pytz.utc
return pytz.timezone(user_preferences.get('time_zone', 'utc'))
def _parse_date(value, _): def _parse_date(value, _):
""" """
Parse an ISO formatted datestring into a datetime object with timezone set to UTC. Parse an ISO formatted datestring into a datetime object with timezone set to UTC.
......
...@@ -6,7 +6,8 @@ from webob import Response ...@@ -6,7 +6,8 @@ from webob import Response
from openassessment.assessment.api import self as self_api from openassessment.assessment.api import self as self_api
from openassessment.workflow import api as workflow_api from openassessment.workflow import api as workflow_api
from submissions import api as submission_api from submissions import api as submission_api
from .resolve_dates import DISTANT_FUTURE, get_current_time_zone from .resolve_dates import DISTANT_FUTURE
from .user_data import get_user_preferences
from .data_conversion import (clean_criterion_feedback, create_submission_dict, from .data_conversion import (clean_criterion_feedback, create_submission_dict,
create_rubric_dict, verify_assessment_parameters) create_rubric_dict, verify_assessment_parameters)
...@@ -54,12 +55,13 @@ class SelfAssessmentMixin(object): ...@@ -54,12 +55,13 @@ class SelfAssessmentMixin(object):
path = 'openassessmentblock/self/oa_self_unavailable.html' path = 'openassessmentblock/self/oa_self_unavailable.html'
problem_closed, reason, start_date, due_date = self.is_closed(step="self-assessment") problem_closed, reason, start_date, due_date = self.is_closed(step="self-assessment")
user_service = self.runtime.service(self, 'user') user_preferences = get_user_preferences(self.runtime.service(self, 'user'))
context = { context = {
'allow_latex': self.allow_latex, 'allow_latex': self.allow_latex,
"xblock_id": self.get_xblock_id(), "xblock_id": self.get_xblock_id(),
'time_zone': get_current_time_zone(user_service) 'user_timezone': user_preferences['user_timezone'],
'user_language': user_preferences['user_language']
} }
# We display the due date whether the problem is open or closed. # We display the due date whether the problem is open or closed.
......
...@@ -24,7 +24,7 @@ from openassessment.assessment.api import self as self_api ...@@ -24,7 +24,7 @@ from openassessment.assessment.api import self as self_api
from openassessment.assessment.api import ai as ai_api from openassessment.assessment.api import ai as ai_api
from openassessment.workflow import api as workflow_api from openassessment.workflow import api as workflow_api
from openassessment.assessment.api import staff as staff_api from openassessment.assessment.api import staff as staff_api
from .resolve_dates import get_current_time_zone from .user_data import get_user_preferences
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -319,11 +319,14 @@ class StaffAreaMixin(object): ...@@ -319,11 +319,14 @@ class StaffAreaMixin(object):
Returns: Returns:
A context dict for rendering a student submission and associated rubric (for staff grading). A context dict for rendering a student submission and associated rubric (for staff grading).
""" """
user_preferences = get_user_preferences(self.runtime.service(self, 'user')) # localize for staff user
context = { context = {
'submission': create_submission_dict(submission, self.prompts) if submission else None, 'submission': create_submission_dict(submission, self.prompts) if submission else None,
'rubric_criteria': copy.deepcopy(self.rubric_criteria_with_labels), 'rubric_criteria': copy.deepcopy(self.rubric_criteria_with_labels),
'student_username': student_username, 'student_username': student_username,
'time_zone': get_current_time_zone(self.runtime.service(self, 'user')), # localize for staff user 'user_timezone': user_preferences['user_timezone'],
'user_language': user_preferences['user_language']
} }
if submission: if submission:
......
...@@ -893,7 +893,8 @@ ...@@ -893,7 +893,8 @@
"cancelled_at": "2015-10-01T04:53", "cancelled_at": "2015-10-01T04:53",
"comments": "Cancelled!" "comments": "Cancelled!"
}, },
"time_zone": "utc" "user_timezone": "utc",
"user_language": "en"
}, },
"output": "oa_staff_cancelled_submission.html" "output": "oa_staff_cancelled_submission.html"
}, },
...@@ -1195,5 +1196,22 @@ ...@@ -1195,5 +1196,22 @@
"student_username": "mock_user_2" "student_username": "mock_user_2"
}, },
"output": "oa_staff_grade_learners_assessment_2.html" "output": "oa_staff_grade_learners_assessment_2.html"
},
{
"template": "openassessmentblock/response/oa_response.html",
"context": {
"saved_response": {
"answer": {
"parts": [
{ "text": "", "prompt": { "description": "Prompt 1" }},
{ "text": "", "prompt": { "description": "Prompt 2" }}
]
}
},
"save_status": "This response has not been saved.",
"submit_enabled": true,
"submission_due": "2020-01-01T00:00"
},
"output": "oa_response_date.html"
} }
] ]
This source diff could not be displayed because it is too large. You can view the blob instead.
/**
Tests for OA dateutil factory.
**/
describe('OpenAssessment.DateTimeFactory', function() {
beforeEach(function() {
// Load the DOM fixture
loadFixtures('oa_response_date.html');
});
describe('apply', function() {
it('has the correct HTML elements', function() {
var timeElement = $('.step__title').get(0);
var datetimeFactory = new OpenAssessment.DateTimeFactory(timeElement);
datetimeFactory.apply();
$('.ora-datetime', timeElement).each(function() {
var self = this;
expect($(self).data('datetime')).toBe('2019-12-31 19:00');
expect($(self).data('string')).toContain('due {date}');
});
});
});
describe('determineContext', function() {
it('generates a context dict', function() {
var timeElement = $('.step__title').get(0);
var datetimeFactory = new OpenAssessment.DateTimeFactory(timeElement);
$('.ora-datetime', timeElement).each(function() {
var self = this;
$(self).attr('data-language', 'en');
$(self).attr('data-timezone', 'America/Los_Angeles');
});
$('.ora-datetime', timeElement).each(function() {
var el = this;
var testContext = datetimeFactory.determineContext($(el));
expect(testContext['datetime']).toBe('2019-12-31 19:00');
expect(testContext['timezone']).toBe('America/Los_Angeles');
expect(testContext['language']).toBe('en');
expect(testContext['format']).toBe('');
})
})
});
describe('determineDateToken', function() {
it('defaults', function() {
var timeElement = $('.step__title').get(0);
var datetimeFactory = new OpenAssessment.DateTimeFactory(timeElement);
$('.ora-datetime', timeElement).each(function() {
var el = this;
var testDateToken = datetimeFactory.determineDateToken($(el));
expect(testDateToken).toBe('date');
});
$('.ora-datetime', timeElement).each(function() {
var self = this;
$(self).attr('data-datetoken', 'TEST');
});
$('.ora-datetime', timeElement).each(function() {
var el = this;
var testDateToken = datetimeFactory.determineDateToken($(el));
expect(testDateToken).toBe('TEST');
})
})
});
describe('isValid', function() {
it('checks a valid variable', function() {
var timeElement = $('.step__title').get(0);
var datetimeFactory = new OpenAssessment.DateTimeFactory(timeElement);
var testDict = {
'Invalid date': false,
'invalid date': true,
'': false,
1: true
};
Object.keys(testDict).forEach(function(key) {
expect(datetimeFactory.isValid(key)).toEqual(testDict[key]);
});
})
});
});
...@@ -466,7 +466,7 @@ describe('OpenAssessment.StaffAreaView', function() { ...@@ -466,7 +466,7 @@ describe('OpenAssessment.StaffAreaView', function() {
// Verify that the student view reflects the cancellation // Verify that the student view reflects the cancellation
expect($($('.staff-info__student__response p', staffArea.element)[0]).text().trim()).toBe( expect($($('.staff-info__student__response p', staffArea.element)[0]).text().trim()).toBe(
'Learner submission removed by staff on October 1, 2015 04:53 UTC' 'Learner submission removed by staff on 2015-10-01 04:53 UTC'
); );
expect($($('.staff-info__student__response p', staffArea.element)[1]).text().trim()).toBe( expect($($('.staff-info__student__response p', staffArea.element)[1]).text().trim()).toBe(
'Comments: Cancelled!' 'Comments: Cancelled!'
......
...@@ -3,5 +3,5 @@ Common test configuration, loaded before any of the spec files. ...@@ -3,5 +3,5 @@ Common test configuration, loaded before any of the spec files.
**/ **/
// Set the fixture path // Set the fixture path
jasmine.getFixtures().fixturesPath = 'base/fixtures'; jasmine.getFixtures().fixturesPath = 'base/js/fixtures';
...@@ -223,6 +223,7 @@ OpenAssessment.BaseView.prototype = { ...@@ -223,6 +223,7 @@ OpenAssessment.BaseView.prototype = {
this.selfView.load(usageID); this.selfView.load(usageID);
this.gradeView.load(usageID); this.gradeView.load(usageID);
this.leaderboardView.load(usageID); this.leaderboardView.load(usageID);
/** /**
this.messageView.load() is intentionally omitted. this.messageView.load() is intentionally omitted.
Because of the asynchronous loading, there is no way to tell (from the perspective of the Because of the asynchronous loading, there is no way to tell (from the perspective of the
...@@ -383,4 +384,5 @@ function OpenAssessmentBlock(runtime, element, data) { ...@@ -383,4 +384,5 @@ function OpenAssessmentBlock(runtime, element, data) {
var server = new OpenAssessment.Server(runtime, element); var server = new OpenAssessment.Server(runtime, element);
var view = new OpenAssessment.BaseView(runtime, element, server, data); var view = new OpenAssessment.BaseView(runtime, element, server, data);
view.load(); view.load();
} }
/**
*
* A helper function to utilize DateUtils.
**/
OpenAssessment.DateTimeFactory = function(element) {
this.element = element;
};
OpenAssessment.DateTimeFactory.prototype = {
apply: function() {
var dtFactory = this;
$('.ora-datetime', this.element).each(function() {
dtFactory.elementApply($(this));
});
},
determineContext: function(el) {
var context;
context = {
datetime: el.data('datetime'),
timezone: el.data('timezone'),
language: el.data('language'),
format: ''
};
return context;
},
determineDateToken: function(el) {
var dtFactory = this;
var dateToken = 'date';
if (dtFactory.isValid(el.data('datetoken'))) {
dateToken = el.data('datetoken');
}
return dateToken;
},
elementApply: function(el) {
var dtFactory = this;
(function(require) {
require([
'jquery',
'edx-ui-toolkit/js/utils/date-utils',
'edx-ui-toolkit/js/utils/string-utils'
], function($, DateUtils, StringUtils) {
var context;
var localTimeString;
var displayDatetime;
var interpolateDict = {};
if (dtFactory.isValid(el.data('datetime'))) {
context = dtFactory.determineContext(el);
if (dtFactory.isValid(el.data('format'))) {
context.format = DateUtils.dateFormatEnum[el.data('format')];
}
localTimeString = DateUtils.localize(context);
interpolateDict[dtFactory.determineDateToken(el)] = localTimeString;
if (dtFactory.isValid(el.data('string'))) {
displayDatetime = StringUtils.interpolate(
el.data('string'),
interpolateDict
);
} else {
displayDatetime = localTimeString;
}
} else {
displayDatetime = StringUtils.interpolate(
el.data('string'),
interpolateDict
);
}
el.text(displayDatetime);
}
);
}).call(this, require || RequireJS.require);
},
isValid: function(candidateVariable) {
return candidateVariable !== undefined &&
candidateVariable !== '' &&
candidateVariable !== 'Invalid date' &&
candidateVariable !== 'None';
}
};
...@@ -12,6 +12,7 @@ OpenAssessment.GradeView = function(element, server, baseView) { ...@@ -12,6 +12,7 @@ OpenAssessment.GradeView = function(element, server, baseView) {
this.baseView = baseView; this.baseView = baseView;
this.announceStatus = false; this.announceStatus = false;
this.isRendering = false; this.isRendering = false;
this.dateFactory = new OpenAssessment.DateTimeFactory(this.element);
}; };
OpenAssessment.GradeView.prototype = { OpenAssessment.GradeView.prototype = {
...@@ -33,6 +34,7 @@ OpenAssessment.GradeView.prototype = { ...@@ -33,6 +34,7 @@ OpenAssessment.GradeView.prototype = {
view.installHandlers(); view.installHandlers();
view.baseView.announceStatusChangeToSRandFocus(stepID, usageID, true, view, focusID); view.baseView.announceStatusChangeToSRandFocus(stepID, usageID, true, view, focusID);
view.dateFactory.apply();
} }
).fail(function(errMsg) { ).fail(function(errMsg) {
baseView.showLoadError('grade', errMsg); baseView.showLoadError('grade', errMsg);
......
...@@ -16,6 +16,8 @@ OpenAssessment.PeerView = function(element, server, baseView) { ...@@ -16,6 +16,8 @@ OpenAssessment.PeerView = function(element, server, baseView) {
this.rubric = null; this.rubric = null;
this.isRendering = false; this.isRendering = false;
this.announceStatus = false; this.announceStatus = false;
this.dateFactory = new OpenAssessment.DateTimeFactory(this.element);
}; };
OpenAssessment.PeerView.prototype = { OpenAssessment.PeerView.prototype = {
...@@ -42,6 +44,7 @@ OpenAssessment.PeerView.prototype = { ...@@ -42,6 +44,7 @@ OpenAssessment.PeerView.prototype = {
view.baseView.announceStatusChangeToSRandFocus(stepID, usageID, false, view, focusID); view.baseView.announceStatusChangeToSRandFocus(stepID, usageID, false, view, focusID);
view.announceStatus = false; view.announceStatus = false;
view.dateFactory.apply();
} }
).fail(function() { ).fail(function() {
view.baseView.showLoadError('peer-assessment'); view.baseView.showLoadError('peer-assessment');
......
...@@ -26,6 +26,7 @@ OpenAssessment.ResponseView = function(element, server, fileUploader, baseView, ...@@ -26,6 +26,7 @@ OpenAssessment.ResponseView = function(element, server, fileUploader, baseView,
this.fileUploaded = false; this.fileUploaded = false;
this.announceStatus = false; this.announceStatus = false;
this.isRendering = false; this.isRendering = false;
this.dateFactory = new OpenAssessment.DateTimeFactory(this.element);
}; };
OpenAssessment.ResponseView.prototype = { OpenAssessment.ResponseView.prototype = {
...@@ -58,11 +59,10 @@ OpenAssessment.ResponseView.prototype = { ...@@ -58,11 +59,10 @@ OpenAssessment.ResponseView.prototype = {
view.server.renderLatex($(stepID, view.element)); view.server.renderLatex($(stepID, view.element));
view.installHandlers(); view.installHandlers();
view.setAutoSaveEnabled(true); view.setAutoSaveEnabled(true);
view.isRendering = false; view.isRendering = false;
view.baseView.announceStatusChangeToSRandFocus(stepID, usageID, false, view, focusID); view.baseView.announceStatusChangeToSRandFocus(stepID, usageID, false, view, focusID);
view.announceStatus = false; view.announceStatus = false;
view.dateFactory.apply();
} }
).fail(function() { ).fail(function() {
view.baseView.showLoadError('response'); view.baseView.showLoadError('response');
......
...@@ -16,6 +16,7 @@ OpenAssessment.SelfView = function(element, server, baseView) { ...@@ -16,6 +16,7 @@ OpenAssessment.SelfView = function(element, server, baseView) {
this.rubric = null; this.rubric = null;
this.isRendering = false; this.isRendering = false;
this.announceStatus = false; this.announceStatus = false;
this.dateFactory = new OpenAssessment.DateTimeFactory(this.element);
}; };
OpenAssessment.SelfView.prototype = { OpenAssessment.SelfView.prototype = {
...@@ -38,8 +39,9 @@ OpenAssessment.SelfView.prototype = { ...@@ -38,8 +39,9 @@ OpenAssessment.SelfView.prototype = {
view.server.renderLatex($(stepID, view.element)); view.server.renderLatex($(stepID, view.element));
view.installHandlers(); view.installHandlers();
view.baseView.announceStatusChangeToSRandFocus(stepID, usageID, false, view, focusID); view.baseView.announceStatusChangeToSRandFocus(stepID, usageID, false, view, focusID);
view.dateFactory.apply();
} }
).fail(function() { ).fail(function() {
view.showLoadError('self-assessment'); view.showLoadError('self-assessment');
......
...@@ -11,6 +11,7 @@ OpenAssessment.StaffView = function(element, server, baseView) { ...@@ -11,6 +11,7 @@ OpenAssessment.StaffView = function(element, server, baseView) {
this.baseView = baseView; this.baseView = baseView;
this.isRendering = false; this.isRendering = false;
this.announceStatus = false; this.announceStatus = false;
}; };
OpenAssessment.StaffView.prototype = { OpenAssessment.StaffView.prototype = {
......
...@@ -16,6 +16,7 @@ OpenAssessment.StudentTrainingView = function(element, server, baseView) { ...@@ -16,6 +16,7 @@ OpenAssessment.StudentTrainingView = function(element, server, baseView) {
this.rubric = null; this.rubric = null;
this.isRendering = false; this.isRendering = false;
this.announceStatus = false; this.announceStatus = false;
this.dateFactory = new OpenAssessment.DateTimeFactory(this.element);
}; };
OpenAssessment.StudentTrainingView.prototype = { OpenAssessment.StudentTrainingView.prototype = {
...@@ -38,7 +39,7 @@ OpenAssessment.StudentTrainingView.prototype = { ...@@ -38,7 +39,7 @@ OpenAssessment.StudentTrainingView.prototype = {
view.baseView.announceStatusChangeToSRandFocus(stepID, usageID, false, view, focusID); view.baseView.announceStatusChangeToSRandFocus(stepID, usageID, false, view, focusID);
view.announceStatus = false; view.announceStatus = false;
view.dateFactory.apply();
} }
).fail(function() { ).fail(function() {
view.baseView.showLoadError('student-training'); view.baseView.showLoadError('student-training');
......
...@@ -8,7 +8,8 @@ from openassessment.assessment.api import student_training ...@@ -8,7 +8,8 @@ from openassessment.assessment.api import student_training
from openassessment.workflow import api as workflow_api from openassessment.workflow import api as workflow_api
from openassessment.workflow.errors import AssessmentWorkflowError from openassessment.workflow.errors import AssessmentWorkflowError
from openassessment.xblock.data_conversion import convert_training_examples_list_to_dict, create_submission_dict from openassessment.xblock.data_conversion import convert_training_examples_list_to_dict, create_submission_dict
from .resolve_dates import DISTANT_FUTURE, get_current_time_zone from .resolve_dates import DISTANT_FUTURE
from .user_data import get_user_preferences
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -69,14 +70,15 @@ class StudentTrainingMixin(object): ...@@ -69,14 +70,15 @@ class StudentTrainingMixin(object):
# If no submissions have been created yet, the status will be None. # If no submissions have been created yet, the status will be None.
workflow_status = self.get_workflow_info().get('status') workflow_status = self.get_workflow_info().get('status')
problem_closed, reason, start_date, due_date = self.is_closed(step="student-training") problem_closed, reason, start_date, due_date = self.is_closed(step="student-training")
user_service = self.runtime.service(self, 'user') user_preferences = get_user_preferences(self.runtime.service(self, 'user'))
context = {"xblock_id": self.get_xblock_id()} context = {"xblock_id": self.get_xblock_id()}
template = 'openassessmentblock/student_training/student_training_unavailable.html' template = 'openassessmentblock/student_training/student_training_unavailable.html'
# add allow_latex field to the context # add allow_latex field to the context
context['allow_latex'] = self.allow_latex context['allow_latex'] = self.allow_latex
context['time_zone'] = get_current_time_zone(user_service) context['user_timezone'] = user_preferences['user_timezone']
context['user_language'] = user_preferences['user_language']
if not workflow_status: if not workflow_status:
return template, context return template, context
......
...@@ -8,7 +8,8 @@ from openassessment.fileupload import api as file_upload_api ...@@ -8,7 +8,8 @@ from openassessment.fileupload import api as file_upload_api
from openassessment.fileupload.exceptions import FileUploadError from openassessment.fileupload.exceptions import FileUploadError
from openassessment.workflow.errors import AssessmentWorkflowError from openassessment.workflow.errors import AssessmentWorkflowError
from .resolve_dates import DISTANT_FUTURE, get_current_time_zone from .resolve_dates import DISTANT_FUTURE
from .user_data import get_user_preferences
from data_conversion import create_submission_dict, prepare_submission_for_serialization from data_conversion import create_submission_dict, prepare_submission_for_serialization
from validation import validate_submission from validation import validate_submission
...@@ -376,11 +377,12 @@ class SubmissionMixin(object): ...@@ -376,11 +377,12 @@ class SubmissionMixin(object):
""" """
workflow = self.get_workflow_info() workflow = self.get_workflow_info()
problem_closed, reason, start_date, due_date = self.is_closed('submission') problem_closed, reason, start_date, due_date = self.is_closed('submission')
user_service = self.runtime.service(self, 'user') user_preferences = get_user_preferences(self.runtime.service(self, 'user'))
path = 'openassessmentblock/response/oa_response.html' path = 'openassessmentblock/response/oa_response.html'
context = { context = {
'time_zone': get_current_time_zone(user_service), 'user_timezone': user_preferences['user_timezone'],
'user_language': user_preferences['user_language'],
"xblock_id": self.get_xblock_id()} "xblock_id": self.get_xblock_id()}
# Due dates can default to the distant future, in which case # Due dates can default to the distant future, in which case
......
...@@ -19,7 +19,7 @@ from .base import XBlockHandlerTestCase, scenario ...@@ -19,7 +19,7 @@ from .base import XBlockHandlerTestCase, scenario
class TestOpenAssessment(XBlockHandlerTestCase): class TestOpenAssessment(XBlockHandlerTestCase):
"""Test Open Asessessment Xblock functionality""" """Test Open Asessessment Xblock functionality"""
TIME_ZONE_FN_PATH = 'openassessment.xblock.peer_assessment_mixin.get_current_time_zone' TIME_ZONE_FN_PATH = 'openassessment.xblock.user_data.get_user_preferences'
@scenario('data/basic_scenario.xml') @scenario('data/basic_scenario.xml')
def test_load_student_view(self, xblock): def test_load_student_view(self, xblock):
...@@ -100,8 +100,8 @@ class TestOpenAssessment(XBlockHandlerTestCase): ...@@ -100,8 +100,8 @@ class TestOpenAssessment(XBlockHandlerTestCase):
# Expect that the page renders even if the update fails # Expect that the page renders even if the update fails
self.assertIn("OpenAssessmentBlock", xblock_fragment.body_html()) self.assertIn("OpenAssessmentBlock", xblock_fragment.body_html())
@ddt.data(('utc', 'April 1, 2014 00:00 UTC'), @ddt.data(('utc', '2014-03-31 20:00'),
('America/Los_Angeles', 'March 31, 2014 17:00 PDT')) ('America/Los_Angeles', '2014-03-31 20:00'))
@ddt.unpack @ddt.unpack
def test_load_student_view_with_dates(self, time_zone, expected_date): def test_load_student_view_with_dates(self, time_zone, expected_date):
"""OA XBlock returns some HTML to the user. """OA XBlock returns some HTML to the user.
...@@ -110,8 +110,8 @@ class TestOpenAssessment(XBlockHandlerTestCase): ...@@ -110,8 +110,8 @@ class TestOpenAssessment(XBlockHandlerTestCase):
Open Assessment XBlock. We don't want to match too heavily against the Open Assessment XBlock. We don't want to match too heavily against the
contents. contents.
""" """
with patch('openassessment.xblock.submission_mixin.get_current_time_zone') as time_zone_fn: with patch('openassessment.xblock.user_data.get_user_preferences') as time_zone_fn:
time_zone_fn.return_value = pytz.timezone(time_zone) time_zone_fn.return_value['user_timezone'] = pytz.timezone(time_zone)
xblock = self.load_scenario('data/dates_scenario.xml') xblock = self.load_scenario('data/dates_scenario.xml')
xblock_fragment = self.runtime.render(xblock, "student_view") xblock_fragment = self.runtime.render(xblock, "student_view")
...@@ -159,67 +159,40 @@ class TestOpenAssessment(XBlockHandlerTestCase): ...@@ -159,67 +159,40 @@ class TestOpenAssessment(XBlockHandlerTestCase):
request.params = {} request.params = {}
return xblock.render_peer_assessment(request) return xblock.render_peer_assessment(request)
@ddt.data(('utc', 'April 1, 2014 01:01 UTC'), @ddt.data(('utc', '2014-03-31 21:01'),
('America/Los_Angeles', 'March 31, 2014 18:01 PDT')) ('America/Los_Angeles', '2014-03-31 21:01'))
@ddt.unpack @ddt.unpack
@freeze_time("2014-01-01") @freeze_time("2014-01-01")
def test_formatted_start_dates(self, time_zone, expected_start_date): def test_formatted_start_dates(self, time_zone, expected_start_date):
"""Test start dates correctly formatted""" """Test start dates correctly formatted"""
with patch(self.TIME_ZONE_FN_PATH) as time_zone_fn: with patch(self.TIME_ZONE_FN_PATH) as time_zone_fn:
time_zone_fn.return_value = pytz.timezone(time_zone) time_zone_fn.return_value['user_timezone'] = pytz.timezone(time_zone)
xblock = self._set_up_start_date(dt.datetime(2014, 4, 1, 1, 1, 1)) xblock = self._set_up_start_date(dt.datetime(2014, 4, 1, 1, 1, 1))
resp = self._render_xblock(xblock) resp = self._render_xblock(xblock)
self.assertIn(expected_start_date, resp.body) self.assertIn(expected_start_date, resp.body)
@ddt.data((dt.datetime(2015, 3, 8, 9, 59, 00, tzinfo=pytz.utc), 'March 8, 2015 01:59 PST'), @ddt.data(('utc', '2014-04-30 20:00'),
(dt.datetime(2015, 3, 8, 10, 00, 00, tzinfo=pytz.utc), 'March 8, 2015 03:00 PDT')) ('America/Los_Angeles', '2014-04-30 20:00'))
@ddt.unpack
@freeze_time("2014-01-01")
def test_formatted_start_dates_daylight_savings(self, date, expected_start_date):
"""Test start dates correctly formatted for daylight savings time"""
with patch(self.TIME_ZONE_FN_PATH) as time_zone_fn:
time_zone_fn.return_value = pytz.timezone('America/Los_Angeles')
# Set start dates'
xblock = self._set_up_start_date(date)
resp = self._render_xblock(xblock)
self.assertIn(expected_start_date, resp.body)
@ddt.data(('utc', 'May 1, 2014 00:00 UTC'),
('America/Los_Angeles', 'April 30, 2014 17:00 PDT'))
@ddt.unpack @ddt.unpack
def test_formatted_end_dates(self, time_zone, expected_end_date): def test_formatted_end_dates(self, time_zone, expected_end_date):
"""Test end dates correctly formatted""" """Test end dates correctly formatted"""
with patch(self.TIME_ZONE_FN_PATH) as time_zone_fn: with patch(self.TIME_ZONE_FN_PATH) as time_zone_fn:
time_zone_fn.return_value = pytz.timezone(time_zone) time_zone_fn.return_value['user_timezone'] = time_zone
# Set due dates' # Set due dates'
xblock = self._set_up_end_date(dt.datetime(2014, 5, 1)) xblock = self._set_up_end_date(dt.datetime(2014, 5, 1))
resp = self._render_xblock(xblock) resp = self._render_xblock(xblock)
self.assertIn(expected_end_date, resp.body) self.assertIn(expected_end_date, resp.body)
@ddt.data((dt.datetime(2015, 3, 8, 9, 59, 00, tzinfo=pytz.utc), 'March 8, 2015 01:59 PST'), @ddt.data(('utc', '2014-03-31 21:01'),
(dt.datetime(2015, 3, 8, 10, 00, 00, tzinfo=pytz.utc), 'March 8, 2015 03:00 PDT')) ('America/Los_Angeles', '2014-03-31 21:01'))
@ddt.unpack
def test_formatted_end_dates_daylight_savings(self, date, expected_end_date):
"""Test end dates correctly formatted for daylight savings time"""
with patch(self.TIME_ZONE_FN_PATH) as time_zone_fn:
time_zone_fn.return_value = pytz.timezone('America/Los_Angeles')
# Set due dates'
xblock = self._set_up_end_date(date)
resp = self._render_xblock(xblock)
self.assertIn(expected_end_date, resp.body)
@ddt.data(('utc', 'April 1, 2014 01:01 UTC'),
('America/Los_Angeles', 'March 31, 2014 18:01 PDT'))
@ddt.unpack @ddt.unpack
@freeze_time("2014-01-01") @freeze_time("2014-01-01")
def test_formatted_start_dates_for_beta_tester_with_days_early(self, time_zone, expected_start_date): def test_formatted_start_dates_for_beta_tester_with_days_early(self, time_zone, expected_start_date):
"""Test start dates for beta tester with days early""" """Test start dates for beta tester with days early"""
with patch(self.TIME_ZONE_FN_PATH) as time_zone_fn: with patch(self.TIME_ZONE_FN_PATH) as time_zone_fn:
time_zone_fn.return_value = pytz.timezone(time_zone) time_zone_fn.return_value['user_timezone'] = pytz.timezone(time_zone)
# Set start dates # Set start dates
xblock = self._set_up_start_date(dt.datetime(2014, 4, 6, 1, 1, 1)) xblock = self._set_up_start_date(dt.datetime(2014, 4, 6, 1, 1, 1))
...@@ -229,25 +202,24 @@ class TestOpenAssessment(XBlockHandlerTestCase): ...@@ -229,25 +202,24 @@ class TestOpenAssessment(XBlockHandlerTestCase):
resp = self._render_xblock(xblock) resp = self._render_xblock(xblock)
self.assertIn(expected_start_date, resp.body) self.assertIn(expected_start_date, resp.body)
@ddt.data(('utc', 'May 1, 2014 00:00 UTC'), @ddt.data(('utc', '2014-04-30 20:00'),
('America/Los_Angeles', 'April 30, 2014 17:00 PDT')) ('America/Los_Angeles', '2014-04-30 20:00'))
@ddt.unpack @ddt.unpack
def test_formatted_end_dates_for_beta_tester_with_days_early(self, time_zone, expected_end_date): def test_formatted_end_dates_for_beta_tester_with_days_early(self, time_zone, expected_end_date):
"""Test end dates for beta tester with days early""" """Test end dates for beta tester with days early"""
with patch(self.TIME_ZONE_FN_PATH) as time_zone_fn: with patch(self.TIME_ZONE_FN_PATH) as time_zone_fn:
time_zone_fn.return_value = pytz.timezone(time_zone) time_zone_fn.return_value['user_timezone'] = pytz.timezone(time_zone)
# Set due dates # Set due dates
xblock = self._set_up_start_date(dt.datetime(2014, 4, 6, 1, 1, 1)) xblock = self._set_up_start_date(dt.datetime(2014, 4, 6, 1, 1, 1))
xblock.due = dt.datetime(2014, 5, 1) xblock.due = dt.datetime(2014, 5, 1)
self._set_up_days_early_for_beta(xblock, 5) self._set_up_days_early_for_beta(xblock, 5)
self.assertEqual(xblock.xmodule_runtime.days_early_for_beta, 5) self.assertEqual(xblock.xmodule_runtime.days_early_for_beta, 5)
resp = self._render_xblock(xblock) resp = self._render_xblock(xblock)
self.assertIn(expected_end_date, resp.body) self.assertIn(expected_end_date, resp.body)
@ddt.data(('utc', 'April 6, 2014 01:01 UTC'), @ddt.data(('utc', '2014-04-05 21:01'),
('America/Los_Angeles', 'April 5, 2014 18:01 PDT')) ('America/Los_Angeles', '2014-04-05 21:01'))
@ddt.unpack @ddt.unpack
@freeze_time("2014-01-01") @freeze_time("2014-01-01")
@patch.object(openassessmentblock.OpenAssessmentBlock, 'is_beta_tester', new_callable=PropertyMock) @patch.object(openassessmentblock.OpenAssessmentBlock, 'is_beta_tester', new_callable=PropertyMock)
...@@ -259,7 +231,7 @@ class TestOpenAssessment(XBlockHandlerTestCase): ...@@ -259,7 +231,7 @@ class TestOpenAssessment(XBlockHandlerTestCase):
): ):
"""Test start dates for beta tester without days early""" """Test start dates for beta tester without days early"""
with patch(self.TIME_ZONE_FN_PATH) as time_zone_fn: with patch(self.TIME_ZONE_FN_PATH) as time_zone_fn:
time_zone_fn.return_value = pytz.timezone(time_zone) time_zone_fn.return_value['user_timezone'] = pytz.timezone(time_zone)
mock_is_beta_tester.return_value = True mock_is_beta_tester.return_value = True
# Set start dates # Set start dates
...@@ -267,8 +239,8 @@ class TestOpenAssessment(XBlockHandlerTestCase): ...@@ -267,8 +239,8 @@ class TestOpenAssessment(XBlockHandlerTestCase):
resp = self._render_xblock(xblock) resp = self._render_xblock(xblock)
self.assertIn(expected_start_date, resp.body) self.assertIn(expected_start_date, resp.body)
@ddt.data(('utc', 'May 1, 2014 00:00 UTC'), @ddt.data(('utc', '2014-04-30 20:00'),
('America/Los_Angeles', 'April 30, 2014 17:00 PDT')) ('America/Los_Angeles', '2014-04-30 20:00'))
@ddt.unpack @ddt.unpack
@patch.object(openassessmentblock.OpenAssessmentBlock, 'is_beta_tester', new_callable=PropertyMock) @patch.object(openassessmentblock.OpenAssessmentBlock, 'is_beta_tester', new_callable=PropertyMock)
def test_formatted_end_dates_for_beta_tester_without_days_early( def test_formatted_end_dates_for_beta_tester_without_days_early(
...@@ -279,7 +251,7 @@ class TestOpenAssessment(XBlockHandlerTestCase): ...@@ -279,7 +251,7 @@ class TestOpenAssessment(XBlockHandlerTestCase):
): ):
"""Test end dates for beta tester without days early""" """Test end dates for beta tester without days early"""
with patch(self.TIME_ZONE_FN_PATH) as time_zone_fn: with patch(self.TIME_ZONE_FN_PATH) as time_zone_fn:
time_zone_fn.return_value = pytz.timezone(time_zone) time_zone_fn.return_value['user_timezone'] = pytz.timezone(time_zone)
mock_is_beta_tester.return_value = True mock_is_beta_tester.return_value = True
# Set due dates # Set due dates
...@@ -287,8 +259,8 @@ class TestOpenAssessment(XBlockHandlerTestCase): ...@@ -287,8 +259,8 @@ class TestOpenAssessment(XBlockHandlerTestCase):
resp = self._render_xblock(xblock) resp = self._render_xblock(xblock)
self.assertIn(expected_end_date, resp.body) self.assertIn(expected_end_date, resp.body)
@ddt.data(('utc', 'April 6, 2014 01:01 UTC'), @ddt.data(('utc', '2014-04-05 21:01'),
('America/Los_Angeles', 'April 5, 2014 18:01 PDT')) ('America/Los_Angeles', '2014-04-05 21:01'))
@ddt.unpack @ddt.unpack
@freeze_time("2014-01-01") @freeze_time("2014-01-01")
def test_formatted_start_dates_for_beta_tester_with_nonetype_days_early(self, time_zone, expected_start_date): def test_formatted_start_dates_for_beta_tester_with_nonetype_days_early(self, time_zone, expected_start_date):
...@@ -304,8 +276,8 @@ class TestOpenAssessment(XBlockHandlerTestCase): ...@@ -304,8 +276,8 @@ class TestOpenAssessment(XBlockHandlerTestCase):
resp = self._render_xblock(xblock) resp = self._render_xblock(xblock)
self.assertIn(expected_start_date, resp.body) self.assertIn(expected_start_date, resp.body)
@ddt.data(('utc', 'May 1, 2014 00:00 UTC'), @ddt.data(('utc', '2014-04-30 20:00'),
('America/Los_Angeles', 'April 30, 2014 17:00 PDT')) ('America/Los_Angeles', '2014-04-30 20:00'))
@ddt.unpack @ddt.unpack
def test_formatted_end_dates_for_beta_tester_with_nonetype_days_early(self, time_zone, expected_end_date): def test_formatted_end_dates_for_beta_tester_with_nonetype_days_early(self, time_zone, expected_end_date):
"""Test end dates for beta tester with NoneType days early""" """Test end dates for beta tester with NoneType days early"""
......
...@@ -309,7 +309,8 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase): ...@@ -309,7 +309,8 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase):
'must_grade': 5, 'must_grade': 5,
'review_num': 1, 'review_num': 1,
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc, 'user_timezone': pytz.utc,
'user_language': 'en'
} }
self._assert_path_and_context( self._assert_path_and_context(
xblock, 'openassessmentblock/peer/oa_peer_unavailable.html', expected_context xblock, 'openassessmentblock/peer/oa_peer_unavailable.html', expected_context
...@@ -325,7 +326,8 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase): ...@@ -325,7 +326,8 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase):
'must_grade': 5, 'must_grade': 5,
'review_num': 1, 'review_num': 1,
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc, 'user_timezone': pytz.utc,
'user_language': 'en'
} }
self._assert_path_and_context( self._assert_path_and_context(
xblock, 'openassessmentblock/peer/oa_peer_closed.html', expected_context xblock, 'openassessmentblock/peer/oa_peer_closed.html', expected_context
...@@ -341,7 +343,8 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase): ...@@ -341,7 +343,8 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase):
'must_grade': 5, 'must_grade': 5,
'review_num': 1, 'review_num': 1,
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc, 'user_timezone': pytz.utc,
'user_language': 'en'
} }
self._assert_path_and_context( self._assert_path_and_context(
xblock, 'openassessmentblock/peer/oa_peer_unavailable.html', expected_context xblock, 'openassessmentblock/peer/oa_peer_unavailable.html', expected_context
...@@ -360,7 +363,8 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase): ...@@ -360,7 +363,8 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase):
'review_num': 1, 'review_num': 1,
'submit_button_text': 'submit your assessment & move to response #2', 'submit_button_text': 'submit your assessment & move to response #2',
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc, 'user_timezone': pytz.utc,
'user_language': 'en'
} }
self._assert_path_and_context( self._assert_path_and_context(
xblock, 'openassessmentblock/peer/oa_peer_waiting.html', xblock, 'openassessmentblock/peer/oa_peer_waiting.html',
...@@ -400,7 +404,8 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase): ...@@ -400,7 +404,8 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase):
'peer_file_url': '', 'peer_file_url': '',
'submit_button_text': 'submit your assessment & move to response #2', 'submit_button_text': 'submit your assessment & move to response #2',
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc, 'user_timezone': pytz.utc,
'user_language': 'en'
} }
self._assert_path_and_context( self._assert_path_and_context(
xblock, 'openassessmentblock/peer/oa_peer_assessment.html', xblock, 'openassessmentblock/peer/oa_peer_assessment.html',
...@@ -420,7 +425,8 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase): ...@@ -420,7 +425,8 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase):
'review_num': 1, 'review_num': 1,
'submit_button_text': 'submit your assessment & move to response #2', 'submit_button_text': 'submit your assessment & move to response #2',
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc, 'user_timezone': pytz.utc,
'user_language': 'en'
} }
self._assert_path_and_context( self._assert_path_and_context(
...@@ -444,7 +450,8 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase): ...@@ -444,7 +450,8 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase):
'review_num': 1, 'review_num': 1,
'submit_button_text': 'submit your assessment & move to response #2', 'submit_button_text': 'submit your assessment & move to response #2',
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc, 'user_timezone': pytz.utc,
'user_language': 'en'
} }
self._assert_path_and_context( self._assert_path_and_context(
xblock, 'openassessmentblock/peer/oa_peer_closed.html', xblock, 'openassessmentblock/peer/oa_peer_closed.html',
...@@ -480,7 +487,8 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase): ...@@ -480,7 +487,8 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase):
'review_num': 1, 'review_num': 1,
'submit_button_text': 'submit your assessment & move to response #2', 'submit_button_text': 'submit your assessment & move to response #2',
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc, 'user_timezone': pytz.utc,
'user_language': 'en'
} }
self._assert_path_and_context( self._assert_path_and_context(
xblock, 'openassessmentblock/peer/oa_peer_closed.html', xblock, 'openassessmentblock/peer/oa_peer_closed.html',
...@@ -508,7 +516,8 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase): ...@@ -508,7 +516,8 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase):
'must_grade': 5, 'must_grade': 5,
'review_num': 1, 'review_num': 1,
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc, 'user_timezone': pytz.utc,
'user_language': 'en'
} }
self._assert_path_and_context( self._assert_path_and_context(
...@@ -541,7 +550,8 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase): ...@@ -541,7 +550,8 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase):
'rubric_criteria': xblock.rubric_criteria, 'rubric_criteria': xblock.rubric_criteria,
'submit_button_text': 'Submit your assessment & review another response', 'submit_button_text': 'Submit your assessment & review another response',
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc, 'user_timezone': pytz.utc,
'user_language': 'en'
} }
self._assert_path_and_context( self._assert_path_and_context(
xblock, 'openassessmentblock/peer/oa_peer_turbo_mode_waiting.html', xblock, 'openassessmentblock/peer/oa_peer_turbo_mode_waiting.html',
...@@ -572,7 +582,8 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase): ...@@ -572,7 +582,8 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase):
'rubric_criteria': xblock.rubric_criteria, 'rubric_criteria': xblock.rubric_criteria,
'submit_button_text': 'Submit your assessment & review another response', 'submit_button_text': 'Submit your assessment & review another response',
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc, 'user_timezone': pytz.utc,
'user_language': 'en'
} }
self._assert_path_and_context( self._assert_path_and_context(
xblock, 'openassessmentblock/peer/oa_peer_turbo_mode.html', xblock, 'openassessmentblock/peer/oa_peer_turbo_mode.html',
...@@ -594,7 +605,8 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase): ...@@ -594,7 +605,8 @@ class TestPeerAssessmentRender(XBlockHandlerTestCase):
'rubric_criteria': xblock.rubric_criteria, 'rubric_criteria': xblock.rubric_criteria,
'submit_button_text': 'Submit your assessment & review another response', 'submit_button_text': 'Submit your assessment & review another response',
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc, 'user_timezone': pytz.utc,
'user_language': 'en'
} }
self._assert_path_and_context( self._assert_path_and_context(
xblock, 'openassessmentblock/peer/oa_peer_unavailable.html', xblock, 'openassessmentblock/peer/oa_peer_unavailable.html',
......
...@@ -6,7 +6,9 @@ import datetime ...@@ -6,7 +6,9 @@ import datetime
from django.test import TestCase from django.test import TestCase
import ddt import ddt
from mock import MagicMock from mock import MagicMock
from openassessment.xblock.resolve_dates import resolve_dates, DISTANT_PAST, DISTANT_FUTURE, get_current_time_zone from openassessment.xblock.resolve_dates import resolve_dates, DISTANT_PAST, DISTANT_FUTURE
from openassessment.xblock.user_data import get_user_preferences
import pytz import pytz
from workbench.runtime import WorkBenchUserService from workbench.runtime import WorkBenchUserService
...@@ -131,13 +133,14 @@ class ResolveDatesTest(TestCase): ...@@ -131,13 +133,14 @@ class ResolveDatesTest(TestCase):
STUB_I18N STUB_I18N
) )
@ddt.data(({}, pytz.utc), @ddt.data(({}, None, None),
({'pref-lang': 'en', 'time_zone': 'America/Los_Angeles'}, pytz.timezone('America/Los_Angeles'))) ({'pref-lang': 'en', 'time_zone': 'America/Los_Angeles'}, 'America/Los_Angeles', 'en'))
@ddt.unpack @ddt.unpack
def test_get_current_time_zone(self, user_preferences, expected_time_zone): def test_get_user_preferences(self, user_preferences, expected_timezone, expected_language):
"""Verify get_current_time_zone returns correct time zone or UTC""" """Verify get_user_preferences returns correct time zone and language"""
user_service = WorkBenchUserService(3) user_service = WorkBenchUserService(3)
user_service.get_current_user().opt_attrs['edx-platform.user_preferences'] = user_preferences user_service.get_current_user().opt_attrs['edx-platform.user_preferences'] = user_preferences
time_zone = get_current_time_zone(user_service) user_preferences = get_user_preferences(user_service)
self.assertEqual(expected_time_zone, time_zone) self.assertEqual(expected_timezone, user_preferences['user_timezone'])
self.assertEqual(expected_language, user_preferences['user_language'])
...@@ -172,7 +172,8 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase): ...@@ -172,7 +172,8 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase):
{ {
'self_start': datetime.datetime(5999, 1, 1).replace(tzinfo=pytz.utc), 'self_start': datetime.datetime(5999, 1, 1).replace(tzinfo=pytz.utc),
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc, 'user_timezone': pytz.utc,
'user_language': 'en'
} }
) )
...@@ -185,7 +186,8 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase): ...@@ -185,7 +186,8 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase):
{ {
'self_due': datetime.datetime(2000, 1, 1).replace(tzinfo=pytz.utc), 'self_due': datetime.datetime(2000, 1, 1).replace(tzinfo=pytz.utc),
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc, 'user_timezone': pytz.utc,
'user_language': 'en'
} }
) )
...@@ -196,7 +198,8 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase): ...@@ -196,7 +198,8 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase):
xblock, 'openassessmentblock/self/oa_self_unavailable.html', xblock, 'openassessmentblock/self/oa_self_unavailable.html',
{ {
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc, 'user_timezone': pytz.utc,
'user_language': 'en'
} }
) )
...@@ -212,7 +215,8 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase): ...@@ -212,7 +215,8 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase):
xblock, 'openassessmentblock/self/oa_self_unavailable.html', xblock, 'openassessmentblock/self/oa_self_unavailable.html',
{ {
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc, 'user_timezone': pytz.utc,
'user_language': 'en'
} }
) )
...@@ -228,7 +232,8 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase): ...@@ -228,7 +232,8 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase):
xblock, 'openassessmentblock/self/oa_self_complete.html', xblock, 'openassessmentblock/self/oa_self_complete.html',
{ {
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc, 'user_timezone': None,
'user_language': None
}, },
workflow_status='waiting', workflow_status='waiting',
status_details={ status_details={
...@@ -249,7 +254,9 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase): ...@@ -249,7 +254,9 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase):
xblock, 'openassessmentblock/self/oa_self_complete.html', xblock, 'openassessmentblock/self/oa_self_complete.html',
{ {
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc, 'user_timezone': None,
'user_language': None
}, },
workflow_status="peer", workflow_status="peer",
status_details={ status_details={
...@@ -268,7 +275,8 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase): ...@@ -268,7 +275,8 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase):
xblock, 'openassessmentblock/self/oa_self_complete.html', xblock, 'openassessmentblock/self/oa_self_complete.html',
{ {
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc, 'user_timezone': None,
'user_language': None
}, },
workflow_status='done' workflow_status='done'
) )
...@@ -283,7 +291,8 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase): ...@@ -283,7 +291,8 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase):
xblock, 'openassessmentblock/self/oa_self_cancelled.html', xblock, 'openassessmentblock/self/oa_self_cancelled.html',
{ {
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc, 'user_timezone': pytz.utc,
'user_language': 'en'
}, },
workflow_status='cancelled' workflow_status='cancelled'
) )
...@@ -302,7 +311,8 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase): ...@@ -302,7 +311,8 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase):
'file_upload_type': None, 'file_upload_type': None,
'self_file_url': '', 'self_file_url': '',
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc, 'user_timezone': pytz.utc,
'user_language': 'en'
}, },
workflow_status='self', workflow_status='self',
submission_uuid=submission['uuid'] submission_uuid=submission['uuid']
...@@ -326,7 +336,8 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase): ...@@ -326,7 +336,8 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase):
xblock, 'openassessmentblock/self/oa_self_complete.html', xblock, 'openassessmentblock/self/oa_self_complete.html',
{ {
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc, 'user_timezone': pytz.utc,
'user_language': 'en'
}, },
workflow_status='self', workflow_status='self',
submission_uuid=submission['uuid'] submission_uuid=submission['uuid']
...@@ -345,7 +356,8 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase): ...@@ -345,7 +356,8 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase):
{ {
'self_due': datetime.datetime(2000, 1, 1).replace(tzinfo=pytz.utc), 'self_due': datetime.datetime(2000, 1, 1).replace(tzinfo=pytz.utc),
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc, 'user_timezone': pytz.utc,
'user_language': 'en'
}, },
workflow_status='self', workflow_status='self',
submission_uuid=submission['uuid'] submission_uuid=submission['uuid']
...@@ -376,7 +388,8 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase): ...@@ -376,7 +388,8 @@ class TestSelfAssessmentRender(XBlockHandlerTestCase):
{ {
'self_due': datetime.datetime(2000, 1, 1).replace(tzinfo=pytz.utc), 'self_due': datetime.datetime(2000, 1, 1).replace(tzinfo=pytz.utc),
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc, 'user_timezone': pytz.utc,
'user_language': 'en'
}, },
workflow_status='self', workflow_status='self',
submission_uuid=submission['uuid'] submission_uuid=submission['uuid']
......
...@@ -66,7 +66,8 @@ class StudentTrainingAssessTest(StudentTrainingTest): ...@@ -66,7 +66,8 @@ class StudentTrainingAssessTest(StudentTrainingTest):
@ddt.file_data('data/student_training_mixin.json') @ddt.file_data('data/student_training_mixin.json')
def test_correct(self, xblock, data): def test_correct(self, xblock, data):
xblock.create_submission(xblock.get_student_item_dict(), self.SUBMISSION) xblock.create_submission(xblock.get_student_item_dict(), self.SUBMISSION)
data["expected_context"]['time_zone'] = pytz.utc data["expected_context"]['user_timezone'] = None
data["expected_context"]['user_language'] = None
self.assert_path_and_context(xblock, data["expected_template"], data["expected_context"]) self.assert_path_and_context(xblock, data["expected_template"], data["expected_context"])
# Agree with the course author's assessment # Agree with the course author's assessment
...@@ -87,7 +88,8 @@ class StudentTrainingAssessTest(StudentTrainingTest): ...@@ -87,7 +88,8 @@ class StudentTrainingAssessTest(StudentTrainingTest):
@ddt.file_data('data/student_training_mixin.json') @ddt.file_data('data/student_training_mixin.json')
def test_correct_with_error(self, xblock, data): def test_correct_with_error(self, xblock, data):
xblock.create_submission(xblock.get_student_item_dict(), self.SUBMISSION) xblock.create_submission(xblock.get_student_item_dict(), self.SUBMISSION)
data["expected_context"]['time_zone'] = pytz.utc data["expected_context"]['user_timezone'] = None
data["expected_context"]['user_language'] = None
self.assert_path_and_context(xblock, data["expected_template"], data["expected_context"]) self.assert_path_and_context(xblock, data["expected_template"], data["expected_context"])
# Agree with the course author's assessment # Agree with the course author's assessment
...@@ -111,7 +113,8 @@ class StudentTrainingAssessTest(StudentTrainingTest): ...@@ -111,7 +113,8 @@ class StudentTrainingAssessTest(StudentTrainingTest):
@ddt.file_data('data/student_training_mixin.json') @ddt.file_data('data/student_training_mixin.json')
def test_incorrect(self, xblock, data): def test_incorrect(self, xblock, data):
xblock.create_submission(xblock.get_student_item_dict(), self.SUBMISSION) xblock.create_submission(xblock.get_student_item_dict(), self.SUBMISSION)
data["expected_context"]['time_zone'] = pytz.utc data["expected_context"]['user_timezone'] = None
data["expected_context"]['user_language'] = None
self.assert_path_and_context(xblock, data["expected_template"], data["expected_context"]) self.assert_path_and_context(xblock, data["expected_template"], data["expected_context"])
# Disagree with the course author's assessment # Disagree with the course author's assessment
...@@ -134,7 +137,8 @@ class StudentTrainingAssessTest(StudentTrainingTest): ...@@ -134,7 +137,8 @@ class StudentTrainingAssessTest(StudentTrainingTest):
expected_context = data["expected_context"].copy() expected_context = data["expected_context"].copy()
expected_template = data["expected_template"] expected_template = data["expected_template"]
xblock.create_submission(xblock.get_student_item_dict(), self.SUBMISSION) xblock.create_submission(xblock.get_student_item_dict(), self.SUBMISSION)
expected_context['time_zone'] = pytz.utc expected_context['user_timezone'] = None
expected_context['user_language'] = None
self.assert_path_and_context(xblock, expected_template, expected_context) self.assert_path_and_context(xblock, expected_template, expected_context)
# Agree with the course author's assessment # Agree with the course author's assessment
...@@ -182,7 +186,8 @@ class StudentTrainingAssessTest(StudentTrainingTest): ...@@ -182,7 +186,8 @@ class StudentTrainingAssessTest(StudentTrainingTest):
self.assertFalse(resp['corrections']) self.assertFalse(resp['corrections'])
expected_context = { expected_context = {
"allow_latex": False, "allow_latex": False,
'time_zone': pytz.utc, 'user_timezone': None,
'user_language': None
} }
expected_template = "openassessmentblock/student_training/student_training_complete.html" expected_template = "openassessmentblock/student_training/student_training_complete.html"
self.assert_path_and_context(xblock, expected_template, expected_context) self.assert_path_and_context(xblock, expected_template, expected_context)
...@@ -213,7 +218,9 @@ class StudentTrainingAssessTest(StudentTrainingTest): ...@@ -213,7 +218,9 @@ class StudentTrainingAssessTest(StudentTrainingTest):
xblock.create_submission(xblock.get_student_item_dict(), self.SUBMISSION) xblock.create_submission(xblock.get_student_item_dict(), self.SUBMISSION)
expected_context = data["expected_context"].copy() expected_context = data["expected_context"].copy()
expected_template = data["expected_template"] expected_template = data["expected_template"]
expected_context['time_zone'] = pytz.utc expected_context['user_timezone'] = None
expected_context['user_language'] = None
self.assert_path_and_context(xblock, expected_template, expected_context) self.assert_path_and_context(xblock, expected_template, expected_context)
resp = self.request(xblock, 'training_assess', json.dumps({}), response_format='json') resp = self.request(xblock, 'training_assess', json.dumps({}), response_format='json')
self.assertFalse(resp['success'], msg=resp.get('msg')) self.assertFalse(resp['success'], msg=resp.get('msg'))
...@@ -230,7 +237,8 @@ class StudentTrainingAssessTest(StudentTrainingTest): ...@@ -230,7 +237,8 @@ class StudentTrainingAssessTest(StudentTrainingTest):
xblock.create_submission(xblock.get_student_item_dict(), self.SUBMISSION) xblock.create_submission(xblock.get_student_item_dict(), self.SUBMISSION)
expected_context = data["expected_context"].copy() expected_context = data["expected_context"].copy()
expected_template = data["expected_template"] expected_template = data["expected_template"]
expected_context['time_zone'] = pytz.utc expected_context['user_timezone'] = None
expected_context['user_language'] = None
self.assert_path_and_context(xblock, expected_template, expected_context) self.assert_path_and_context(xblock, expected_template, expected_context)
selected_data = { selected_data = {
...@@ -317,7 +325,8 @@ class StudentTrainingRenderTest(StudentTrainingTest): ...@@ -317,7 +325,8 @@ class StudentTrainingRenderTest(StudentTrainingTest):
expected_context = { expected_context = {
'training_due': "2000-01-01T00:00:00+00:00", 'training_due': "2000-01-01T00:00:00+00:00",
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc, 'user_timezone': None,
'user_language': None
} }
self.assert_path_and_context(xblock, expected_template, expected_context) self.assert_path_and_context(xblock, expected_template, expected_context)
...@@ -331,7 +340,8 @@ class StudentTrainingRenderTest(StudentTrainingTest): ...@@ -331,7 +340,8 @@ class StudentTrainingRenderTest(StudentTrainingTest):
expected_template = "openassessmentblock/student_training/student_training_cancelled.html" expected_template = "openassessmentblock/student_training/student_training_cancelled.html"
expected_context = { expected_context = {
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc, 'user_timezone': None,
'user_language': None
} }
self.assert_path_and_context(xblock, expected_template, expected_context) self.assert_path_and_context(xblock, expected_template, expected_context)
...@@ -350,6 +360,7 @@ class StudentTrainingRenderTest(StudentTrainingTest): ...@@ -350,6 +360,7 @@ class StudentTrainingRenderTest(StudentTrainingTest):
expected_context = { expected_context = {
'training_start': datetime.datetime(3000, 1, 1).replace(tzinfo=pytz.utc), 'training_start': datetime.datetime(3000, 1, 1).replace(tzinfo=pytz.utc),
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc, 'user_timezone': None,
'user_language': None
} }
self.assert_path_and_context(xblock, expected_template, expected_context) self.assert_path_and_context(xblock, expected_template, expected_context)
...@@ -182,7 +182,8 @@ class SubmissionRenderTest(XBlockHandlerTestCase): ...@@ -182,7 +182,8 @@ class SubmissionRenderTest(XBlockHandlerTestCase):
'file_upload_type': None, 'file_upload_type': None,
'submission_start': dt.datetime(4999, 4, 1).replace(tzinfo=pytz.utc), 'submission_start': dt.datetime(4999, 4, 1).replace(tzinfo=pytz.utc),
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc, 'user_timezone': None,
'user_language': None
} }
) )
...@@ -205,7 +206,8 @@ class SubmissionRenderTest(XBlockHandlerTestCase): ...@@ -205,7 +206,8 @@ class SubmissionRenderTest(XBlockHandlerTestCase):
'peer_incomplete': True, 'peer_incomplete': True,
'self_incomplete': True, 'self_incomplete': True,
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc, 'user_timezone': None,
'user_language': None
} }
) )
...@@ -224,7 +226,8 @@ class SubmissionRenderTest(XBlockHandlerTestCase): ...@@ -224,7 +226,8 @@ class SubmissionRenderTest(XBlockHandlerTestCase):
'submit_enabled': False, 'submit_enabled': False,
'submission_due': dt.datetime(2999, 5, 6).replace(tzinfo=pytz.utc), 'submission_due': dt.datetime(2999, 5, 6).replace(tzinfo=pytz.utc),
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc, 'user_timezone': None,
'user_language': None
} }
) )
...@@ -242,7 +245,8 @@ class SubmissionRenderTest(XBlockHandlerTestCase): ...@@ -242,7 +245,8 @@ class SubmissionRenderTest(XBlockHandlerTestCase):
'save_status': 'This response has not been saved.', 'save_status': 'This response has not been saved.',
'submit_enabled': False, 'submit_enabled': False,
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc, 'user_timezone': None,
'user_language': None
} }
) )
...@@ -266,7 +270,8 @@ class SubmissionRenderTest(XBlockHandlerTestCase): ...@@ -266,7 +270,8 @@ class SubmissionRenderTest(XBlockHandlerTestCase):
'submit_enabled': True, 'submit_enabled': True,
'submission_due': dt.datetime(2999, 5, 6).replace(tzinfo=pytz.utc), 'submission_due': dt.datetime(2999, 5, 6).replace(tzinfo=pytz.utc),
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc, 'user_timezone': None,
'user_language': None
} }
) )
...@@ -290,7 +295,8 @@ class SubmissionRenderTest(XBlockHandlerTestCase): ...@@ -290,7 +295,8 @@ class SubmissionRenderTest(XBlockHandlerTestCase):
'submit_enabled': True, 'submit_enabled': True,
'submission_due': dt.datetime(2999, 5, 6).replace(tzinfo=pytz.utc), 'submission_due': dt.datetime(2999, 5, 6).replace(tzinfo=pytz.utc),
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc, 'user_timezone': None,
'user_language': None
} }
) )
...@@ -309,7 +315,8 @@ class SubmissionRenderTest(XBlockHandlerTestCase): ...@@ -309,7 +315,8 @@ class SubmissionRenderTest(XBlockHandlerTestCase):
'peer_incomplete': True, 'peer_incomplete': True,
'self_incomplete': True, 'self_incomplete': True,
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc, 'user_timezone': None,
'user_language': None
} }
) )
...@@ -341,7 +348,8 @@ class SubmissionRenderTest(XBlockHandlerTestCase): ...@@ -341,7 +348,8 @@ class SubmissionRenderTest(XBlockHandlerTestCase):
'cancelled_by_id': 'Bob', 'cancelled_by_id': 'Bob',
'cancelled_by': mock_staff 'cancelled_by': mock_staff
}, },
'time_zone': pytz.utc, 'user_timezone': None,
'user_language': None
} }
) )
...@@ -367,7 +375,8 @@ class SubmissionRenderTest(XBlockHandlerTestCase): ...@@ -367,7 +375,8 @@ class SubmissionRenderTest(XBlockHandlerTestCase):
'peer_incomplete': True, 'peer_incomplete': True,
'self_incomplete': True, 'self_incomplete': True,
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc, 'user_timezone': None,
'user_language': None
} }
) )
...@@ -379,7 +388,8 @@ class SubmissionRenderTest(XBlockHandlerTestCase): ...@@ -379,7 +388,8 @@ class SubmissionRenderTest(XBlockHandlerTestCase):
'file_upload_type': None, 'file_upload_type': None,
'submission_due': dt.datetime(2014, 4, 5).replace(tzinfo=pytz.utc), 'submission_due': dt.datetime(2014, 4, 5).replace(tzinfo=pytz.utc),
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc, 'user_timezone': None,
'user_language': None
} }
) )
...@@ -398,7 +408,8 @@ class SubmissionRenderTest(XBlockHandlerTestCase): ...@@ -398,7 +408,8 @@ class SubmissionRenderTest(XBlockHandlerTestCase):
'peer_incomplete': False, 'peer_incomplete': False,
'self_incomplete': True, 'self_incomplete': True,
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc, 'user_timezone': None,
'user_language': None
} }
) )
...@@ -423,7 +434,8 @@ class SubmissionRenderTest(XBlockHandlerTestCase): ...@@ -423,7 +434,8 @@ class SubmissionRenderTest(XBlockHandlerTestCase):
'student_submission': create_submission_dict(submission, xblock.prompts), 'student_submission': create_submission_dict(submission, xblock.prompts),
'file_upload_type': None, 'file_upload_type': None,
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc, 'user_timezone': None,
'user_language': None
} }
) )
...@@ -448,7 +460,8 @@ class SubmissionRenderTest(XBlockHandlerTestCase): ...@@ -448,7 +460,8 @@ class SubmissionRenderTest(XBlockHandlerTestCase):
'student_submission': create_submission_dict(submission, xblock.prompts), 'student_submission': create_submission_dict(submission, xblock.prompts),
'file_upload_type': None, 'file_upload_type': None,
'allow_latex': False, 'allow_latex': False,
'time_zone': pytz.utc, 'user_timezone': None,
'user_language': None
} }
) )
...@@ -457,7 +470,7 @@ class SubmissionRenderTest(XBlockHandlerTestCase): ...@@ -457,7 +470,7 @@ class SubmissionRenderTest(XBlockHandlerTestCase):
# Expect that the response step is open and displays the deadline # Expect that the response step is open and displays the deadline
resp = self.request(xblock, 'render_submission', json.dumps(dict())) resp = self.request(xblock, 'render_submission', json.dumps(dict()))
self.assertIn('Enter your response to the question', resp) self.assertIn('Enter your response to the question', resp)
self.assertIn('Monday, May 6, 2999 00:00 UTC', resp) self.assertIn('2999-05-05 19:00', resp)
# Create a submission for the user # Create a submission for the user
xblock.create_submission( xblock.create_submission(
......
"""
Retrieve user-specific data
"""
def get_user_preferences(user_service):
"""
Returns the preferred language and timezone for the current user, if specified, or None if not.
:param user_service: XblockUserService
"""
user_preferences = {
'user_language': None,
'user_timezone': None
}
retrieved_preferences = user_service.get_current_user().opt_attrs.get('edx-platform.user_preferences')
if retrieved_preferences is not None:
user_preferences['user_timezone'] = retrieved_preferences.get('time_zone')
user_preferences['user_language'] = retrieved_preferences.get('pref-lang')
return user_preferences
...@@ -2,23 +2,29 @@ ...@@ -2,23 +2,29 @@
"name": "edx-ora2", "name": "edx-ora2",
"version": "0.2.0", "version": "0.2.0",
"repository": "https://github.com/edx/edx-ora2.git", "repository": "https://github.com/edx/edx-ora2.git",
"dependencies": {
"edx-ui-toolkit": "1.5.1",
"moment": "^2.15.1",
"moment-timezone": "~0.5.5",
"requirejs": "~2.3.2",
"underscore": "^1.8.2"
},
"devDependencies": { "devDependencies": {
"underscore": "^1.8.2", "jasmine": "^2.3.0",
"jscs": "2.6.0",
"jshint": "2.8.0",
"karma": "^0.12.16", "karma": "^0.12.16",
"karma-chrome-launcher": "^0.1.4",
"karma-coverage": "^0.2.6", "karma-coverage": "^0.2.6",
"karma-jasmine": "^0.3.6", "karma-jasmine": "^0.3.6",
"karma-jasmine-html-reporter": "~0.1",
"karma-jasmine-jquery": "^0.1.1", "karma-jasmine-jquery": "^0.1.1",
"karma-chrome-launcher": "^0.1.4",
"karma-phantomjs-launcher": "^0.1.4", "karma-phantomjs-launcher": "^0.1.4",
"karma-sinon": "^1.0.3", "karma-sinon": "^1.0.3",
"karma-jasmine-html-reporter": "~0.1",
"karma-spec-reporter": "^0.0.20", "karma-spec-reporter": "^0.0.20",
"jasmine": "^2.3.0",
"phantomjs": "^1.9.11", "phantomjs": "^1.9.11",
"sinon": "^1.10.3", "sinon": "^1.10.3",
"uglify-js": "^2.6.3", "uglify-js": "^2.6.3"
"jshint": "2.8.0",
"jscs": "2.6.0"
}, },
"scripts": { "scripts": {
"test": "./node_modules/karma/bin/karma start --reporters spec,coverage" "test": "./node_modules/karma/bin/karma start --reporters spec,coverage"
......
(function(require, define) {
'use strict';
var defineDependency;
// We do not wish to bundle common libraries (that may also be used by non-RequireJS code on the page
// into the optimized files. Therefore load these libraries through script tags and explicitly define them.
// Note that when the optimizer executes this code, window will not be defined.
if (window) {
defineDependency = function (globalName, name, noShim) {
var getGlobalValue = function () {
var globalNamePath = globalName.split('.'),
result = window,
i;
for (i = 0; i < globalNamePath.length; i++) {
result = result[globalNamePath[i]];
}
return result;
},
globalValue = getGlobalValue();
if (globalValue) {
if (noShim) {
define(name, {});
} else {
define(name, [], function () {
return globalValue;
});
}
} else {
console.error('Expected library to be included on page, but not found on window object: ' + name);
}
};
defineDependency('jQuery', 'jquery');
}
function getBaseUrlPath() {
// require-config doesn't play nice with relative paths, but
// the Travis server, devstack, and staging/production servers all place the
// node_module files differently in absolute paths - this is a way to retrieve the
// path to the Jasmine req's as nicely as possible
var scripts = document.getElementsByTagName("script");
var fullTag = $(scripts[scripts.length-1]);
var baseUrl = fullTag.attr('src').split(['require-config'])[0];
return baseUrl
}
require.config({
baseUrl: getBaseUrlPath(),
paths: {
'jquery': './openassessment/xblock/static/js/lib/jquery.min',
'moment': './node_modules/moment/min/moment-with-locales.min',
'moment-timezone': './node_modules/moment-timezone/builds/moment-timezone-with-data.min',
'edx-ui-toolkit/js/utils/date-utils': './node_modules/edx-ui-toolkit/src/js/utils/date-utils',
'edx-ui-toolkit/js/utils/string-utils': './node_modules/edx-ui-toolkit/src/js/utils/string-utils'
},
shim: {
'jquery': {
exports: 'jQuery'
}
}
})
}).call(this, require || RequireJS.require, define || RequireJS.define);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment