diff --git a/Gemfile b/Gemfile index 51afecde..a1a0e62f 100644 --- a/Gemfile +++ b/Gemfile @@ -36,6 +36,7 @@ gem 'bootstrap3-datetimepicker-rails' # for the datetime widget gem 'bootstrap-toggle-rails' # for toggle buttons instead of checkboxes gem 'momentjs-rails', '>= 2.9.0' # needed for human-friendly textual dates +gem 'moment_timezone-rails' gem 'font-awesome-rails' gem 'addressable' diff --git a/Gemfile.lock b/Gemfile.lock index 41e874ab..bc20d9f4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -168,6 +168,7 @@ GEM builder minitest (>= 5.0) ruby-progressbar + moment_timezone-rails (0.5.0) momentjs-rails (2.20.1) railties (>= 3.1) net-ldap (0.16.2) @@ -330,6 +331,7 @@ DEPENDENCIES launchy listen minitest-reporters + moment_timezone-rails momentjs-rails (>= 2.9.0) passenger (>= 5.0.25) pg diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index a306279c..8e243976 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -21,6 +21,7 @@ //= require jquery.keyDecoder //= require cocoon //= require moment +//= require moment-timezone-with-data //= require bootstrap-sprockets //= require bootstrap-datetimepicker //= require bootstrap.treeview @@ -172,6 +173,24 @@ function makeFriendlyDate(str, showTime) { } } +// convert human-readable datetimes to ISO8601 times, including timezone +function makeDatesIso8601($dates) { + var savedDates = []; + $dates.each(function(index, dtp) { + var $dtp = $(dtp); + savedDates[index] = $dtp.val(); + $dtp.val($dtp.data("DateTimePicker").date().format()); + }); + return savedDates; +} + +function restoreDatesFromIso8601($dates, savedDates) { + $dates.each(function(index, dtp) { + $(dtp).val(savedDates[index]); + }); +} + + function activate_file_picker($e) { $e.find(".custom-file").change(function() { var label = $(this).val().replace(/\\/g, '/').replace(/.*\//, ''); diff --git a/app/assets/javascripts/assignments.js b/app/assets/javascripts/assignments.js index 044ad413..1ece2b80 100644 --- a/app/assets/javascripts/assignments.js +++ b/app/assets/javascripts/assignments.js @@ -140,7 +140,8 @@ function init_datetime() { $('.datetime-picker').datetimepicker({ sideBySide: true, - format: "YYYY/MM/DD h:mm A", + format: "YYYY/MM/DD h:mm A z", + timeZone: moment.tz.guess(), defaultDate: undefined }); } @@ -240,6 +241,7 @@ return false; } } + makeDatesIso8601($(".datetime-picker")); return true; }); diff --git a/app/models/course.rb b/app/models/course.rb index 1f402970..62c19d52 100644 --- a/app/models/course.rb +++ b/app/models/course.rb @@ -205,6 +205,7 @@ def score_summary(for_students = nil) if sub.score.nil? if assns[sub.assignment_id].extra_credit extra_adjust += assns[sub.assignment_id].points_available + extra_avail -= assns[sub.assignment_id].points_available # Don't double-count else adjust += assns[sub.assignment_id].points_available end @@ -223,10 +224,10 @@ def score_summary(for_students = nil) unsubs += o.points_available end end - max = min + remaining + extra_adjust + adjust + unsubs + max = min + remaining + extra_adjust + adjust + unsubs + extra_avail {s: s, dropped: dropped, min: min * (100.0 / total_points), cur: cur * (100.0 / total_points), max: max * (100.0 / total_points), - pending: adjust + extra_adjust, pending_names: pending_names, + pending: adjust + extra_adjust + extra_avail, pending_names: pending_names, unsub: unsubs, unsub_names: unsub_names, remaining: remaining, used: used} end diff --git a/app/views/individual_extensions/_edit.js.erb b/app/views/individual_extensions/_edit.js.erb index 1c060d7a..2e03a258 100644 --- a/app/views/individual_extensions/_edit.js.erb +++ b/app/views/individual_extensions/_edit.js.erb @@ -7,7 +7,8 @@ $(function() { $initialFilled.closest("tr").find("button.revoke").removeClass("hidden"); $('.datetime-picker').datetimepicker({ sideBySide: true, - format: "YYYY/MM/DD h:mm A", + format: "YYYY/MM/DD h:mm A z", + timeZone: moment.tz.guess(), defaultDate: undefined }); $("form").map(function() { @@ -24,11 +25,15 @@ $(function() { var $valuesToSubmit = $row.find("form"); var $check = $row.find("#success_" + $valuesToSubmit[0].id); var $fail = $row.find("#failure_" + $valuesToSubmit[0].id); + var $dates = $row.find(".datetime-picker"); + var savedDates = makeDatesIso8601($dates); + var data = $valuesToSubmit.serialize(); + restoreDatesFromIso8601($dates, savedDates); debugger $.ajax({ type: "PATCH", url: "<%= patch_course_assignment_extensions_path(@course, @assignment) %>", - data: $valuesToSubmit.serialize(), + data: data, dataType: "JSON", timeout: 500 }).done(function(json) { @@ -55,10 +60,14 @@ $(function() { var $valuesToSubmit = $row.find("form"); var $check = $row.find("#success_" + $valuesToSubmit[0].id); var $fail = $row.find("#failure_" + $valuesToSubmit[0].id); + var $dates = $row.find(".datetime-picker"); + var savedDates = makeDatesIso8601($dates); + var data = $valuesToSubmit.serialize(); + restoreDatesFromIso8601($dates, savedDates); $.ajax({ type: "DELETE", url: "<%= delete_course_assignment_extensions_path(@course, @assignment) %>", - data: $valuesToSubmit.serialize(), + data: data, dataType: "JSON", timeout: 500 }).done(function(json) { diff --git a/app/views/submissions/_new_common.html.erb b/app/views/submissions/_new_common.html.erb index 120feafd..551103c6 100644 --- a/app/views/submissions/_new_common.html.erb +++ b/app/views/submissions/_new_common.html.erb @@ -57,7 +57,7 @@

