-
Notifications
You must be signed in to change notification settings - Fork 172
E2566 Finish Due Dates #234
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
base: main
Are you sure you want to change the base?
Changes from 3 commits
e3d03c9
0049749
e2e1540
ec7a755
9937b35
8f0fc6b
29f4ba3
096561b
ab83d49
40e9aef
0c6907e
c02ae6d
da53025
58fbb12
a5ca117
d2a5841
9eb9a49
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| module DueDateActions | ||
| included do | ||
| has_many :due_dates, as: :parent, dependent: :destroy | ||
| end | ||
|
|
||
| # Generic activity permission checker that determines if an activity is permissible | ||
| # based on the current deadline state for this parent object | ||
| def activity_permissible?(activity) | ||
| current_deadline = next_due_date | ||
| return false unless current_deadline | ||
|
|
||
| current_deadline.activity_permissible?(activity) | ||
| end | ||
|
|
||
| # Activity permission checker for a specific deadline date (not current date) | ||
| def activity_permissible_for?(activity, deadline_date) | ||
| deadline = due_dates.where('due_at <= ?', deadline_date).order(:due_at).last | ||
| return false unless deadline | ||
|
|
||
| deadline.activity_permissible?(activity) | ||
| end | ||
|
|
||
| # Syntactic sugar methods for common activities | ||
| def submission_permissible? | ||
| activity_permissible?(:submission) | ||
| end | ||
|
|
||
| def review_permissible? | ||
| activity_permissible?(:review) | ||
| end | ||
|
|
||
| def teammate_review_permissible? | ||
| activity_permissible?(:teammate_review) | ||
| end | ||
|
|
||
| def quiz_permissible? | ||
| activity_permissible?(:quiz) | ||
| end | ||
|
|
||
| def team_formation_permissible? | ||
| activity_permissible?(:team_formation) | ||
| end | ||
|
|
||
| def signup_permissible? | ||
| activity_permissible?(:signup) | ||
| end | ||
|
|
||
| def drop_topic_permissible? | ||
| activity_permissible?(:drop_topic) | ||
| end | ||
|
|
||
| # Get the next due date for this parent | ||
| def next_due_date | ||
| due_dates.where('due_at >= ?', Time.current).order(:due_at).first | ||
| end | ||
|
|
||
| # Get the current stage name for display purposes | ||
| def current_stage | ||
| deadline = next_due_date | ||
| return 'finished' unless deadline | ||
|
|
||
| deadline.deadline_type&.name || 'unknown' | ||
| end | ||
|
|
||
| # Copy due dates to a new parent object | ||
| def copy_due_dates_to(new_parent) | ||
| due_dates.find_each do |due_date| | ||
| new_due_date = due_date.dup | ||
| new_due_date.parent = new_parent | ||
| new_due_date.save! | ||
| end | ||
| end | ||
|
|
||
| # Shift deadlines of a specific type by a time interval | ||
| def shift_deadlines_of_type(deadline_type_name, time_interval) | ||
|
||
| due_dates.joins(:deadline_type) | ||
| .where(deadline_types: { name: deadline_type_name }) | ||
| .update_all("due_at = due_at + INTERVAL #{time_interval.to_i} SECOND") | ||
| end | ||
|
|
||
| # Check if deadlines are in proper chronological order | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this method is useful. You can put them in order with a single statement. If you wanted them to be in order, you'd just sort them. Please remove this method. |
||
| def deadlines_properly_ordered? | ||
| sorted_deadlines = due_dates.order(:due_at) | ||
| previous_date = nil | ||
|
|
||
| sorted_deadlines.each do |deadline| | ||
| return false if previous_date && deadline.due_at < previous_date | ||
| previous_date = deadline.due_at | ||
| end | ||
|
|
||
| true | ||
| end | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,95 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| module DueDatePermissions | ||
| # Permission checking methods that combine deadline-based and role-based logic | ||
|
|
||
| def can_submit? | ||
|
||
| return false unless submission_allowed_id | ||
|
|
||
| deadline_right = DeadlineRight.find_by(id: submission_allowed_id) | ||
| deadline_right&.name&.in?(%w[OK Late]) | ||
| end | ||
|
|
||
| def can_review? | ||
| return false unless review_allowed_id | ||
|
|
||
| deadline_right = DeadlineRight.find_by(id: review_allowed_id) | ||
| deadline_right&.name&.in?(%w[OK Late]) | ||
| end | ||
|
|
||
| def can_take_quiz? | ||
| return false unless quiz_allowed_id | ||
|
|
||
| deadline_right = DeadlineRight.find_by(id: quiz_allowed_id) | ||
| deadline_right&.name&.in?(%w[OK Late]) | ||
| end | ||
|
|
||
| def can_teammate_review? | ||
|
||
| return false unless teammate_review_allowed_id | ||
|
|
||
| deadline_right = DeadlineRight.find_by(id: teammate_review_allowed_id) | ||
| deadline_right&.name&.in?(%w[OK Late]) | ||
| end | ||
|
|
||
| # Generic permission checker | ||
| def activity_permissible?(activity) | ||
| permission_field = "#{activity}_allowed_id" | ||
| return false unless respond_to?(permission_field) | ||
|
|
||
| allowed_id = public_send(permission_field) | ||
| return false unless allowed_id | ||
|
|
||
| deadline_right = DeadlineRight.find_by(id: allowed_id) | ||
| deadline_right&.name&.in?(%w[OK Late]) | ||
| end | ||
|
|
||
| # Check if deadline allows late submissions | ||
SeojinSeojin marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| def allows_late_submission? | ||
| return false unless submission_allowed_id | ||
|
|
||
| deadline_right = DeadlineRight.find_by(id: submission_allowed_id) | ||
| deadline_right&.name == 'Late' | ||
| end | ||
|
|
||
| def allows_late_review? | ||
| return false unless review_allowed_id | ||
|
|
||
| deadline_right = DeadlineRight.find_by(id: review_allowed_id) | ||
| deadline_right&.name == 'Late' | ||
| end | ||
|
|
||
| def allows_late_quiz? | ||
| return false unless quiz_allowed_id | ||
|
|
||
| deadline_right = DeadlineRight.find_by(id: quiz_allowed_id) | ||
| deadline_right&.name == 'Late' | ||
| end | ||
|
|
||
| # Check if this deadline is currently active (allows some action) | ||
|
||
| def active? | ||
| can_submit? || can_review? || can_take_quiz? || can_teammate_review? | ||
| end | ||
|
|
||
| # Get permission status for an action (OK, Late, No) | ||
| def permission_status_for(action) | ||
| permission_field = "#{action}_allowed_id" | ||
| return 'No' unless respond_to?(permission_field) | ||
|
|
||
| allowed_id = public_send(permission_field) | ||
| return 'No' unless allowed_id | ||
|
|
||
| deadline_right = DeadlineRight.find_by(id: allowed_id) | ||
| deadline_right&.name || 'No' | ||
| end | ||
|
|
||
| # Get a summary of all permissions for this deadline | ||
|
||
| def permissions_summary | ||
| { | ||
| submission: permission_status_for(:submission), | ||
| review: permission_status_for(:review), | ||
| quiz: permission_status_for(:quiz), | ||
| teammate_review: permission_status_for(:teammate_review), | ||
| active: active? | ||
|
||
| } | ||
| end | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| module DueDateQueries | ||
|
||
| # Get next due date for this parent | ||
|
||
| def next_due_date(topic_id = nil) | ||
| if topic_id && has_topic_specific_deadlines? | ||
| topic_deadline = due_dates.where(parent_id: topic_id, parent_type: 'SignUpTopic') | ||
|
||
| .where('due_at >= ?', Time.current) | ||
| .order(:due_at) | ||
| .first | ||
| return topic_deadline if topic_deadline | ||
| end | ||
|
|
||
| due_dates.where('due_at >= ?', Time.current).order(:due_at).first | ||
| end | ||
|
|
||
| # Get all deadlines for a topic (topic-specific + assignment fallback) | ||
|
||
| def deadlines_for_topic(topic_id) | ||
| assignment_deadlines = due_dates.where(parent_type: 'Assignment') | ||
| topic_deadlines = due_dates.where(parent_id: topic_id, parent_type: 'SignUpTopic') | ||
|
|
||
| (assignment_deadlines + topic_deadlines).sort_by(&:due_at) | ||
| end | ||
|
|
||
| # Check if assignment has topic-specific deadlines | ||
|
||
| def has_topic_specific_deadlines? | ||
| due_dates.where(parent_type: 'SignUpTopic').exists? | ||
| end | ||
|
|
||
| private | ||
|
|
||
| # Map action names to deadline type names for lookup | ||
| def action_to_deadline_type(action) | ||
| { | ||
| 'submit' => 'submission', | ||
| 'submission' => 'submission', | ||
| 'review' => 'review', | ||
| 'teammate_review' => 'teammate_review', | ||
| 'quiz' => 'quiz', | ||
| 'team_formation' => 'team_formation', | ||
| 'signup' => 'signup', | ||
| 'drop_topic' => 'drop_topic' | ||
| }[action.to_s.downcase] | ||
| end | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,51 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| class DeadlineRight < ApplicationRecord | ||
| validates :name, presence: true, uniqueness: true | ||
|
|
||
| # Class methods for finding deadline rights | ||
|
||
| def self.find_by_name(name) | ||
| find_by(name: name.to_s) | ||
| end | ||
|
|
||
| # Permission checking methods | ||
| def allows_action? | ||
| %w[OK Late].include?(name) | ||
| end | ||
|
|
||
| def denies_action? | ||
| name == 'No' | ||
| end | ||
|
|
||
| def allows_with_penalty? | ||
| name == 'Late' | ||
| end | ||
|
|
||
| def allows_without_penalty? | ||
| name == 'OK' | ||
| end | ||
|
|
||
| # Display methods | ||
| def to_s | ||
| name | ||
| end | ||
|
|
||
| def permission_level | ||
| case name | ||
| when 'No' | ||
| 0 | ||
| when 'Late' | ||
| 1 | ||
| when 'OK' | ||
| 2 | ||
| else | ||
| -1 | ||
| end | ||
| end | ||
|
|
||
| def <=>(other) | ||
| return nil unless other.is_a?(DeadlineRight) | ||
|
|
||
| permission_level <=> other.permission_level | ||
| end | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,63 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| class DeadlineType < ApplicationRecord | ||
| validates :name, presence: true, uniqueness: true | ||
| validates :description, presence: true | ||
|
|
||
| has_many :due_dates, foreign_key: :deadline_type_id, dependent: :restrict_with_exception | ||
|
|
||
| # Class methods for finding deadline types | ||
|
||
| def self.find_by_name(name) | ||
| find_by(name: name.to_s) | ||
| end | ||
|
|
||
| # Semantic helper methods for deadline type identification | ||
| def submission? | ||
| name == 'submission' | ||
| end | ||
|
|
||
| def review? | ||
| name == 'review' | ||
| end | ||
|
|
||
| def teammate_review? | ||
| name == 'teammate_review' | ||
| end | ||
|
|
||
| def quiz? | ||
| name == 'quiz' | ||
| end | ||
|
|
||
| def team_formation? | ||
| name == 'team_formation' | ||
| end | ||
|
|
||
| def signup? | ||
| name == 'signup' | ||
| end | ||
|
|
||
| def drop_topic? | ||
| name == 'drop_topic' | ||
| end | ||
|
|
||
| # Display methods | ||
| def display_name | ||
| name.humanize | ||
| end | ||
|
|
||
| def to_s | ||
| display_name | ||
| end | ||
|
|
||
| private | ||
|
|
||
| # Ensure we maintain referential integrity | ||
| def cannot_delete_if_has_due_dates | ||
| return unless due_dates.exists? | ||
|
|
||
| errors.add(:base, 'Cannot delete deadline type that has associated due dates') | ||
| throw :abort | ||
| end | ||
|
|
||
| before_destroy :cannot_delete_if_has_due_dates | ||
| end | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
deadline --> upcoming_due_date