Commit bfc45275 by Brian Wilson

Switch timed_module to store location, and use to navigate from timer when timer…

Switch timed_module to store location, and use to navigate from timer when timer displays on non-exam course pages.
parent 1b465d1b
......@@ -1138,10 +1138,11 @@ def test_center_login(request):
# know the module_id to use that corresponds to the particular exam_series_code.
# For now, we can hardcode that...
if exam_series_code == '6002x001':
chapter_url_name = 'Final_Exam'
section_url_name = 'Final_Exam_Fall_2012'
redirect_url = reverse('courseware_section', args=[course_id, chapter_url_name, section_url_name])
location = 'i4x://MITx/6.002x/2012_Fall/sequence/Final_Exam_Fall_2012'
# This should not be hardcoded here, but should be added to the exam definition.
# TODO: look the location up in the course, by finding the exam_info with the matching code,
# and get the location from that.
location = 'i4x://MITx/6.002x/sequential/Final_Exam_Fall_2012'
redirect_url = reverse('jump_to', kwargs={'course_id': course_id, 'location': location})
else:
# TODO: clarify if this is the right error code for this condition.
return HttpResponseRedirect(makeErrorURL(error_url, "incorrectCandidateTests"));
......@@ -1152,7 +1153,7 @@ def test_center_login(request):
'ETDBTM' : 'ADDDOUBLE', }
# check if the test has already been taken
timed_modules = TimedModule.objects.filter(student=testcenteruser.user, course_id=course_id, module_state_key=location)
timed_modules = TimedModule.objects.filter(student=testcenteruser.user, course_id=course_id, location=location)
if timed_modules:
timed_module = timed_modules[0]
if timed_module.has_ended:
......@@ -1167,17 +1168,9 @@ def test_center_login(request):
if client_candidate_id == "edX003671291147":
time_accommodation_code = 'TESTING'
if time_accommodation_code:
timed_module = TimedModule(student=request.user, course_id=course_id, module_state_key=location)
timed_module = TimedModule(student=request.user, course_id=course_id, location=location)
timed_module.accommodation_code = time_accommodation_code
timed_module.save()
# Now log the user in:
# user = authenticate(username=testcenteruser.user.username,
# password=testcenteruser.user.password)
#
# if user is None:
# # argh. We couldn't login!
# return HttpResponseRedirect(makeErrorURL(error_url, "ARGH! User cannot log in"));
# UGLY HACK!!!
# Login assumes that authentication has occurred, and that there is a
......
......@@ -11,7 +11,7 @@ class Migration(SchemaMigration):
# Adding model 'TimedModule'
db.create_table('courseware_timedmodule', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('module_state_key', self.gf('django.db.models.fields.CharField')(max_length=255, db_column='module_id', db_index=True)),
('location', self.gf('django.db.models.fields.CharField')(max_length=255, db_column='location', db_index=True)),
('student', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])),
('course_id', self.gf('django.db.models.fields.CharField')(max_length=255, db_index=True)),
('accommodation_code', self.gf('django.db.models.fields.CharField')(default='NONE', max_length=12, db_index=True)),
......@@ -22,13 +22,13 @@ class Migration(SchemaMigration):
))
db.send_create_signal('courseware', ['TimedModule'])
# Adding unique constraint on 'TimedModule', fields ['student', 'module_state_key', 'course_id']
db.create_unique('courseware_timedmodule', ['student_id', 'module_id', 'course_id'])
# Adding unique constraint on 'TimedModule', fields ['student', 'location', 'course_id']
db.create_unique('courseware_timedmodule', ['student_id', 'location', 'course_id'])
def backwards(self, orm):
# Removing unique constraint on 'TimedModule', fields ['student', 'module_state_key', 'course_id']
db.delete_unique('courseware_timedmodule', ['student_id', 'module_id', 'course_id'])
# Removing unique constraint on 'TimedModule', fields ['student', 'location', 'course_id']
db.delete_unique('courseware_timedmodule', ['student_id', 'location', 'course_id'])
# Deleting model 'TimedModule'
db.delete_table('courseware_timedmodule')
......@@ -103,15 +103,15 @@ class Migration(SchemaMigration):
'student': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
},
'courseware.timedmodule': {
'Meta': {'unique_together': "(('student', 'module_state_key', 'course_id'),)", 'object_name': 'TimedModule'},
'Meta': {'unique_together': "(('student', 'location', 'course_id'),)", 'object_name': 'TimedModule'},
'accommodation_code': ('django.db.models.fields.CharField', [], {'default': "'NONE'", 'max_length': '12', 'db_index': 'True'}),
'beginning_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'db_index': 'True'}),
'course_id': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'db_index': 'True', 'blank': 'True'}),
'ending_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'db_index': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'location': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_column': "'location'", 'db_index': 'True'}),
'modified_at': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'db_index': 'True', 'blank': 'True'}),
'module_state_key': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_column': "'module_id'", 'db_index': 'True'}),
'student': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"})
}
}
......
......@@ -224,12 +224,14 @@ class TimedModule(models.Model):
# but for abtests and the like, this can be set to a shared value
# for many instances of the module.
# Filename for homeworks, etc.
module_state_key = models.CharField(max_length=255, db_index=True, db_column='module_id')
# module_state_key = models.CharField(max_length=255, db_index=True, db_column='module_id')
location = models.CharField(max_length=255, db_index=True, db_column='location')
student = models.ForeignKey(User, db_index=True)
course_id = models.CharField(max_length=255, db_index=True)
class Meta:
unique_together = (('student', 'module_state_key', 'course_id'),)
# unique_together = (('student', 'module_state_key', 'course_id'),)
unique_together = (('student', 'location', 'course_id'),)
# For a timed activity, we are only interested here
# in time-related accommodations, and these should be disjoint.
......
......@@ -215,6 +215,43 @@ def index(request, course_id, chapter=None, section=None,
'xqa_server': settings.MITX_FEATURES.get('USE_XQA_SERVER','http://xqa:server@content-qa.mitx.mit.edu/xqa')
}
# check here if this page is within a course that has an active timed module running. If so, then
# display the appropriate timer information:
timed_modules = TimedModule.objects.filter(student=request.user, course_id=course_id)
if timed_modules:
for timed_module in timed_modules:
if timed_module.has_begun and not timed_module.has_ended:
# a timed module has been found that is active, so display
# the relevant time:
# module_state_key = timed_module.module_state_key
location = timed_module.location
# when we actually make the state be stored in the StudentModule, then
# we can fetch what we need from that.
# student_module = student_module_cache.lookup(course_id, 'sequential', module_state_key)
# But the module doesn't give us anything helpful to find the corresponding descriptor
# get the corresponding section_descriptor for this timed_module entry:
section_descriptor = modulestore().get_instance(course_id, Location(location))
# determine where to go when the timer expires:
# Note that if we could get this from the timed_module, we wouldn't have to
# fetch the section_descriptor in the first place.
if 'time_expired_redirect_url' not in section_descriptor.metadata:
raise Http404
time_expired_redirect_url = section_descriptor.metadata.get('time_expired_redirect_url')
context['time_expired_redirect_url'] = time_expired_redirect_url
# Fetch the end time (in GMT) as stored in the module when it was started.
# This value should be UTC time as number of milliseconds since epoch.
end_date = timed_module.get_end_time_in_ms()
context['timer_expiration_datetime'] = end_date
if 'suppress_toplevel_navigation' in section_descriptor.metadata:
context['suppress_toplevel_navigation'] = section_descriptor.metadata['suppress_toplevel_navigation']
return_url = reverse('jump_to', kwargs={'course_id': course_id, 'location': location})
context['timer_navigation_return_url'] = return_url
chapter_descriptor = course.get_child_by(lambda m: m.url_name == chapter)
if chapter_descriptor is not None:
instance_module = get_instance_module(course_id, request.user, course_module, student_module_cache)
......@@ -416,7 +453,7 @@ def timed_exam(request, course_id, chapter, section):
# get corresponding time module, if one is present:
try:
timed_module = TimedModule.objects.get(student=request.user, course_id=course_id, module_state_key=section_module.id)
timed_module = TimedModule.objects.get(student=request.user, course_id=course_id, location=section_module.location)
# if a module exists, check to see if it has already been started,
# and if it has already ended.
......@@ -446,7 +483,7 @@ def timed_exam(request, course_id, chapter, section):
except TimedModule.DoesNotExist:
# no entry found. So we're starting this test
# without any accommodations being preset.
timed_module = TimedModule(student=request.user, course_id=course_id, module_state_key=section_module.id)
timed_module = TimedModule(student=request.user, course_id=course_id, location=section_module.location)
timed_module.begin(duration)
timed_module.save()
......@@ -457,9 +494,12 @@ def timed_exam(request, course_id, chapter, section):
end_date = timed_module.get_end_time_in_ms()
# This value should be UTC time as number of milliseconds since epoch.
context['end_date'] = end_date
result = render_to_response('courseware/testcenter_exam.html', context)
# context['end_date'] = end_date
context['timer_expiration_datetime'] = end_date
if 'suppress_toplevel_navigation' in section_descriptor.metadata:
context['suppress_toplevel_navigation'] = section_descriptor.metadata['suppress_toplevel_navigation']
result = render_to_response('courseware/courseware.html', context)
except Exception as e:
if isinstance(e, Http404):
# let it propagate
......
......@@ -59,12 +59,75 @@
});
});
</script>
% if timer_expiration_datetime:
<script type="text/javascript">
var timer = {
timer_inst : null,
get_remaining_secs : function() {
// set the end time when the template is rendered.
// This value should be UTC time as number of milliseconds since epoch.
var endTime = new Date(${timer_expiration_datetime});
var currentTime = new Date();
var remaining_secs = Math.floor((endTime - currentTime)/1000);
return remaining_secs;
},
get_time_string : function() {
function pretty_time_string(num) {
return ( num < 10 ? "0" : "" ) + num;
}
// count down in terms of hours, minutes, and seconds:
var hours = pretty_time_string(Math.floor(remaining_secs / 3600));
remaining_secs = remaining_secs % 3600;
var minutes = pretty_time_string(Math.floor(remaining_secs / 60));
remaining_secs = remaining_secs % 60;
var seconds = pretty_time_string(Math.floor(remaining_secs));
var remainingTimeString = hours + ":" + minutes + ":" + seconds;
return remainingTimeString;
},
update_time : function(self) {
remaining_secs = self.get_remaining_secs();
if (remaining_secs <= 0) {
self.end(self);
}
$('#exam_timer').text(self.get_time_string(remaining_secs));
},
start : function() { var that = this;
this.timer_inst = setInterval(function(){ that.update_time(that); }, 1000);
},
end : function(self) {
clearInterval(self.timer_inst);
// redirect to specified URL:
window.location = "${time_expired_redirect_url}";
}
}
// start timer right away:
timer.start();
</script>
% endif
</%block>
<%include file="/courseware/course_navigation.html" args="active_page='courseware'" />
% if timer_expiration_datetime:
<div class="timer-main">
<div id="timer_wrapper">
<div class="timer_label">Time Remaining:</div> <div id="exam_timer" class="timer_value">&nbsp;</div>
% if timer_navigation_return_url:
<a href="${timer_navigation_return_url}" class="timer_return_url">Return...</a>
% endif
</div>
</div>
% endif
% if accordion:
<%include file="/courseware/course_navigation.html" args="active_page='courseware'" />
% endif
<section class="container">
<div class="course-wrapper">
% if accordion:
<section aria-label="Course Navigation" class="course-index">
<header id="open_close_accordion">
<a href="#">close</a>
......@@ -76,6 +139,7 @@
</nav>
</div>
</section>
% endif
<section class="course-content">
${content}
......
......@@ -29,13 +29,18 @@
<body class="<%block name='bodyclass'/>">
% if not suppress_toplevel_navigation:
<%include file="navigation.html" />
% endif
<section class="content-wrapper">
${self.body()}
<%block name="bodyextra"/>
</section>
% if not suppress_toplevel_navigation:
<%include file="footer.html" />
% endif
<%static:js group='application'/>
<%static:js group='module-js'/>
......
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