If lateness is not ignored, this <%= type[:noun] %> should claim to have been submitted at:

- <%= f.text_field :created_at, class: 'form-control', disabled: "disabled" %> + <%= f.text_field :created_at, class: 'form-control datetime-picker', disabled: "disabled" %>
<% end %>
@@ -70,7 +70,8 @@ $(function () { $('#submission_created_at').datetimepicker({ sideBySide: true, - format: "YYYY/MM/DD h:mm A", + format: "YYYY/MM/DD h:mm A z", + timeZone: moment.tz.guess(), minDate: "<%= @course.created_at.beginning_of_day.iso8601 %>", <% if @course.created_at < DateTime.current %> defaultDate: "<%= DateTime.current.iso8601 %>" diff --git a/app/views/submissions/new_codereview.html.erb b/app/views/submissions/new_codereview.html.erb index a1dbf659..57f6e45a 100644 --- a/app/views/submissions/new_codereview.html.erb +++ b/app/views/submissions/new_codereview.html.erb @@ -180,7 +180,11 @@ warning.find("h3").removeClass("hidden"); }, Math.max(deadline - now, 0)); $("form").submit(function(e) { - return ensureValidNumericInputOnSubmit(e, "input.numeric:not([disabled])"); + var num = ensureValidNumericInputOnSubmit(e, "input.numeric:not([disabled])"); + if (num) { + makeDatesIso8601($(".datetime-picker")); + } + return num; }); <% end %><%# content-for %> diff --git a/app/views/submissions/new_files.html.erb b/app/views/submissions/new_files.html.erb index d0d17570..54bf9bdf 100644 --- a/app/views/submissions/new_files.html.erb +++ b/app/views/submissions/new_files.html.erb @@ -97,6 +97,9 @@ $("form").submit(function(e) { var num = ensureValidNumericInputOnSubmit(e, "span.spinner > input"); var files = ensureFilesPresentOnSubmit(e); + if (num && files) { + makeDatesIso8601($(".datetime-picker")); + } return num && files; }); diff --git a/app/views/submissions/new_questions.html.erb b/app/views/submissions/new_questions.html.erb index 209d6adc..acdf6b64 100644 --- a/app/views/submissions/new_questions.html.erb +++ b/app/views/submissions/new_questions.html.erb @@ -108,7 +108,11 @@ warning.find("h3").removeClass("hidden"); }, Math.max(deadline - now, 0)); $("form").submit(function(e) { - return ensureValidNumericInputOnSubmit(e, "input.numeric:not([disabled])"); + var num = ensureValidNumericInputOnSubmit(e, "input.numeric:not([disabled])"); + if (num) { + makeDatesIso8601($(".datetime-picker")); + } + return num; }); <% end %> diff --git a/test/integration/submissions_test.rb b/test/integration/submissions_test.rb index 658d7498..e336b0f2 100644 --- a/test/integration/submissions_test.rb +++ b/test/integration/submissions_test.rb @@ -161,10 +161,10 @@ def clean(summary) @as3.reload # needed for the lateness config @summary = clean(@cs101.score_summary(@john)) assert_equal(@summary[@john.id], - {dropped: nil, min: 7.5, cur: 75.0, max: 97.5, + {dropped: nil, min: 7.5, cur: 75.0, max: 102.5, pending: 0.0, pending_names: [], unsub: 0.0, unsub_names: [], remaining: 90.0}, - "After creating an extra credit assignment, but not submitting, nothing should change in the grades") + "After creating an extra credit assignment, but not submitting, max goes up by the available e.c.") @sub3 = create(:submission, user: @john, assignment: @as3, created_at: Time.now - 2.days) @sub3.set_used_sub!