-
Notifications
You must be signed in to change notification settings - Fork 7
Update Canvas and Gradescope integrations to handle late due dates for extensions #272
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
cycomachead
merged 24 commits into
main
from
update-canvas-and-gradescope-integrations-to-handle-late-due-dates-for-extensions-hWNcq7Kk6T7H
Feb 27, 2026
Merged
Changes from all commits
Commits
Show all changes
24 commits
Select commit
Hold shift + click to select a range
234c217
feat: handle late due dates for Canvas and Gradescope extensions
cycomachead 2664d71
Merge branch 'main' into update-canvas-and-gradescope-integrations-to…
cycomachead 4574e0f
delint part 1
cycomachead 4822ba8
cleanup lints
cycomachead 941c0cd
run migrations
cycomachead 112ba7d
fixup spec
cycomachead 328710c
delete test case which seems impossible
cycomachead 2cef963
cleanup the Canvas facade to not always send a late due date
cycomachead 2ebc83c
ignore claude
cycomachead 9a78f98
Reorder settings
cycomachead 476234e
feat: handle late due dates for Canvas and Gradescope extensions
cycomachead 5ebc78a
delint part 1
cycomachead 906a015
cleanup lints
cycomachead faad0f6
run migrations
cycomachead c10d861
fixup spec
cycomachead 0836034
delete test case which seems impossible
cycomachead 177f7c2
cleanup the Canvas facade to not always send a late due date
cycomachead 94d0fe5
ignore claude
cycomachead 3ff8d6c
Reorder settings
cycomachead 01dbacc
refactor: extract extension date logic to AssignmentDateCalculator
cycomachead 1c1e428
merge ai updates
cycomachead 5099035
minor delint
cycomachead 2d52f3e
tidy specs
cycomachead 236ec5a
Tidy rubocop
cycomachead File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| # Calculates the dates to be set on an external LMS when provisioning an extension. | ||
| # | ||
| # This service encapsulates the logic for determining: | ||
| # - release_date: When the assignment becomes available (currently always nil) | ||
| # - due_date: The new due date from the approved extension request | ||
| # - late_due_date: The deadline for late submissions, calculated based on course settings | ||
| # | ||
| # The late_due_date logic: | ||
| # - If the assignment has no late_due_date, returns nil (no late due date should be set) | ||
| # - If extend_late_due_date setting is true (default), shifts the late due date by the same | ||
| # delta as the extension (preserving the gap between due date and late due date) | ||
| # - If extend_late_due_date setting is false, returns the later of the original late due date | ||
| # and the new extended due date | ||
| class AssignmentDateCalculator | ||
| attr_reader :assignment, :request, :course_settings | ||
|
|
||
| # @param assignment [Assignment] The assignment being extended | ||
| # @param request [Request] The extension request with the new requested_due_date | ||
| # @param course_settings [CourseSettings, nil] The course settings (may be nil) | ||
| def initialize(assignment:, request:, course_settings:) | ||
| @assignment = assignment | ||
| @request = request | ||
| @course_settings = course_settings | ||
| end | ||
|
|
||
| # Returns the calculated dates for the extension | ||
| # @return [Hash] with keys :release_date, :due_date, :late_due_date | ||
| def calculate | ||
| { | ||
| release_date: release_date, | ||
| due_date: due_date, | ||
| late_due_date: late_due_date | ||
| } | ||
| end | ||
|
|
||
| # The release date for the assignment (currently always nil) | ||
| # @return [nil] | ||
| def release_date | ||
| nil | ||
| end | ||
|
|
||
| # The new due date from the extension request | ||
| # @return [DateTime] | ||
| def due_date | ||
| request.requested_due_date | ||
| end | ||
|
|
||
| # Calculates the new late due date for an extension based on course settings. | ||
| # Returns nil if the assignment has no late due date. | ||
| # @return [DateTime, nil] | ||
| def late_due_date | ||
| original_late_due_date = assignment.late_due_date | ||
| return nil if original_late_due_date.blank? | ||
|
|
||
| if extend_late_due_date? | ||
| # Shift the late due date by the same delta as the extension | ||
| extension_delta = request.requested_due_date - assignment.due_date | ||
| original_late_due_date + extension_delta | ||
| else | ||
| # Return the later of the original late due date and the new extended due date | ||
| [ original_late_due_date, request.requested_due_date ].max | ||
| end | ||
| end | ||
|
|
||
| private | ||
|
|
||
| # Determines whether to extend the late due date by the same delta as the extension. | ||
| # Defaults to true if the setting is nil (for backwards compatibility). | ||
| # @return [Boolean] | ||
| def extend_late_due_date? | ||
| setting = course_settings&.extend_late_due_date | ||
| # Default to true if setting is nil (for backwards compatibility) | ||
| setting.nil? ? true : setting | ||
| end | ||
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
5 changes: 5 additions & 0 deletions
5
db/migrate/20260202000001_add_extend_late_due_date_to_course_settings.rb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| class AddExtendLateDueDateToCourseSettings < ActiveRecord::Migration[7.2] | ||
| def change | ||
| add_column :course_settings, :extend_late_due_date, :boolean, default: true, null: false | ||
| end | ||
| end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| namespace :course_settings do | ||
| desc 'Backfill extend_late_due_date setting for all existing courses (sets to true for all courses)' | ||
| task backfill_extend_late_due_date: :environment do | ||
| puts 'Starting backfill of extend_late_due_date setting for all courses...' | ||
|
|
||
| total_courses = Course.count | ||
| updated_count = 0 | ||
| skipped_count = 0 | ||
| created_count = 0 | ||
|
|
||
| Course.find_each do |course| | ||
| if course.course_settings.nil? | ||
| # Create course settings if they don't exist | ||
| CourseSettings.create!(course: course, extend_late_due_date: true) | ||
| created_count += 1 | ||
| puts "Created course settings for course #{course.id} (#{course.course_name})" | ||
| elsif course.course_settings.extend_late_due_date.nil? | ||
| # Update existing course settings if extend_late_due_date is nil | ||
| course.course_settings.update!(extend_late_due_date: true) | ||
| updated_count += 1 | ||
| puts "Updated course #{course.id} (#{course.course_name})" | ||
| else | ||
| skipped_count += 1 | ||
| end | ||
| end | ||
|
|
||
| puts "\nBackfill complete!" | ||
| puts "Total courses: #{total_courses}" | ||
| puts "Settings created: #{created_count}" | ||
| puts "Settings updated: #{updated_count}" | ||
| puts "Skipped (already set): #{skipped_count}" | ||
| end | ||
| end |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.