<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.expertiza.ncsu.edu/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Skim253</id>
	<title>Expertiza_Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.expertiza.ncsu.edu/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Skim253"/>
	<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=Special:Contributions/Skim253"/>
	<updated>2026-05-16T09:56:15Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.41.0</generator>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167301</id>
		<title>CSC/ECE 517 Fall 2025 - E2566. Finish DueDates</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167301"/>
		<updated>2025-12-02T23:03:37Z</updated>

		<summary type="html">&lt;p&gt;Skim253: /* Demo */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
This project aims to complete the implementation of the DueDate system in Expertiza. The current implementation consists mostly of class methods and lacks proper definition of different deadline types and permission checking functionality. This redesign will create a more robust, readable, and maintainable due date system.&lt;br /&gt;
&lt;br /&gt;
=== Existing Components ===&lt;br /&gt;
* &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model with polymorphic parent association (Assignment/SignUpTopic)&lt;br /&gt;
* &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; subclasses&lt;br /&gt;
* Basic CRUD operations (within &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AssignmentForm&amp;lt;/code&amp;gt;) and sorting functionality (&amp;lt;code&amp;gt;deadline_sort&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Database schema with &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt; field&lt;br /&gt;
&lt;br /&gt;
=== Current Behavior ===&lt;br /&gt;
&lt;br /&gt;
====Admin's View====&lt;br /&gt;
[[File:Duedates.png|800px]]&lt;br /&gt;
&lt;br /&gt;
When editing an assignment, due dates can be modified under the Due Dates tab. Each row contains the following columns: &amp;lt;code&amp;gt;Date &amp;amp; Time&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Use Date Updater&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Submission Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Teammate Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Meta-Review Allowed&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Reminder&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:Assignments-CurrentStage.png|800px]]&lt;br /&gt;
&lt;br /&gt;
In the Assignments tab, the current stage and stage deadline of each assignment is displayed.&lt;br /&gt;
&lt;br /&gt;
[[File:Due-date-topic-instructor.png|800px]]&lt;br /&gt;
&lt;br /&gt;
[[File:Due-date-topic-instructor-2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
In the Topics subtab, due dates for each topic are displayed.&lt;br /&gt;
&lt;br /&gt;
====Student's View====&lt;br /&gt;
[[File:Student-assignments.png|800px]]&lt;br /&gt;
&lt;br /&gt;
Students can view due date information under &amp;lt;code&amp;gt;Current State&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Stage Deadline&amp;lt;/code&amp;gt;. The actions available to students are determined by the current DueDate status.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-not-over.png|800px]]&lt;br /&gt;
&lt;br /&gt;
During the submission period, students can access the &amp;lt;code&amp;gt;Submit a hyperlink&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Submit a file&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-over.png|800px]]&lt;br /&gt;
&lt;br /&gt;
Once the due date has passed, students cannot submit either a hyperlink or a file.&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
&lt;br /&gt;
Currently, there are some glaring issues with how due_dates was created in the first place, including redundancy and lack of modularity.&lt;br /&gt;
&lt;br /&gt;
'''Modeling &amp;amp; Structural Issues'''&lt;br /&gt;
# No dedicated &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to define different kinds of deadlines. The existing &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; class only serves as an association for &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; models, and is never referenced in the current &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; implementation.&lt;br /&gt;
# Duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table, which may lead to potential bugs.&lt;br /&gt;
# Incomplete deadline type definitions — &amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt; need to be added.&lt;br /&gt;
# No clear separation of concerns — data persistence, business logic, and permission logic are mixed in a single model.&lt;br /&gt;
&lt;br /&gt;
'''Permission &amp;amp; Behavior Issues'''&lt;br /&gt;
# Overuse of class methods making the code difficult to maintain.&lt;br /&gt;
# Missing permission checking logic for user actions that combines both role-based and time-based constraints.&lt;br /&gt;
&lt;br /&gt;
=== Tasks Identified ===&lt;br /&gt;
&lt;br /&gt;
'''Modeling &amp;amp; Structural'''&lt;br /&gt;
# Refactor the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model to separate persistence, business logic, and permission logic into distinct layers.&lt;br /&gt;
# Implement reusable mixins (&amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;) to handle permission evaluation and deadline-related operations.&lt;br /&gt;
# Introduce instance-level methods (e.g., &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;) to replace class-level utilities and improve testability.&lt;br /&gt;
&lt;br /&gt;
'''Permission &amp;amp; Behavior'''&lt;br /&gt;
# Integrate role-based and time-based permission checks within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; to ensure consistent access control.&lt;br /&gt;
# Redesign the &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.&lt;br /&gt;
# Add missing deadline types (&amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt;) and remove duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries for data integrity.&lt;br /&gt;
&lt;br /&gt;
== Proposed Solution ==&lt;br /&gt;
&lt;br /&gt;
The proposed solution restructures the due date system by separating concerns across dedicated models and mixin modules. Deadline definitions (&amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt;), core deadline data (&amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;), and behavioral logic (&amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;) are isolated so each component has a single clear responsibility. This modular design improves readability, reduces coupling, and makes deadline-related behavior easier to extend and maintain.&lt;br /&gt;
&lt;br /&gt;
=== Architecture Overview ===&lt;br /&gt;
&lt;br /&gt;
[[File:Duedate-diagram.png|800px]]&lt;br /&gt;
&lt;br /&gt;
This diagram visualizes the redesigned architecture with five distinct layers:&lt;br /&gt;
&lt;br /&gt;
* '''Layer 1 (Blue)''': Parent models (Assignment, SignUpTopic) that own due dates&lt;br /&gt;
* '''Layer 2 (Orange)''': DueDateActions module providing action-level queries&lt;br /&gt;
* '''Layer 3 (Red)''': DueDate model managing core deadline data&lt;br /&gt;
* '''Layer 4 (Orange)''': DueDatePermissions module evaluating permissions&lt;br /&gt;
* '''Layer 5 (Green)''': Reference data models (DeadlineType, DeadlineRight, Participant)&lt;br /&gt;
&lt;br /&gt;
The flow works as follows: Parent models include DueDateActions, which queries DueDate objects. Each DueDate includes DueDatePermissions to evaluate whether actions are allowed based on DeadlineRight states and Participant role flags.&lt;br /&gt;
&lt;br /&gt;
=== DeadlineType Model ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Although the current codebase includes a &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model, it only serves as a passive lookup table. In practice, most parts of the system still rely on hard-coded numeric IDs or manual lookups:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Example of current usage&lt;br /&gt;
deadline_type_id = DeadlineType.find_by(name: 'review').id&lt;br /&gt;
if due_date.deadline_type_id == deadline_type_id&lt;br /&gt;
  # Perform review-related logic&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This leads to several structural issues:&lt;br /&gt;
* Inconsistent references (some use IDs, others strings)&lt;br /&gt;
* Tight coupling between &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
* Lack of semantic helper methods (e.g., &amp;lt;code&amp;gt;review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;submission?&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Data duplication and integrity risks (two &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; rows)&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; Table Structure&lt;br /&gt;
|- &lt;br /&gt;
! Column !! Type !! Description&lt;br /&gt;
|-&lt;br /&gt;
| id || BIGINT || Primary key, referenced by &amp;lt;code&amp;gt;due_dates.deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| name || VARCHAR(255) || Unique symbolic name (e.g., &amp;lt;code&amp;gt;submission&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| description || TEXT || Human-readable explanation of the deadline category&lt;br /&gt;
|-&lt;br /&gt;
| created_at || TIMESTAMP || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| updated_at || TIMESTAMP || Last update timestamp&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Deadline Type Definitions ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Canonical Deadline Type Entries&lt;br /&gt;
|-&lt;br /&gt;
! ID !! Name !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 1 || submission || Student submission deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 2 || review || Peer review deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 3 || teammate_review || Team member evaluation deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 5 || metareview || Meta-review deadlines (backward compatibility)&lt;br /&gt;
|-&lt;br /&gt;
| 6 || drop_topic || Topic drop deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 7 || signup || Topic signup deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 8 || team_formation || Team formation deadlines (duplicate cleaned)&lt;br /&gt;
|-&lt;br /&gt;
| 11 || quiz || Quiz completion deadlines&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== DeadlineRight Model ===&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model represents the permission level for a specific action at a given deadline:&lt;br /&gt;
&lt;br /&gt;
* '''OK''': Action is allowed without penalty&lt;br /&gt;
* '''Late''': Action is allowed but marked as late&lt;br /&gt;
* '''No''': Action is not permitted&lt;br /&gt;
&lt;br /&gt;
These values are referenced by &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; through fields such as &amp;lt;code&amp;gt;submission_allowed_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review_allowed_id&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;quiz_allowed_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_rights&amp;lt;/code&amp;gt; Table Structure&lt;br /&gt;
|-&lt;br /&gt;
! Column !! Type !! Description&lt;br /&gt;
|-&lt;br /&gt;
| id || BIGINT || Primary key, referenced by &amp;lt;code&amp;gt;due_dates.*_allowed_id&amp;lt;/code&amp;gt; fields&lt;br /&gt;
|-&lt;br /&gt;
| name || VARCHAR(255) || Unique permission name (&amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| created_at || TIMESTAMP || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| updated_at || TIMESTAMP || Last update timestamp&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== DueDate Model Refactoring ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model currently mixes persistence logic, deadline calculation, and permission checking, violating the Single Responsibility Principle. Most of its logic is implemented as class methods, which makes testing and extension difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Old design: class method that mixes concerns&lt;br /&gt;
def self.copy(old_assignment_id, new_assignment_id)&lt;br /&gt;
  duedates = where(parent_id: old_assignment_id)&lt;br /&gt;
  duedates.each do |orig_due_date|&lt;br /&gt;
    new_due_date = orig_due_date.dup&lt;br /&gt;
    new_due_date.parent_id = new_assignment_id&lt;br /&gt;
    new_due_date.save&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Improved Design ====&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model is focused on representing a single deadline entry. Behavior has been extracted into appropriate modules:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Responsibility Distribution After Refactoring&lt;br /&gt;
|-&lt;br /&gt;
! Concern !! Where It Lives&lt;br /&gt;
|-&lt;br /&gt;
| Permission checking || &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module (included in DueDate)&lt;br /&gt;
|-&lt;br /&gt;
| Deadline-related actions || &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module (included in parent models)&lt;br /&gt;
|-&lt;br /&gt;
| Deadline type semantics || &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model&lt;br /&gt;
|-&lt;br /&gt;
| Core deadline data || &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Key improvements:&lt;br /&gt;
* '''Instance-based behavior''': Replaced class methods with instance methods&lt;br /&gt;
* '''Clear query methods''': Simple predicates like &amp;lt;code&amp;gt;upcoming?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;overdue?&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Scopes for common queries''': &amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DueDatePermissions Module ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Currently, permission checks are split into two disconnected layers:&lt;br /&gt;
&lt;br /&gt;
'''Role-based permissions''' (in participant_helpers.rb):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def participant_permissions(authorization)&lt;br /&gt;
  case authorization&lt;br /&gt;
  when 'reader' then { can_submit: false, can_review: true, can_take_quiz: true }&lt;br /&gt;
  # ...&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Deadline-based checks''' (in Assignment model):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def submission_allowed(topic_id = nil)&lt;br /&gt;
  check_condition('submission_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These two layers never interact, leading to inconsistencies.&lt;br /&gt;
&lt;br /&gt;
==== Proposed Solution ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module integrates both layers:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Permission Methods&lt;br /&gt;
|-&lt;br /&gt;
! Method !! What It Checks&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_submit?(participant)&amp;lt;/code&amp;gt; || Submission deadline is OK/Late AND participant can submit&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_review?(participant)&amp;lt;/code&amp;gt; || Review deadline is OK/Late AND participant can review&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_take_quiz?(participant)&amp;lt;/code&amp;gt; || Quiz deadline is OK/Late AND participant can take quiz&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_review_teammate?(participant)&amp;lt;/code&amp;gt; || Teammate review deadline is OK/Late AND participant can review&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;activity_permissible?(activity)&amp;lt;/code&amp;gt; || Generic checker for any activity&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;permission_status_for(action)&amp;lt;/code&amp;gt; || Returns 'OK', 'Late', or 'No' for display&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== DueDateActions Module ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module provides a unified interface for models that own due dates (&amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt;). This centralizes the logic for querying upcoming deadlines and determining whether specific activities are currently allowed.&lt;br /&gt;
&lt;br /&gt;
==== Proposed Solution ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Methods in DueDateActions&lt;br /&gt;
|-&lt;br /&gt;
! Method !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;activity_permissible?(activity)&amp;lt;/code&amp;gt; || Checks if an activity is permitted at the current time&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;submission_permissible?&amp;lt;/code&amp;gt; || Convenience wrapper for submission checking&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;review_permissible?&amp;lt;/code&amp;gt; || Convenience wrapper for review checking&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;next_due_date(topic_id)&amp;lt;/code&amp;gt; || Resolves the next applicable deadline&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;current_stage&amp;lt;/code&amp;gt; || Returns human-readable stage name (e.g., &amp;quot;review&amp;quot;, &amp;quot;submission&amp;quot;)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;has_topic_specific_deadlines?&amp;lt;/code&amp;gt; || Determines if assignment uses topic-specific deadlines&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;copy_due_dates_to(new_parent)&amp;lt;/code&amp;gt; || Utility for cloning due dates to another assignment&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Deadline Resolution Flow ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ How next_due_date Works&lt;br /&gt;
|-&lt;br /&gt;
! Step !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 1. Topic-specific check || If topic ID provided and assignment uses staggered deadlines, check topic-level deadlines first&lt;br /&gt;
|-&lt;br /&gt;
| 2. Assignment-level fallback || If no topic deadline exists, use nearest assignment-level deadline&lt;br /&gt;
|-&lt;br /&gt;
| 3. Permission evaluation || Delegate to DueDatePermissions to determine if action is allowed&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Implementation Summary ==&lt;br /&gt;
&lt;br /&gt;
=== Models Created/Modified ===&lt;br /&gt;
&lt;br /&gt;
* '''DeadlineType''' — Canonical deadline type definitions with semantic helper methods&lt;br /&gt;
* '''DeadlineRight''' — Permission state model (OK/Late/No) with comparison methods&lt;br /&gt;
* '''DueDate''' — Refactored with instance methods, scopes, and permission checking&lt;br /&gt;
* '''TopicDueDate''' — STI subclass for topic-specific deadlines&lt;br /&gt;
* '''Assignment''' — Includes DueDateActions mixin&lt;br /&gt;
* '''SignUpTopic''' — Includes DueDateActions mixin&lt;br /&gt;
&lt;br /&gt;
=== Modules Created ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions''' — Permission checking combining deadline and role constraints&lt;br /&gt;
* '''DueDateActions''' — Deadline-related operations for parent models&lt;br /&gt;
&lt;br /&gt;
=== Key Improvements ===&lt;br /&gt;
&lt;br /&gt;
# '''Separation of Concerns''': Permission logic separated into dedicated modules&lt;br /&gt;
# '''Instance Methods''': Replaced class methods with testable instance methods&lt;br /&gt;
# '''Polymorphic Design''': Single DueDate model serves both Assignment and SignUpTopic&lt;br /&gt;
# '''Unified Permissions''': Role-based and time-based checks combined in one interface&lt;br /&gt;
# '''Data Integrity''': Removed duplicate deadline types, enforced foreign key constraints&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
This test suite provides coverage for both the Assignment model and the DueDate model.&lt;br /&gt;
The Assignment tests also exercise all behaviors provided by the DueDateActions mixin, since that module is included directly in the Assignment class.&lt;br /&gt;
&lt;br /&gt;
The tests are designed to verify how deadlines influence allowed activities (submission, review, quiz, etc.), how the system determines the next relevant deadline, and how deadlines are ordered.&lt;br /&gt;
&lt;br /&gt;
=== Coverage Summary ===&lt;br /&gt;
&lt;br /&gt;
==== Assignment (DueDateActions mixin included) ====&lt;br /&gt;
The Assignment spec validates:&lt;br /&gt;
&lt;br /&gt;
activity permission logic:&lt;br /&gt;
* &amp;lt;code&amp;gt;activity_permissible?&amp;lt;/code&amp;gt; – evaluates permissions using the next upcoming deadline&lt;br /&gt;
* &amp;lt;code&amp;gt;activity_permissible_for?&amp;lt;/code&amp;gt; – finds the most recent past deadline relative to a specific time&lt;br /&gt;
&lt;br /&gt;
deadline-based navigation:&lt;br /&gt;
* &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;deadlines_properly_ordered?&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
delegation helpers such as:&lt;br /&gt;
* &amp;lt;code&amp;gt;submission_permissible?&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;review_permissible?&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These tests ensure that Assignment correctly interprets the state of its associated due dates.&lt;br /&gt;
&lt;br /&gt;
==== DueDate Model ====&lt;br /&gt;
The DueDate spec validates:&lt;br /&gt;
&lt;br /&gt;
required field validations (parent, due_at, deadline_type_id)&lt;br /&gt;
round field rules (cannot be zero or negative, sets default when nil)&lt;br /&gt;
scopes:&lt;br /&gt;
* &amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
instance methods:&lt;br /&gt;
* &amp;lt;code&amp;gt;#overdue?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#upcoming?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#set&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#copy&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#deadline_type_name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#last_deadline?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#&amp;lt;=&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
class methods:&lt;br /&gt;
* &amp;lt;code&amp;gt;.sort_due_dates&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.any_future_due_dates?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.copy&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
callback:&lt;br /&gt;
* &amp;lt;code&amp;gt;before_save :set_default_round&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rationale for Test Design ===&lt;br /&gt;
&lt;br /&gt;
The test suites for DueDate and Assignment (including DueDateActions) are designed to validate two different layers of behavior:&lt;br /&gt;
* DueDate tests verify record-level behavior: validations, scopes, and individual instance methods.&lt;br /&gt;
* Assignment tests verify behavior that depends on collections of due dates, such as selecting the next deadline or determining whether an activity is permissible.&lt;br /&gt;
&lt;br /&gt;
All tests use real database records (DeadlineType, DeadlineRight, DueDate) to ensure that associations, validations, and permission checks reflect real application behavior.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
The suite uses nested RSpec contexts:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
describe 'activity_permissible_for?' do&lt;br /&gt;
  context 'when past deadline exists' do&lt;br /&gt;
    it 'returns permission from most recent past deadline'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when only future deadlines exist' do&lt;br /&gt;
    it 'returns false'&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Contexts clearly describe the situation being tested, while individual examples describe expected behaviors.&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
[https://youtu.be/sTrVRGSaBlE Click to watch on YouTube]&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add deadline notification system using the new permission framework&lt;br /&gt;
* Implement deadline templates for common assignment patterns&lt;br /&gt;
* Add API endpoints for mobile app integration&lt;br /&gt;
* Create admin dashboard for monitoring deadline compliance across courses&lt;br /&gt;
* Add automated deadline extension workflows based on configurable rules&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2555._Front_end_for_Participants_Page&amp;diff=167300</id>
		<title>CSC/ECE 517 Fall 2025 - E2555. Front end for Participants Page</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2555._Front_end_for_Participants_Page&amp;diff=167300"/>
		<updated>2025-12-02T23:02:56Z</updated>

		<summary type="html">&lt;p&gt;Skim253: /* Demo Video */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
===Background===&lt;br /&gt;
The Participants page displays a list of users associated with an assignment, including students and their roles.&lt;br /&gt;
&lt;br /&gt;
===Motivation===&lt;br /&gt;
&lt;br /&gt;
* '''Modernize the interface:''' Simplify the Participants table by reducing unnecessary columns and improving readability.&lt;br /&gt;
* '''Improve role and permission management:''' Ensure table columns reflect real participant actions and roles.&lt;br /&gt;
* '''Enhance maintainability:''' Refactor the front-end with clean code principles using TypeScript.&lt;br /&gt;
&lt;br /&gt;
===Tasks Identified===&lt;br /&gt;
&lt;br /&gt;
==== Simplify the Participants table ====&lt;br /&gt;
&lt;br /&gt;
* Global Search Filter - Search by Participant Name&lt;br /&gt;
* Column header &amp;quot;Email Address&amp;quot; → &amp;quot;Email&amp;quot;&lt;br /&gt;
* Column header &amp;quot;Name&amp;quot; → &amp;quot;Username&amp;quot;&lt;br /&gt;
* Column header &amp;quot;Full Name&amp;quot; → &amp;quot;Name&amp;quot;&lt;br /&gt;
* Consolidate actions: Replace Review, Submission, Metareview, Submit, Take Quiz with '''Submit''', '''Review''', '''Take Quiz''' (only shown when the assignment includes quizzes)&lt;br /&gt;
* Remove the Metareview column.&lt;br /&gt;
* Add a delete icon in each row to allow removing a participant - Use the standard icon from the standard icon library:[https://github.com/AnvithaReddyGutha/reimplementation-front-end/blob/main/design_document.md#icon-library].&lt;br /&gt;
&lt;br /&gt;
==== Remove Submit button for role changes and implement auto-saving ====&lt;br /&gt;
&lt;br /&gt;
* Provide a dropdown menu for selecting a participant’s role (e.g., Participant, Mentor, Reader, Reviewer, Submitter).&lt;br /&gt;
* '''Auto-save behavior:''' When the dropdown value changes, the update is saved automatically—no separate &amp;quot;Submit&amp;quot; button needed.&lt;br /&gt;
&lt;br /&gt;
==== Add action links (copy/import/export participants) ====&lt;br /&gt;
&lt;br /&gt;
* '''Copy participants from course:''' Import existing course participants into the assignment.&lt;br /&gt;
* '''Copy participants to course:''' Push assignment participants back to the course.&lt;br /&gt;
* '''Import assignment participants:''' Upload participants in bulk from a file.&lt;br /&gt;
* '''Export assignment participants:''' Download the participant list as CSV/Excel.&lt;br /&gt;
* '''Back:''' Return to the assignment overview page.&lt;br /&gt;
&lt;br /&gt;
==Demo==&lt;br /&gt;
===Demo Video===&lt;br /&gt;
[https://youtu.be/sTrVRGSaBlE Click to watch on YouTube]&lt;br /&gt;
&lt;br /&gt;
===Demo Link===&lt;br /&gt;
https://frontend-production-e7dc.up.railway.app/participants&lt;br /&gt;
* login with '''admin''' '''password123'''&lt;br /&gt;
* than go to '''/participant''' page&lt;br /&gt;
&lt;br /&gt;
==ParticipantsAPI Component==&lt;br /&gt;
&lt;br /&gt;
===Overview===&lt;br /&gt;
The '''ParticipantsAPI''' component is the main container for managing assignment participants.&lt;br /&gt;
It provides an integrated UI for viewing, editing, importing, exporting, and removing participants.&lt;br /&gt;
It separates data handling (`useParticipants`) from presentation (`ParticipantTable`, `Toolbar`), following a modular and maintainable React + TypeScript architecture.&lt;br /&gt;
&lt;br /&gt;
===Core Responsibilities===&lt;br /&gt;
&lt;br /&gt;
* Display and manage the Participants table&lt;br /&gt;
* Handle user interactions such as role updates and deletions&lt;br /&gt;
* Integrate with the custom hook `useParticipants`&lt;br /&gt;
* Connect import/export functionalities with CSV helpers&lt;br /&gt;
* Provide feedback via alerts and modals&lt;br /&gt;
&lt;br /&gt;
===Main Hooks and States===&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;useParticipants({ assignmentId: 1 })&amp;lt;/code&amp;gt;: Fetches participant data and provides CRUD utilities.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;useState&amp;lt;/code&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;alert&amp;lt;/code&amp;gt;: Stores temporary UI feedback (success/info/error).&lt;br /&gt;
&amp;lt;code&amp;gt;deleteModal&amp;lt;/code&amp;gt;: Manages the confirmation modal for participant deletion.&lt;br /&gt;
&amp;lt;code&amp;gt;requireQuiz&amp;lt;/code&amp;gt;: Indicates whether quiz-related actions should be shown.&lt;br /&gt;
&lt;br /&gt;
===Alert System===&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;showInfo(message)&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;showSuccess(message)&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;showError(message)&amp;lt;/code&amp;gt; : Display contextual alerts with color-coded feedback. Alerts are auto-dismissed after 5 seconds.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;closeAlert()&amp;lt;/code&amp;gt; : manually dismisses the alert.&lt;br /&gt;
&lt;br /&gt;
===Role Management===&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;onRoleChange(id, newRoleId)&amp;lt;/code&amp;gt; : Updates a participant’s role using &amp;lt;code&amp;gt;updateRole&amp;lt;/code&amp;gt; from &amp;lt;code&amp;gt;useParticipants&amp;lt;/code&amp;gt;. The update is automatically saved and confirmed with a success message.&lt;br /&gt;
&lt;br /&gt;
===Participant Removal===&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;onRemoveClick(participant)&amp;lt;/code&amp;gt; : Opens a confirmation modal before deletion.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;onConfirmDelete()&amp;lt;/code&amp;gt; : Executes deletion and displays a success message.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;onCancelDelete()&amp;lt;/code&amp;gt; : Closes the modal without changes.&lt;br /&gt;
&lt;br /&gt;
===Import/Export and Navigation===&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;onImportFileChange(e)&amp;lt;/code&amp;gt; : Imports participants from CSV and shows the number imported.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;onExport()&amp;lt;/code&amp;gt; : Exports participant data as a CSV file.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;onCopyFromCourse()&amp;lt;/code&amp;gt; / &amp;lt;code&amp;gt;onCopyToCourse()&amp;lt;/code&amp;gt; : Placeholder actions for syncing data.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;onBack()&amp;lt;/code&amp;gt; : Navigates back to the previous page.&lt;br /&gt;
&lt;br /&gt;
===UI Components===&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ParticipantToolbar&amp;lt;/code&amp;gt;: Handles search and file import/export.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;ParticipantTable&amp;lt;/code&amp;gt;: Displays participant data and role dropdowns.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;DeleteConfirmationModal&amp;lt;/code&amp;gt;: Confirms participant removal.&lt;br /&gt;
&lt;br /&gt;
===Error Handling===&lt;br /&gt;
&lt;br /&gt;
* Automatically triggers &amp;lt;code&amp;gt;showError()&amp;lt;/code&amp;gt; for &amp;lt;code&amp;gt;fetchError&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;deleteError&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;code&amp;gt;try...catch&amp;lt;/code&amp;gt; blocks in import logic handle parsing failures gracefully.&lt;br /&gt;
&lt;br /&gt;
===Styling===&lt;br /&gt;
&lt;br /&gt;
* Uses React-Bootstrap for layout (&amp;lt;code&amp;gt;Container&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Row&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Col&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Alert&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
* Inline styles control layout, typography, and spacing.&lt;br /&gt;
&lt;br /&gt;
* Includes a scoped &amp;lt;code&amp;gt;&amp;lt;style&amp;gt;&amp;lt;/code&amp;gt; block for alert close button behavior.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Future Work==&lt;br /&gt;
&lt;br /&gt;
* '''Backend API integration:''': The backend endpoints (`GET /participants/student_tasks/:id`, `PATCH /participants/:id`) are not yet implemented and must be connected in a future phase.&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167238</id>
		<title>CSC/ECE 517 Fall 2025 - E2566. Finish DueDates</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167238"/>
		<updated>2025-12-02T19:51:16Z</updated>

		<summary type="html">&lt;p&gt;Skim253: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
This project aims to complete the implementation of the DueDate system in Expertiza. The current implementation consists mostly of class methods and lacks proper definition of different deadline types and permission checking functionality. This redesign will create a more robust, readable, and maintainable due date system.&lt;br /&gt;
&lt;br /&gt;
=== Existing Components ===&lt;br /&gt;
* &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model with polymorphic parent association (Assignment/SignUpTopic)&lt;br /&gt;
* &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; subclasses&lt;br /&gt;
* Basic CRUD operations (within &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AssignmentForm&amp;lt;/code&amp;gt;) and sorting functionality (&amp;lt;code&amp;gt;deadline_sort&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Database schema with &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt; field&lt;br /&gt;
&lt;br /&gt;
=== Current Behavior ===&lt;br /&gt;
&lt;br /&gt;
====Admin's View====&lt;br /&gt;
[[File:Duedates.png|800px]]&lt;br /&gt;
&lt;br /&gt;
When editing an assignment, due dates can be modified under the Due Dates tab. Each row contains the following columns: &amp;lt;code&amp;gt;Date &amp;amp; Time&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Use Date Updater&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Submission Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Teammate Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Meta-Review Allowed&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Reminder&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:Assignments-CurrentStage.png|800px]]&lt;br /&gt;
&lt;br /&gt;
In the Assignments tab, the current stage and stage deadline of each assignment is displayed.&lt;br /&gt;
&lt;br /&gt;
[[File:Due-date-topic-instructor.png|800px]]&lt;br /&gt;
&lt;br /&gt;
[[File:Due-date-topic-instructor-2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
In the Topics subtab, due dates for each topic are displayed.&lt;br /&gt;
&lt;br /&gt;
====Student's View====&lt;br /&gt;
[[File:Student-assignments.png|800px]]&lt;br /&gt;
&lt;br /&gt;
Students can view due date information under &amp;lt;code&amp;gt;Current State&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Stage Deadline&amp;lt;/code&amp;gt;. The actions available to students are determined by the current DueDate status.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-not-over.png|800px]]&lt;br /&gt;
&lt;br /&gt;
During the submission period, students can access the &amp;lt;code&amp;gt;Submit a hyperlink&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Submit a file&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-over.png|800px]]&lt;br /&gt;
&lt;br /&gt;
Once the due date has passed, students cannot submit either a hyperlink or a file.&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
&lt;br /&gt;
Currently, there are some glaring issues with how due_dates was created in the first place, including redundancy and lack of modularity.&lt;br /&gt;
&lt;br /&gt;
'''Modeling &amp;amp; Structural Issues'''&lt;br /&gt;
# No dedicated &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to define different kinds of deadlines. The existing &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; class only serves as an association for &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; models, and is never referenced in the current &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; implementation.&lt;br /&gt;
# Duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table, which may lead to potential bugs.&lt;br /&gt;
# Incomplete deadline type definitions — &amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt; need to be added.&lt;br /&gt;
# No clear separation of concerns — data persistence, business logic, and permission logic are mixed in a single model.&lt;br /&gt;
&lt;br /&gt;
'''Permission &amp;amp; Behavior Issues'''&lt;br /&gt;
# Overuse of class methods making the code difficult to maintain.&lt;br /&gt;
# Missing permission checking logic for user actions that combines both role-based and time-based constraints.&lt;br /&gt;
&lt;br /&gt;
=== Tasks Identified ===&lt;br /&gt;
&lt;br /&gt;
'''Modeling &amp;amp; Structural'''&lt;br /&gt;
# Refactor the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model to separate persistence, business logic, and permission logic into distinct layers.&lt;br /&gt;
# Implement reusable mixins (&amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;) to handle permission evaluation and deadline-related operations.&lt;br /&gt;
# Introduce instance-level methods (e.g., &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;) to replace class-level utilities and improve testability.&lt;br /&gt;
&lt;br /&gt;
'''Permission &amp;amp; Behavior'''&lt;br /&gt;
# Integrate role-based and time-based permission checks within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; to ensure consistent access control.&lt;br /&gt;
# Redesign the &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.&lt;br /&gt;
# Add missing deadline types (&amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt;) and remove duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries for data integrity.&lt;br /&gt;
&lt;br /&gt;
== Proposed Solution ==&lt;br /&gt;
&lt;br /&gt;
The proposed solution restructures the due date system by separating concerns across dedicated models and mixin modules. Deadline definitions (&amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt;), core deadline data (&amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;), and behavioral logic (&amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;) are isolated so each component has a single clear responsibility. This modular design improves readability, reduces coupling, and makes deadline-related behavior easier to extend and maintain.&lt;br /&gt;
&lt;br /&gt;
=== Architecture Overview ===&lt;br /&gt;
&lt;br /&gt;
[[File:Duedate-diagram.png|800px]]&lt;br /&gt;
&lt;br /&gt;
This diagram visualizes the redesigned architecture with five distinct layers:&lt;br /&gt;
&lt;br /&gt;
* '''Layer 1 (Blue)''': Parent models (Assignment, SignUpTopic) that own due dates&lt;br /&gt;
* '''Layer 2 (Orange)''': DueDateActions module providing action-level queries&lt;br /&gt;
* '''Layer 3 (Red)''': DueDate model managing core deadline data&lt;br /&gt;
* '''Layer 4 (Orange)''': DueDatePermissions module evaluating permissions&lt;br /&gt;
* '''Layer 5 (Green)''': Reference data models (DeadlineType, DeadlineRight, Participant)&lt;br /&gt;
&lt;br /&gt;
The flow works as follows: Parent models include DueDateActions, which queries DueDate objects. Each DueDate includes DueDatePermissions to evaluate whether actions are allowed based on DeadlineRight states and Participant role flags.&lt;br /&gt;
&lt;br /&gt;
=== DeadlineType Model ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Although the current codebase includes a &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model, it only serves as a passive lookup table. In practice, most parts of the system still rely on hard-coded numeric IDs or manual lookups:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Example of current usage&lt;br /&gt;
deadline_type_id = DeadlineType.find_by(name: 'review').id&lt;br /&gt;
if due_date.deadline_type_id == deadline_type_id&lt;br /&gt;
  # Perform review-related logic&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This leads to several structural issues:&lt;br /&gt;
* Inconsistent references (some use IDs, others strings)&lt;br /&gt;
* Tight coupling between &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
* Lack of semantic helper methods (e.g., &amp;lt;code&amp;gt;review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;submission?&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Data duplication and integrity risks (two &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; rows)&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; Table Structure&lt;br /&gt;
|- &lt;br /&gt;
! Column !! Type !! Description&lt;br /&gt;
|-&lt;br /&gt;
| id || BIGINT || Primary key, referenced by &amp;lt;code&amp;gt;due_dates.deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| name || VARCHAR(255) || Unique symbolic name (e.g., &amp;lt;code&amp;gt;submission&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| description || TEXT || Human-readable explanation of the deadline category&lt;br /&gt;
|-&lt;br /&gt;
| created_at || TIMESTAMP || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| updated_at || TIMESTAMP || Last update timestamp&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Deadline Type Definitions ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Canonical Deadline Type Entries&lt;br /&gt;
|-&lt;br /&gt;
! ID !! Name !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 1 || submission || Student submission deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 2 || review || Peer review deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 3 || teammate_review || Team member evaluation deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 5 || metareview || Meta-review deadlines (backward compatibility)&lt;br /&gt;
|-&lt;br /&gt;
| 6 || drop_topic || Topic drop deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 7 || signup || Topic signup deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 8 || team_formation || Team formation deadlines (duplicate cleaned)&lt;br /&gt;
|-&lt;br /&gt;
| 11 || quiz || Quiz completion deadlines&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== DeadlineRight Model ===&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model represents the permission level for a specific action at a given deadline:&lt;br /&gt;
&lt;br /&gt;
* '''OK''': Action is allowed without penalty&lt;br /&gt;
* '''Late''': Action is allowed but marked as late&lt;br /&gt;
* '''No''': Action is not permitted&lt;br /&gt;
&lt;br /&gt;
These values are referenced by &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; through fields such as &amp;lt;code&amp;gt;submission_allowed_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review_allowed_id&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;quiz_allowed_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_rights&amp;lt;/code&amp;gt; Table Structure&lt;br /&gt;
|-&lt;br /&gt;
! Column !! Type !! Description&lt;br /&gt;
|-&lt;br /&gt;
| id || BIGINT || Primary key, referenced by &amp;lt;code&amp;gt;due_dates.*_allowed_id&amp;lt;/code&amp;gt; fields&lt;br /&gt;
|-&lt;br /&gt;
| name || VARCHAR(255) || Unique permission name (&amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| created_at || TIMESTAMP || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| updated_at || TIMESTAMP || Last update timestamp&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== DueDate Model Refactoring ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model currently mixes persistence logic, deadline calculation, and permission checking, violating the Single Responsibility Principle. Most of its logic is implemented as class methods, which makes testing and extension difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Old design: class method that mixes concerns&lt;br /&gt;
def self.copy(old_assignment_id, new_assignment_id)&lt;br /&gt;
  duedates = where(parent_id: old_assignment_id)&lt;br /&gt;
  duedates.each do |orig_due_date|&lt;br /&gt;
    new_due_date = orig_due_date.dup&lt;br /&gt;
    new_due_date.parent_id = new_assignment_id&lt;br /&gt;
    new_due_date.save&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Improved Design ====&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model is focused on representing a single deadline entry. Behavior has been extracted into appropriate modules:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Responsibility Distribution After Refactoring&lt;br /&gt;
|-&lt;br /&gt;
! Concern !! Where It Lives&lt;br /&gt;
|-&lt;br /&gt;
| Permission checking || &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module (included in DueDate)&lt;br /&gt;
|-&lt;br /&gt;
| Deadline-related actions || &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module (included in parent models)&lt;br /&gt;
|-&lt;br /&gt;
| Deadline type semantics || &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model&lt;br /&gt;
|-&lt;br /&gt;
| Core deadline data || &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Key improvements:&lt;br /&gt;
* '''Instance-based behavior''': Replaced class methods with instance methods&lt;br /&gt;
* '''Clear query methods''': Simple predicates like &amp;lt;code&amp;gt;upcoming?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;overdue?&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Scopes for common queries''': &amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DueDatePermissions Module ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Currently, permission checks are split into two disconnected layers:&lt;br /&gt;
&lt;br /&gt;
'''Role-based permissions''' (in participant_helpers.rb):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def participant_permissions(authorization)&lt;br /&gt;
  case authorization&lt;br /&gt;
  when 'reader' then { can_submit: false, can_review: true, can_take_quiz: true }&lt;br /&gt;
  # ...&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Deadline-based checks''' (in Assignment model):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def submission_allowed(topic_id = nil)&lt;br /&gt;
  check_condition('submission_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These two layers never interact, leading to inconsistencies.&lt;br /&gt;
&lt;br /&gt;
==== Proposed Solution ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module integrates both layers:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Permission Methods&lt;br /&gt;
|-&lt;br /&gt;
! Method !! What It Checks&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_submit?(participant)&amp;lt;/code&amp;gt; || Submission deadline is OK/Late AND participant can submit&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_review?(participant)&amp;lt;/code&amp;gt; || Review deadline is OK/Late AND participant can review&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_take_quiz?(participant)&amp;lt;/code&amp;gt; || Quiz deadline is OK/Late AND participant can take quiz&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_review_teammate?(participant)&amp;lt;/code&amp;gt; || Teammate review deadline is OK/Late AND participant can review&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;activity_permissible?(activity)&amp;lt;/code&amp;gt; || Generic checker for any activity&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;permission_status_for(action)&amp;lt;/code&amp;gt; || Returns 'OK', 'Late', or 'No' for display&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== DueDateActions Module ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module provides a unified interface for models that own due dates (&amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt;). This centralizes the logic for querying upcoming deadlines and determining whether specific activities are currently allowed.&lt;br /&gt;
&lt;br /&gt;
==== Proposed Solution ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Methods in DueDateActions&lt;br /&gt;
|-&lt;br /&gt;
! Method !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;activity_permissible?(activity)&amp;lt;/code&amp;gt; || Checks if an activity is permitted at the current time&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;submission_permissible?&amp;lt;/code&amp;gt; || Convenience wrapper for submission checking&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;review_permissible?&amp;lt;/code&amp;gt; || Convenience wrapper for review checking&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;next_due_date(topic_id)&amp;lt;/code&amp;gt; || Resolves the next applicable deadline&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;current_stage&amp;lt;/code&amp;gt; || Returns human-readable stage name (e.g., &amp;quot;review&amp;quot;, &amp;quot;submission&amp;quot;)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;has_topic_specific_deadlines?&amp;lt;/code&amp;gt; || Determines if assignment uses topic-specific deadlines&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;copy_due_dates_to(new_parent)&amp;lt;/code&amp;gt; || Utility for cloning due dates to another assignment&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Deadline Resolution Flow ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ How next_due_date Works&lt;br /&gt;
|-&lt;br /&gt;
! Step !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 1. Topic-specific check || If topic ID provided and assignment uses staggered deadlines, check topic-level deadlines first&lt;br /&gt;
|-&lt;br /&gt;
| 2. Assignment-level fallback || If no topic deadline exists, use nearest assignment-level deadline&lt;br /&gt;
|-&lt;br /&gt;
| 3. Permission evaluation || Delegate to DueDatePermissions to determine if action is allowed&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Implementation Summary ==&lt;br /&gt;
&lt;br /&gt;
=== Models Created/Modified ===&lt;br /&gt;
&lt;br /&gt;
* '''DeadlineType''' — Canonical deadline type definitions with semantic helper methods&lt;br /&gt;
* '''DeadlineRight''' — Permission state model (OK/Late/No) with comparison methods&lt;br /&gt;
* '''DueDate''' — Refactored with instance methods, scopes, and permission checking&lt;br /&gt;
* '''TopicDueDate''' — STI subclass for topic-specific deadlines&lt;br /&gt;
* '''Assignment''' — Includes DueDateActions mixin&lt;br /&gt;
* '''SignUpTopic''' — Includes DueDateActions mixin&lt;br /&gt;
&lt;br /&gt;
=== Modules Created ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions''' — Permission checking combining deadline and role constraints&lt;br /&gt;
* '''DueDateActions''' — Deadline-related operations for parent models&lt;br /&gt;
&lt;br /&gt;
=== Key Improvements ===&lt;br /&gt;
&lt;br /&gt;
# '''Separation of Concerns''': Permission logic separated into dedicated modules&lt;br /&gt;
# '''Instance Methods''': Replaced class methods with testable instance methods&lt;br /&gt;
# '''Polymorphic Design''': Single DueDate model serves both Assignment and SignUpTopic&lt;br /&gt;
# '''Unified Permissions''': Role-based and time-based checks combined in one interface&lt;br /&gt;
# '''Data Integrity''': Removed duplicate deadline types, enforced foreign key constraints&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
This test suite provides coverage for both the Assignment model and the DueDate model.&lt;br /&gt;
The Assignment tests also exercise all behaviors provided by the DueDateActions mixin, since that module is included directly in the Assignment class.&lt;br /&gt;
&lt;br /&gt;
The tests are designed to verify how deadlines influence allowed activities (submission, review, quiz, etc.), how the system determines the next relevant deadline, and how deadlines are ordered.&lt;br /&gt;
&lt;br /&gt;
=== Coverage Summary ===&lt;br /&gt;
&lt;br /&gt;
==== Assignment (DueDateActions mixin included) ====&lt;br /&gt;
The Assignment spec validates:&lt;br /&gt;
&lt;br /&gt;
activity permission logic:&lt;br /&gt;
* &amp;lt;code&amp;gt;activity_permissible?&amp;lt;/code&amp;gt; – evaluates permissions using the next upcoming deadline&lt;br /&gt;
* &amp;lt;code&amp;gt;activity_permissible_for?&amp;lt;/code&amp;gt; – finds the most recent past deadline relative to a specific time&lt;br /&gt;
&lt;br /&gt;
deadline-based navigation:&lt;br /&gt;
* &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;deadlines_properly_ordered?&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
delegation helpers such as:&lt;br /&gt;
* &amp;lt;code&amp;gt;submission_permissible?&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;review_permissible?&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These tests ensure that Assignment correctly interprets the state of its associated due dates.&lt;br /&gt;
&lt;br /&gt;
==== DueDate Model ====&lt;br /&gt;
The DueDate spec validates:&lt;br /&gt;
&lt;br /&gt;
required field validations (parent, due_at, deadline_type_id)&lt;br /&gt;
round field rules (cannot be zero or negative, sets default when nil)&lt;br /&gt;
scopes:&lt;br /&gt;
* &amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
instance methods:&lt;br /&gt;
* &amp;lt;code&amp;gt;#overdue?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#upcoming?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#set&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#copy&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#deadline_type_name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#last_deadline?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#&amp;lt;=&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
class methods:&lt;br /&gt;
* &amp;lt;code&amp;gt;.sort_due_dates&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.any_future_due_dates?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.copy&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
callback:&lt;br /&gt;
* &amp;lt;code&amp;gt;before_save :set_default_round&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Rationale for Test Design ===&lt;br /&gt;
&lt;br /&gt;
The test suites for DueDate and Assignment (including DueDateActions) are designed to validate two different layers of behavior:&lt;br /&gt;
* DueDate tests verify record-level behavior: validations, scopes, and individual instance methods.&lt;br /&gt;
* Assignment tests verify behavior that depends on collections of due dates, such as selecting the next deadline or determining whether an activity is permissible.&lt;br /&gt;
&lt;br /&gt;
All tests use real database records (DeadlineType, DeadlineRight, DueDate) to ensure that associations, validations, and permission checks reflect real application behavior.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
The suite uses nested RSpec contexts:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
describe 'activity_permissible_for?' do&lt;br /&gt;
  context 'when past deadline exists' do&lt;br /&gt;
    it 'returns permission from most recent past deadline'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when only future deadlines exist' do&lt;br /&gt;
    it 'returns false'&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Contexts clearly describe the situation being tested, while individual examples describe expected behaviors.&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
=== Demo Link ===&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add deadline notification system using the new permission framework&lt;br /&gt;
* Implement deadline templates for common assignment patterns&lt;br /&gt;
* Add API endpoints for mobile app integration&lt;br /&gt;
* Create admin dashboard for monitoring deadline compliance across courses&lt;br /&gt;
* Add automated deadline extension workflows based on configurable rules&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167231</id>
		<title>CSC/ECE 517 Fall 2025 - E2566. Finish DueDates</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167231"/>
		<updated>2025-12-02T19:23:01Z</updated>

		<summary type="html">&lt;p&gt;Skim253: /* Coverage Summary */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
This project aims to complete the implementation of the DueDate system in Expertiza. The current implementation consists mostly of class methods and lacks proper definition of different deadline types and permission checking functionality. This redesign will create a more robust, readable, and maintainable due date system.&lt;br /&gt;
&lt;br /&gt;
=== Existing Components ===&lt;br /&gt;
* &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model with polymorphic parent association (Assignment/SignUpTopic)&lt;br /&gt;
* &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; subclasses&lt;br /&gt;
* Basic CRUD operations (within &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AssignmentForm&amp;lt;/code&amp;gt;) and sorting functionality (&amp;lt;code&amp;gt;deadline_sort&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Database schema with &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt; field&lt;br /&gt;
&lt;br /&gt;
=== Current Behavior ===&lt;br /&gt;
&lt;br /&gt;
====Admin's View====&lt;br /&gt;
[[File:Duedates.png|800px]]&lt;br /&gt;
&lt;br /&gt;
When editing an assignment, due dates can be modified under the Due Dates tab. Each row contains the following columns: &amp;lt;code&amp;gt;Date &amp;amp; Time&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Use Date Updater&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Submission Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Teammate Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Meta-Review Allowed&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Reminder&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:Assignments-CurrentStage.png|800px]]&lt;br /&gt;
&lt;br /&gt;
In the Assignments tab, the current stage and stage deadline of each assignment is displayed.&lt;br /&gt;
&lt;br /&gt;
[[File:Due-date-topic-instructor.png|800px]]&lt;br /&gt;
&lt;br /&gt;
[[File:Due-date-topic-instructor-2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
In the Topics subtab, due dates for each topic are displayed.&lt;br /&gt;
&lt;br /&gt;
====Student's View====&lt;br /&gt;
[[File:Student-assignments.png|800px]]&lt;br /&gt;
&lt;br /&gt;
Students can view due date information under &amp;lt;code&amp;gt;Current State&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Stage Deadline&amp;lt;/code&amp;gt;. The actions available to students are determined by the current DueDate status.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-not-over.png|800px]]&lt;br /&gt;
&lt;br /&gt;
During the submission period, students can access the &amp;lt;code&amp;gt;Submit a hyperlink&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Submit a file&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-over.png|800px]]&lt;br /&gt;
&lt;br /&gt;
Once the due date has passed, students cannot submit either a hyperlink or a file.&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
&lt;br /&gt;
Currently, there are some glaring issues with how due_dates was created in the first place, including redundancy and lack of modularity.&lt;br /&gt;
&lt;br /&gt;
'''Modeling &amp;amp; Structural Issues'''&lt;br /&gt;
# No dedicated &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to define different kinds of deadlines. The existing &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; class only serves as an association for &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; models, and is never referenced in the current &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; implementation.&lt;br /&gt;
# Duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table, which may lead to potential bugs.&lt;br /&gt;
# Incomplete deadline type definitions — &amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt; need to be added.&lt;br /&gt;
# No clear separation of concerns — data persistence, business logic, and permission logic are mixed in a single model.&lt;br /&gt;
&lt;br /&gt;
'''Permission &amp;amp; Behavior Issues'''&lt;br /&gt;
# Overuse of class methods making the code difficult to maintain.&lt;br /&gt;
# Missing permission checking logic for user actions that combines both role-based and time-based constraints.&lt;br /&gt;
&lt;br /&gt;
=== Tasks Identified ===&lt;br /&gt;
&lt;br /&gt;
'''Modeling &amp;amp; Structural'''&lt;br /&gt;
# Refactor the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model to separate persistence, business logic, and permission logic into distinct layers.&lt;br /&gt;
# Implement reusable mixins (&amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;) to handle permission evaluation and deadline-related operations.&lt;br /&gt;
# Introduce instance-level methods (e.g., &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;) to replace class-level utilities and improve testability.&lt;br /&gt;
&lt;br /&gt;
'''Permission &amp;amp; Behavior'''&lt;br /&gt;
# Integrate role-based and time-based permission checks within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; to ensure consistent access control.&lt;br /&gt;
# Redesign the &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.&lt;br /&gt;
# Add missing deadline types (&amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt;) and remove duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries for data integrity.&lt;br /&gt;
&lt;br /&gt;
== Proposed Solution ==&lt;br /&gt;
&lt;br /&gt;
The proposed solution restructures the due date system by separating concerns across dedicated models and mixin modules. Deadline definitions (&amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt;), core deadline data (&amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;), and behavioral logic (&amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;) are isolated so each component has a single clear responsibility. This modular design improves readability, reduces coupling, and makes deadline-related behavior easier to extend and maintain.&lt;br /&gt;
&lt;br /&gt;
=== Architecture Overview ===&lt;br /&gt;
&lt;br /&gt;
[[File:Duedate-diagram.png|800px]]&lt;br /&gt;
&lt;br /&gt;
This diagram visualizes the redesigned architecture with five distinct layers:&lt;br /&gt;
&lt;br /&gt;
* '''Layer 1 (Blue)''': Parent models (Assignment, SignUpTopic) that own due dates&lt;br /&gt;
* '''Layer 2 (Orange)''': DueDateActions module providing action-level queries&lt;br /&gt;
* '''Layer 3 (Red)''': DueDate model managing core deadline data&lt;br /&gt;
* '''Layer 4 (Orange)''': DueDatePermissions module evaluating permissions&lt;br /&gt;
* '''Layer 5 (Green)''': Reference data models (DeadlineType, DeadlineRight, Participant)&lt;br /&gt;
&lt;br /&gt;
The flow works as follows: Parent models include DueDateActions, which queries DueDate objects. Each DueDate includes DueDatePermissions to evaluate whether actions are allowed based on DeadlineRight states and Participant role flags.&lt;br /&gt;
&lt;br /&gt;
=== DeadlineType Model ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Although the current codebase includes a &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model, it only serves as a passive lookup table. In practice, most parts of the system still rely on hard-coded numeric IDs or manual lookups:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Example of current usage&lt;br /&gt;
deadline_type_id = DeadlineType.find_by(name: 'review').id&lt;br /&gt;
if due_date.deadline_type_id == deadline_type_id&lt;br /&gt;
  # Perform review-related logic&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This leads to several structural issues:&lt;br /&gt;
* Inconsistent references (some use IDs, others strings)&lt;br /&gt;
* Tight coupling between &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
* Lack of semantic helper methods (e.g., &amp;lt;code&amp;gt;review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;submission?&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Data duplication and integrity risks (two &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; rows)&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; Table Structure&lt;br /&gt;
|- &lt;br /&gt;
! Column !! Type !! Description&lt;br /&gt;
|-&lt;br /&gt;
| id || BIGINT || Primary key, referenced by &amp;lt;code&amp;gt;due_dates.deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| name || VARCHAR(255) || Unique symbolic name (e.g., &amp;lt;code&amp;gt;submission&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| description || TEXT || Human-readable explanation of the deadline category&lt;br /&gt;
|-&lt;br /&gt;
| created_at || TIMESTAMP || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| updated_at || TIMESTAMP || Last update timestamp&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Deadline Type Definitions ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Canonical Deadline Type Entries&lt;br /&gt;
|-&lt;br /&gt;
! ID !! Name !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 1 || submission || Student submission deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 2 || review || Peer review deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 3 || teammate_review || Team member evaluation deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 5 || metareview || Meta-review deadlines (backward compatibility)&lt;br /&gt;
|-&lt;br /&gt;
| 6 || drop_topic || Topic drop deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 7 || signup || Topic signup deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 8 || team_formation || Team formation deadlines (duplicate cleaned)&lt;br /&gt;
|-&lt;br /&gt;
| 11 || quiz || Quiz completion deadlines&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== DeadlineRight Model ===&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model represents the permission level for a specific action at a given deadline:&lt;br /&gt;
&lt;br /&gt;
* '''OK''': Action is allowed without penalty&lt;br /&gt;
* '''Late''': Action is allowed but marked as late&lt;br /&gt;
* '''No''': Action is not permitted&lt;br /&gt;
&lt;br /&gt;
These values are referenced by &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; through fields such as &amp;lt;code&amp;gt;submission_allowed_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review_allowed_id&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;quiz_allowed_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_rights&amp;lt;/code&amp;gt; Table Structure&lt;br /&gt;
|-&lt;br /&gt;
! Column !! Type !! Description&lt;br /&gt;
|-&lt;br /&gt;
| id || BIGINT || Primary key, referenced by &amp;lt;code&amp;gt;due_dates.*_allowed_id&amp;lt;/code&amp;gt; fields&lt;br /&gt;
|-&lt;br /&gt;
| name || VARCHAR(255) || Unique permission name (&amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| created_at || TIMESTAMP || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| updated_at || TIMESTAMP || Last update timestamp&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== DueDate Model Refactoring ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model currently mixes persistence logic, deadline calculation, and permission checking, violating the Single Responsibility Principle. Most of its logic is implemented as class methods, which makes testing and extension difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Old design: class method that mixes concerns&lt;br /&gt;
def self.copy(old_assignment_id, new_assignment_id)&lt;br /&gt;
  duedates = where(parent_id: old_assignment_id)&lt;br /&gt;
  duedates.each do |orig_due_date|&lt;br /&gt;
    new_due_date = orig_due_date.dup&lt;br /&gt;
    new_due_date.parent_id = new_assignment_id&lt;br /&gt;
    new_due_date.save&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Improved Design ====&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model is focused on representing a single deadline entry. Behavior has been extracted into appropriate modules:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Responsibility Distribution After Refactoring&lt;br /&gt;
|-&lt;br /&gt;
! Concern !! Where It Lives&lt;br /&gt;
|-&lt;br /&gt;
| Permission checking || &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module (included in DueDate)&lt;br /&gt;
|-&lt;br /&gt;
| Deadline-related actions || &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module (included in parent models)&lt;br /&gt;
|-&lt;br /&gt;
| Deadline type semantics || &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model&lt;br /&gt;
|-&lt;br /&gt;
| Core deadline data || &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Key improvements:&lt;br /&gt;
* '''Instance-based behavior''': Replaced class methods with instance methods&lt;br /&gt;
* '''Clear query methods''': Simple predicates like &amp;lt;code&amp;gt;upcoming?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;overdue?&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Scopes for common queries''': &amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DueDatePermissions Module ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Currently, permission checks are split into two disconnected layers:&lt;br /&gt;
&lt;br /&gt;
'''Role-based permissions''' (in participant_helpers.rb):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def participant_permissions(authorization)&lt;br /&gt;
  case authorization&lt;br /&gt;
  when 'reader' then { can_submit: false, can_review: true, can_take_quiz: true }&lt;br /&gt;
  # ...&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Deadline-based checks''' (in Assignment model):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def submission_allowed(topic_id = nil)&lt;br /&gt;
  check_condition('submission_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These two layers never interact, leading to inconsistencies.&lt;br /&gt;
&lt;br /&gt;
==== Proposed Solution ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module integrates both layers:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Permission Methods&lt;br /&gt;
|-&lt;br /&gt;
! Method !! What It Checks&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_submit?(participant)&amp;lt;/code&amp;gt; || Submission deadline is OK/Late AND participant can submit&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_review?(participant)&amp;lt;/code&amp;gt; || Review deadline is OK/Late AND participant can review&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_take_quiz?(participant)&amp;lt;/code&amp;gt; || Quiz deadline is OK/Late AND participant can take quiz&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_review_teammate?(participant)&amp;lt;/code&amp;gt; || Teammate review deadline is OK/Late AND participant can review&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;activity_permissible?(activity)&amp;lt;/code&amp;gt; || Generic checker for any activity&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;permission_status_for(action)&amp;lt;/code&amp;gt; || Returns 'OK', 'Late', or 'No' for display&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== DueDateActions Module ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module provides a unified interface for models that own due dates (&amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt;). This centralizes the logic for querying upcoming deadlines and determining whether specific activities are currently allowed.&lt;br /&gt;
&lt;br /&gt;
==== Proposed Solution ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Methods in DueDateActions&lt;br /&gt;
|-&lt;br /&gt;
! Method !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;activity_permissible?(activity)&amp;lt;/code&amp;gt; || Checks if an activity is permitted at the current time&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;submission_permissible?&amp;lt;/code&amp;gt; || Convenience wrapper for submission checking&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;review_permissible?&amp;lt;/code&amp;gt; || Convenience wrapper for review checking&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;next_due_date(topic_id)&amp;lt;/code&amp;gt; || Resolves the next applicable deadline&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;current_stage&amp;lt;/code&amp;gt; || Returns human-readable stage name (e.g., &amp;quot;review&amp;quot;, &amp;quot;submission&amp;quot;)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;has_topic_specific_deadlines?&amp;lt;/code&amp;gt; || Determines if assignment uses topic-specific deadlines&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;copy_due_dates_to(new_parent)&amp;lt;/code&amp;gt; || Utility for cloning due dates to another assignment&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Deadline Resolution Flow ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ How next_due_date Works&lt;br /&gt;
|-&lt;br /&gt;
! Step !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 1. Topic-specific check || If topic ID provided and assignment uses staggered deadlines, check topic-level deadlines first&lt;br /&gt;
|-&lt;br /&gt;
| 2. Assignment-level fallback || If no topic deadline exists, use nearest assignment-level deadline&lt;br /&gt;
|-&lt;br /&gt;
| 3. Permission evaluation || Delegate to DueDatePermissions to determine if action is allowed&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Implementation Summary ==&lt;br /&gt;
&lt;br /&gt;
=== Models Created/Modified ===&lt;br /&gt;
&lt;br /&gt;
* '''DeadlineType''' — Canonical deadline type definitions with semantic helper methods&lt;br /&gt;
* '''DeadlineRight''' — Permission state model (OK/Late/No) with comparison methods&lt;br /&gt;
* '''DueDate''' — Refactored with instance methods, scopes, and permission checking&lt;br /&gt;
* '''TopicDueDate''' — STI subclass for topic-specific deadlines&lt;br /&gt;
* '''Assignment''' — Includes DueDateActions mixin&lt;br /&gt;
* '''SignUpTopic''' — Includes DueDateActions mixin&lt;br /&gt;
&lt;br /&gt;
=== Modules Created ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions''' — Permission checking combining deadline and role constraints&lt;br /&gt;
* '''DueDateActions''' — Deadline-related operations for parent models&lt;br /&gt;
&lt;br /&gt;
=== Key Improvements ===&lt;br /&gt;
&lt;br /&gt;
# '''Separation of Concerns''': Permission logic separated into dedicated modules&lt;br /&gt;
# '''Instance Methods''': Replaced class methods with testable instance methods&lt;br /&gt;
# '''Polymorphic Design''': Single DueDate model serves both Assignment and SignUpTopic&lt;br /&gt;
# '''Unified Permissions''': Role-based and time-based checks combined in one interface&lt;br /&gt;
# '''Data Integrity''': Removed duplicate deadline types, enforced foreign key constraints&lt;br /&gt;
&lt;br /&gt;
== Testing Overview ==&lt;br /&gt;
&lt;br /&gt;
This test suite provides coverage for both the Assignment model and the DueDate model.&lt;br /&gt;
The Assignment tests also exercise all behaviors provided by the DueDateActions mixin, since that module is included directly in the Assignment class.&lt;br /&gt;
&lt;br /&gt;
The tests are designed to verify how deadlines influence allowed activities (submission, review, quiz, etc.), how the system determines the next relevant deadline, and how deadlines are ordered.&lt;br /&gt;
&lt;br /&gt;
== Coverage Summary ==&lt;br /&gt;
&lt;br /&gt;
=== Assignment (DueDateActions mixin included) ===&lt;br /&gt;
The Assignment spec validates:&lt;br /&gt;
&lt;br /&gt;
activity permission logic:&lt;br /&gt;
* &amp;lt;code&amp;gt;activity_permissible?&amp;lt;/code&amp;gt; – evaluates permissions using the next upcoming deadline&lt;br /&gt;
* &amp;lt;code&amp;gt;activity_permissible_for?&amp;lt;/code&amp;gt; – finds the most recent past deadline relative to a specific time&lt;br /&gt;
&lt;br /&gt;
deadline-based navigation:&lt;br /&gt;
* &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;deadlines_properly_ordered?&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
delegation helpers such as:&lt;br /&gt;
* &amp;lt;code&amp;gt;submission_permissible?&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;review_permissible?&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These tests ensure that Assignment correctly interprets the state of its associated due dates.&lt;br /&gt;
&lt;br /&gt;
=== DueDate Model ===&lt;br /&gt;
The DueDate spec validates:&lt;br /&gt;
&lt;br /&gt;
required field validations (parent, due_at, deadline_type_id)&lt;br /&gt;
round field rules (cannot be zero or negative, sets default when nil)&lt;br /&gt;
scopes:&lt;br /&gt;
* &amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;&lt;br /&gt;
* &amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
instance methods:&lt;br /&gt;
* &amp;lt;code&amp;gt;#overdue?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#upcoming?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#set&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#copy&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#deadline_type_name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#last_deadline?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#&amp;lt;=&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
class methods:&lt;br /&gt;
* &amp;lt;code&amp;gt;.sort_due_dates&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.any_future_due_dates?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.copy&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
callback:&lt;br /&gt;
* &amp;lt;code&amp;gt;before_save :set_default_round&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Rationale for Test Design ==&lt;br /&gt;
&lt;br /&gt;
The test suites for DueDate and Assignment (including DueDateActions) are designed to validate two different layers of behavior:&lt;br /&gt;
* DueDate tests verify record-level behavior: validations, scopes, and individual instance methods.&lt;br /&gt;
* Assignment tests verify behavior that depends on collections of due dates, such as selecting the next deadline or determining whether an activity is permissible.&lt;br /&gt;
&lt;br /&gt;
All tests use real database records (DeadlineType, DeadlineRight, DueDate) to ensure that associations, validations, and permission checks reflect real application behavior.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Test Structure ==&lt;br /&gt;
&lt;br /&gt;
The suite uses nested RSpec contexts:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
describe 'activity_permissible_for?' do&lt;br /&gt;
  context 'when past deadline exists' do&lt;br /&gt;
    it 'returns permission from most recent past deadline'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when only future deadlines exist' do&lt;br /&gt;
    it 'returns false'&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Contexts clearly describe the situation being tested, while individual examples describe expected behaviors.&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
=== Demo Link ===&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add deadline notification system using the new permission framework&lt;br /&gt;
* Implement deadline templates for common assignment patterns&lt;br /&gt;
* Add API endpoints for mobile app integration&lt;br /&gt;
* Create admin dashboard for monitoring deadline compliance across courses&lt;br /&gt;
* Add automated deadline extension workflows based on configurable rules&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167230</id>
		<title>CSC/ECE 517 Fall 2025 - E2566. Finish DueDates</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167230"/>
		<updated>2025-12-02T19:20:47Z</updated>

		<summary type="html">&lt;p&gt;Skim253: /* Testing */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
This project aims to complete the implementation of the DueDate system in Expertiza. The current implementation consists mostly of class methods and lacks proper definition of different deadline types and permission checking functionality. This redesign will create a more robust, readable, and maintainable due date system.&lt;br /&gt;
&lt;br /&gt;
=== Existing Components ===&lt;br /&gt;
* &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model with polymorphic parent association (Assignment/SignUpTopic)&lt;br /&gt;
* &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; subclasses&lt;br /&gt;
* Basic CRUD operations (within &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AssignmentForm&amp;lt;/code&amp;gt;) and sorting functionality (&amp;lt;code&amp;gt;deadline_sort&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Database schema with &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt; field&lt;br /&gt;
&lt;br /&gt;
=== Current Behavior ===&lt;br /&gt;
&lt;br /&gt;
====Admin's View====&lt;br /&gt;
[[File:Duedates.png|800px]]&lt;br /&gt;
&lt;br /&gt;
When editing an assignment, due dates can be modified under the Due Dates tab. Each row contains the following columns: &amp;lt;code&amp;gt;Date &amp;amp; Time&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Use Date Updater&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Submission Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Teammate Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Meta-Review Allowed&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Reminder&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:Assignments-CurrentStage.png|800px]]&lt;br /&gt;
&lt;br /&gt;
In the Assignments tab, the current stage and stage deadline of each assignment is displayed.&lt;br /&gt;
&lt;br /&gt;
[[File:Due-date-topic-instructor.png|800px]]&lt;br /&gt;
&lt;br /&gt;
[[File:Due-date-topic-instructor-2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
In the Topics subtab, due dates for each topic are displayed.&lt;br /&gt;
&lt;br /&gt;
====Student's View====&lt;br /&gt;
[[File:Student-assignments.png|800px]]&lt;br /&gt;
&lt;br /&gt;
Students can view due date information under &amp;lt;code&amp;gt;Current State&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Stage Deadline&amp;lt;/code&amp;gt;. The actions available to students are determined by the current DueDate status.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-not-over.png|800px]]&lt;br /&gt;
&lt;br /&gt;
During the submission period, students can access the &amp;lt;code&amp;gt;Submit a hyperlink&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Submit a file&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-over.png|800px]]&lt;br /&gt;
&lt;br /&gt;
Once the due date has passed, students cannot submit either a hyperlink or a file.&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
&lt;br /&gt;
Currently, there are some glaring issues with how due_dates was created in the first place, including redundancy and lack of modularity.&lt;br /&gt;
&lt;br /&gt;
'''Modeling &amp;amp; Structural Issues'''&lt;br /&gt;
# No dedicated &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to define different kinds of deadlines. The existing &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; class only serves as an association for &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; models, and is never referenced in the current &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; implementation.&lt;br /&gt;
# Duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table, which may lead to potential bugs.&lt;br /&gt;
# Incomplete deadline type definitions — &amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt; need to be added.&lt;br /&gt;
# No clear separation of concerns — data persistence, business logic, and permission logic are mixed in a single model.&lt;br /&gt;
&lt;br /&gt;
'''Permission &amp;amp; Behavior Issues'''&lt;br /&gt;
# Overuse of class methods making the code difficult to maintain.&lt;br /&gt;
# Missing permission checking logic for user actions that combines both role-based and time-based constraints.&lt;br /&gt;
&lt;br /&gt;
=== Tasks Identified ===&lt;br /&gt;
&lt;br /&gt;
'''Modeling &amp;amp; Structural'''&lt;br /&gt;
# Refactor the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model to separate persistence, business logic, and permission logic into distinct layers.&lt;br /&gt;
# Implement reusable mixins (&amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;) to handle permission evaluation and deadline-related operations.&lt;br /&gt;
# Introduce instance-level methods (e.g., &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;) to replace class-level utilities and improve testability.&lt;br /&gt;
&lt;br /&gt;
'''Permission &amp;amp; Behavior'''&lt;br /&gt;
# Integrate role-based and time-based permission checks within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; to ensure consistent access control.&lt;br /&gt;
# Redesign the &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.&lt;br /&gt;
# Add missing deadline types (&amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt;) and remove duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries for data integrity.&lt;br /&gt;
&lt;br /&gt;
== Proposed Solution ==&lt;br /&gt;
&lt;br /&gt;
The proposed solution restructures the due date system by separating concerns across dedicated models and mixin modules. Deadline definitions (&amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt;), core deadline data (&amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;), and behavioral logic (&amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;) are isolated so each component has a single clear responsibility. This modular design improves readability, reduces coupling, and makes deadline-related behavior easier to extend and maintain.&lt;br /&gt;
&lt;br /&gt;
=== Architecture Overview ===&lt;br /&gt;
&lt;br /&gt;
[[File:Duedate-diagram.png|800px]]&lt;br /&gt;
&lt;br /&gt;
This diagram visualizes the redesigned architecture with five distinct layers:&lt;br /&gt;
&lt;br /&gt;
* '''Layer 1 (Blue)''': Parent models (Assignment, SignUpTopic) that own due dates&lt;br /&gt;
* '''Layer 2 (Orange)''': DueDateActions module providing action-level queries&lt;br /&gt;
* '''Layer 3 (Red)''': DueDate model managing core deadline data&lt;br /&gt;
* '''Layer 4 (Orange)''': DueDatePermissions module evaluating permissions&lt;br /&gt;
* '''Layer 5 (Green)''': Reference data models (DeadlineType, DeadlineRight, Participant)&lt;br /&gt;
&lt;br /&gt;
The flow works as follows: Parent models include DueDateActions, which queries DueDate objects. Each DueDate includes DueDatePermissions to evaluate whether actions are allowed based on DeadlineRight states and Participant role flags.&lt;br /&gt;
&lt;br /&gt;
=== DeadlineType Model ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Although the current codebase includes a &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model, it only serves as a passive lookup table. In practice, most parts of the system still rely on hard-coded numeric IDs or manual lookups:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Example of current usage&lt;br /&gt;
deadline_type_id = DeadlineType.find_by(name: 'review').id&lt;br /&gt;
if due_date.deadline_type_id == deadline_type_id&lt;br /&gt;
  # Perform review-related logic&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This leads to several structural issues:&lt;br /&gt;
* Inconsistent references (some use IDs, others strings)&lt;br /&gt;
* Tight coupling between &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
* Lack of semantic helper methods (e.g., &amp;lt;code&amp;gt;review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;submission?&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Data duplication and integrity risks (two &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; rows)&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; Table Structure&lt;br /&gt;
|- &lt;br /&gt;
! Column !! Type !! Description&lt;br /&gt;
|-&lt;br /&gt;
| id || BIGINT || Primary key, referenced by &amp;lt;code&amp;gt;due_dates.deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| name || VARCHAR(255) || Unique symbolic name (e.g., &amp;lt;code&amp;gt;submission&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| description || TEXT || Human-readable explanation of the deadline category&lt;br /&gt;
|-&lt;br /&gt;
| created_at || TIMESTAMP || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| updated_at || TIMESTAMP || Last update timestamp&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Deadline Type Definitions ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Canonical Deadline Type Entries&lt;br /&gt;
|-&lt;br /&gt;
! ID !! Name !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 1 || submission || Student submission deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 2 || review || Peer review deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 3 || teammate_review || Team member evaluation deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 5 || metareview || Meta-review deadlines (backward compatibility)&lt;br /&gt;
|-&lt;br /&gt;
| 6 || drop_topic || Topic drop deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 7 || signup || Topic signup deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 8 || team_formation || Team formation deadlines (duplicate cleaned)&lt;br /&gt;
|-&lt;br /&gt;
| 11 || quiz || Quiz completion deadlines&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== DeadlineRight Model ===&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model represents the permission level for a specific action at a given deadline:&lt;br /&gt;
&lt;br /&gt;
* '''OK''': Action is allowed without penalty&lt;br /&gt;
* '''Late''': Action is allowed but marked as late&lt;br /&gt;
* '''No''': Action is not permitted&lt;br /&gt;
&lt;br /&gt;
These values are referenced by &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; through fields such as &amp;lt;code&amp;gt;submission_allowed_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review_allowed_id&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;quiz_allowed_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_rights&amp;lt;/code&amp;gt; Table Structure&lt;br /&gt;
|-&lt;br /&gt;
! Column !! Type !! Description&lt;br /&gt;
|-&lt;br /&gt;
| id || BIGINT || Primary key, referenced by &amp;lt;code&amp;gt;due_dates.*_allowed_id&amp;lt;/code&amp;gt; fields&lt;br /&gt;
|-&lt;br /&gt;
| name || VARCHAR(255) || Unique permission name (&amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| created_at || TIMESTAMP || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| updated_at || TIMESTAMP || Last update timestamp&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== DueDate Model Refactoring ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model currently mixes persistence logic, deadline calculation, and permission checking, violating the Single Responsibility Principle. Most of its logic is implemented as class methods, which makes testing and extension difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Old design: class method that mixes concerns&lt;br /&gt;
def self.copy(old_assignment_id, new_assignment_id)&lt;br /&gt;
  duedates = where(parent_id: old_assignment_id)&lt;br /&gt;
  duedates.each do |orig_due_date|&lt;br /&gt;
    new_due_date = orig_due_date.dup&lt;br /&gt;
    new_due_date.parent_id = new_assignment_id&lt;br /&gt;
    new_due_date.save&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Improved Design ====&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model is focused on representing a single deadline entry. Behavior has been extracted into appropriate modules:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Responsibility Distribution After Refactoring&lt;br /&gt;
|-&lt;br /&gt;
! Concern !! Where It Lives&lt;br /&gt;
|-&lt;br /&gt;
| Permission checking || &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module (included in DueDate)&lt;br /&gt;
|-&lt;br /&gt;
| Deadline-related actions || &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module (included in parent models)&lt;br /&gt;
|-&lt;br /&gt;
| Deadline type semantics || &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model&lt;br /&gt;
|-&lt;br /&gt;
| Core deadline data || &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Key improvements:&lt;br /&gt;
* '''Instance-based behavior''': Replaced class methods with instance methods&lt;br /&gt;
* '''Clear query methods''': Simple predicates like &amp;lt;code&amp;gt;upcoming?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;overdue?&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Scopes for common queries''': &amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DueDatePermissions Module ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Currently, permission checks are split into two disconnected layers:&lt;br /&gt;
&lt;br /&gt;
'''Role-based permissions''' (in participant_helpers.rb):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def participant_permissions(authorization)&lt;br /&gt;
  case authorization&lt;br /&gt;
  when 'reader' then { can_submit: false, can_review: true, can_take_quiz: true }&lt;br /&gt;
  # ...&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Deadline-based checks''' (in Assignment model):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def submission_allowed(topic_id = nil)&lt;br /&gt;
  check_condition('submission_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These two layers never interact, leading to inconsistencies.&lt;br /&gt;
&lt;br /&gt;
==== Proposed Solution ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module integrates both layers:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Permission Methods&lt;br /&gt;
|-&lt;br /&gt;
! Method !! What It Checks&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_submit?(participant)&amp;lt;/code&amp;gt; || Submission deadline is OK/Late AND participant can submit&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_review?(participant)&amp;lt;/code&amp;gt; || Review deadline is OK/Late AND participant can review&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_take_quiz?(participant)&amp;lt;/code&amp;gt; || Quiz deadline is OK/Late AND participant can take quiz&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_review_teammate?(participant)&amp;lt;/code&amp;gt; || Teammate review deadline is OK/Late AND participant can review&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;activity_permissible?(activity)&amp;lt;/code&amp;gt; || Generic checker for any activity&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;permission_status_for(action)&amp;lt;/code&amp;gt; || Returns 'OK', 'Late', or 'No' for display&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== DueDateActions Module ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module provides a unified interface for models that own due dates (&amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt;). This centralizes the logic for querying upcoming deadlines and determining whether specific activities are currently allowed.&lt;br /&gt;
&lt;br /&gt;
==== Proposed Solution ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Methods in DueDateActions&lt;br /&gt;
|-&lt;br /&gt;
! Method !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;activity_permissible?(activity)&amp;lt;/code&amp;gt; || Checks if an activity is permitted at the current time&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;submission_permissible?&amp;lt;/code&amp;gt; || Convenience wrapper for submission checking&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;review_permissible?&amp;lt;/code&amp;gt; || Convenience wrapper for review checking&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;next_due_date(topic_id)&amp;lt;/code&amp;gt; || Resolves the next applicable deadline&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;current_stage&amp;lt;/code&amp;gt; || Returns human-readable stage name (e.g., &amp;quot;review&amp;quot;, &amp;quot;submission&amp;quot;)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;has_topic_specific_deadlines?&amp;lt;/code&amp;gt; || Determines if assignment uses topic-specific deadlines&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;copy_due_dates_to(new_parent)&amp;lt;/code&amp;gt; || Utility for cloning due dates to another assignment&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Deadline Resolution Flow ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ How next_due_date Works&lt;br /&gt;
|-&lt;br /&gt;
! Step !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 1. Topic-specific check || If topic ID provided and assignment uses staggered deadlines, check topic-level deadlines first&lt;br /&gt;
|-&lt;br /&gt;
| 2. Assignment-level fallback || If no topic deadline exists, use nearest assignment-level deadline&lt;br /&gt;
|-&lt;br /&gt;
| 3. Permission evaluation || Delegate to DueDatePermissions to determine if action is allowed&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Implementation Summary ==&lt;br /&gt;
&lt;br /&gt;
=== Models Created/Modified ===&lt;br /&gt;
&lt;br /&gt;
* '''DeadlineType''' — Canonical deadline type definitions with semantic helper methods&lt;br /&gt;
* '''DeadlineRight''' — Permission state model (OK/Late/No) with comparison methods&lt;br /&gt;
* '''DueDate''' — Refactored with instance methods, scopes, and permission checking&lt;br /&gt;
* '''TopicDueDate''' — STI subclass for topic-specific deadlines&lt;br /&gt;
* '''Assignment''' — Includes DueDateActions mixin&lt;br /&gt;
* '''SignUpTopic''' — Includes DueDateActions mixin&lt;br /&gt;
&lt;br /&gt;
=== Modules Created ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions''' — Permission checking combining deadline and role constraints&lt;br /&gt;
* '''DueDateActions''' — Deadline-related operations for parent models&lt;br /&gt;
&lt;br /&gt;
=== Key Improvements ===&lt;br /&gt;
&lt;br /&gt;
# '''Separation of Concerns''': Permission logic separated into dedicated modules&lt;br /&gt;
# '''Instance Methods''': Replaced class methods with testable instance methods&lt;br /&gt;
# '''Polymorphic Design''': Single DueDate model serves both Assignment and SignUpTopic&lt;br /&gt;
# '''Unified Permissions''': Role-based and time-based checks combined in one interface&lt;br /&gt;
# '''Data Integrity''': Removed duplicate deadline types, enforced foreign key constraints&lt;br /&gt;
&lt;br /&gt;
== Testing Overview ==&lt;br /&gt;
&lt;br /&gt;
This test suite provides coverage for both the Assignment model and the DueDate model.&lt;br /&gt;
The Assignment tests also exercise all behaviors provided by the DueDateActions mixin, since that module is included directly in the Assignment class.&lt;br /&gt;
&lt;br /&gt;
The tests are designed to verify how deadlines influence allowed activities (submission, review, quiz, etc.), how the system determines the next relevant deadline, and how deadlines are ordered.&lt;br /&gt;
&lt;br /&gt;
== Coverage Summary ==&lt;br /&gt;
&lt;br /&gt;
=== Assignment (DueDateActions mixin included) ===&lt;br /&gt;
The Assignment spec validates:&lt;br /&gt;
&lt;br /&gt;
* activity permission logic:&lt;br /&gt;
  ** &amp;lt;code&amp;gt;activity_permissible?&amp;lt;/code&amp;gt; – evaluates permissions using the next upcoming deadline&lt;br /&gt;
  ** &amp;lt;code&amp;gt;activity_permissible_for?&amp;lt;/code&amp;gt; – finds the most recent past deadline relative to a specific time&lt;br /&gt;
* deadline-based navigation:&lt;br /&gt;
  ** &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;&lt;br /&gt;
  ** &amp;lt;code&amp;gt;deadlines_properly_ordered?&amp;lt;/code&amp;gt;&lt;br /&gt;
* delegation helpers such as:&lt;br /&gt;
  ** &amp;lt;code&amp;gt;submission_permissible?&amp;lt;/code&amp;gt;&lt;br /&gt;
  ** &amp;lt;code&amp;gt;review_permissible?&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These tests ensure that Assignment correctly interprets the state of its associated due dates.&lt;br /&gt;
&lt;br /&gt;
=== DueDate Model ===&lt;br /&gt;
The DueDate spec validates:&lt;br /&gt;
&lt;br /&gt;
* required field validations (parent, due_at, deadline_type_id)&lt;br /&gt;
* round field rules (cannot be zero or negative, sets default when nil)&lt;br /&gt;
* scopes:&lt;br /&gt;
  ** &amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;&lt;br /&gt;
  ** &amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;&lt;br /&gt;
  ** &amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;&lt;br /&gt;
  ** &amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt;&lt;br /&gt;
* instance methods:&lt;br /&gt;
  ** &amp;lt;code&amp;gt;#overdue?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#upcoming?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#set&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#copy&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#deadline_type_name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#last_deadline?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#&amp;lt;=&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
* class methods:&lt;br /&gt;
  ** &amp;lt;code&amp;gt;.sort_due_dates&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.any_future_due_dates?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.copy&amp;lt;/code&amp;gt;&lt;br /&gt;
* callback:&lt;br /&gt;
  ** &amp;lt;code&amp;gt;before_save :set_default_round&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This ensures the model behaves correctly as an individual record, independent from Assignment.&lt;br /&gt;
&lt;br /&gt;
== Rationale for Test Design ==&lt;br /&gt;
&lt;br /&gt;
The test suites for DueDate and Assignment (including DueDateActions) are designed to validate two different layers of behavior:&lt;br /&gt;
* DueDate tests verify record-level behavior: validations, scopes, and individual instance methods.&lt;br /&gt;
* Assignment tests verify behavior that depends on collections of due dates, such as selecting the next deadline or determining whether an activity is permissible.&lt;br /&gt;
&lt;br /&gt;
All tests use real database records (DeadlineType, DeadlineRight, DueDate) to ensure that associations, validations, and permission checks reflect real application behavior.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Test Structure ==&lt;br /&gt;
&lt;br /&gt;
The suite uses nested RSpec contexts:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
describe 'activity_permissible_for?' do&lt;br /&gt;
  context 'when past deadline exists' do&lt;br /&gt;
    it 'returns permission from most recent past deadline'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when only future deadlines exist' do&lt;br /&gt;
    it 'returns false'&lt;br /&gt;
  end&lt;br /&gt;
end &amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Contexts clearly describe the situation being tested, while individual examples describe expected behaviors.&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
=== Demo Link ===&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add deadline notification system using the new permission framework&lt;br /&gt;
* Implement deadline templates for common assignment patterns&lt;br /&gt;
* Add API endpoints for mobile app integration&lt;br /&gt;
* Create admin dashboard for monitoring deadline compliance across courses&lt;br /&gt;
* Add automated deadline extension workflows based on configurable rules&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167223</id>
		<title>CSC/ECE 517 Fall 2025 - E2566. Finish DueDates</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167223"/>
		<updated>2025-12-02T15:38:08Z</updated>

		<summary type="html">&lt;p&gt;Skim253: /* Proposed Solution */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
This project aims to complete the implementation of the DueDate system in Expertiza. The current implementation consists mostly of class methods and lacks proper definition of different deadline types and permission checking functionality. This redesign will create a more robust, readable, and maintainable due date system.&lt;br /&gt;
&lt;br /&gt;
=== Existing Components ===&lt;br /&gt;
* &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model with polymorphic parent association (Assignment/SignUpTopic)&lt;br /&gt;
* &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; subclasses&lt;br /&gt;
* Basic CRUD operations (within &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AssignmentForm&amp;lt;/code&amp;gt;) and sorting functionality (&amp;lt;code&amp;gt;deadline_sort&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Database schema with &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt; field&lt;br /&gt;
&lt;br /&gt;
=== Current Behavior ===&lt;br /&gt;
&lt;br /&gt;
====Admin's View====&lt;br /&gt;
[[File:Duedates.png|800px]]&lt;br /&gt;
&lt;br /&gt;
When editing an assignment, due dates can be modified under the Due Dates tab. Each row contains the following columns: &amp;lt;code&amp;gt;Date &amp;amp; Time&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Use Date Updater&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Submission Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Teammate Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Meta-Review Allowed&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Reminder&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:Assignments-CurrentStage.png|800px]]&lt;br /&gt;
&lt;br /&gt;
In the Assignments tab, the current stage and stage deadline of each assignment is displayed.&lt;br /&gt;
&lt;br /&gt;
[[File:Due-date-topic-instructor.png|800px]]&lt;br /&gt;
&lt;br /&gt;
[[File:Due-date-topic-instructor-2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
In the Topics subtab, due dates for each topic are displayed.&lt;br /&gt;
&lt;br /&gt;
====Student's View====&lt;br /&gt;
[[File:Student-assignments.png|800px]]&lt;br /&gt;
&lt;br /&gt;
Students can view due date information under &amp;lt;code&amp;gt;Current State&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Stage Deadline&amp;lt;/code&amp;gt;. The actions available to students are determined by the current DueDate status.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-not-over.png|800px]]&lt;br /&gt;
&lt;br /&gt;
During the submission period, students can access the &amp;lt;code&amp;gt;Submit a hyperlink&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Submit a file&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-over.png|800px]]&lt;br /&gt;
&lt;br /&gt;
Once the due date has passed, students cannot submit either a hyperlink or a file.&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
&lt;br /&gt;
Currently, there are some glaring issues with how due_dates was created in the first place, including redundancy and lack of modularity.&lt;br /&gt;
&lt;br /&gt;
'''Modeling &amp;amp; Structural Issues'''&lt;br /&gt;
# No dedicated &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to define different kinds of deadlines. The existing &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; class only serves as an association for &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; models, and is never referenced in the current &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; implementation.&lt;br /&gt;
# Duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table, which may lead to potential bugs.&lt;br /&gt;
# Incomplete deadline type definitions — &amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt; need to be added.&lt;br /&gt;
# No clear separation of concerns — data persistence, business logic, and permission logic are mixed in a single model.&lt;br /&gt;
&lt;br /&gt;
'''Permission &amp;amp; Behavior Issues'''&lt;br /&gt;
# Overuse of class methods making the code difficult to maintain.&lt;br /&gt;
# Missing permission checking logic for user actions that combines both role-based and time-based constraints.&lt;br /&gt;
&lt;br /&gt;
=== Tasks Identified ===&lt;br /&gt;
&lt;br /&gt;
'''Modeling &amp;amp; Structural'''&lt;br /&gt;
# Refactor the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model to separate persistence, business logic, and permission logic into distinct layers.&lt;br /&gt;
# Implement reusable mixins (&amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;) to handle permission evaluation and deadline-related operations.&lt;br /&gt;
# Introduce instance-level methods (e.g., &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;) to replace class-level utilities and improve testability.&lt;br /&gt;
&lt;br /&gt;
'''Permission &amp;amp; Behavior'''&lt;br /&gt;
# Integrate role-based and time-based permission checks within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; to ensure consistent access control.&lt;br /&gt;
# Redesign the &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.&lt;br /&gt;
# Add missing deadline types (&amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt;) and remove duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries for data integrity.&lt;br /&gt;
&lt;br /&gt;
== Proposed Solution ==&lt;br /&gt;
&lt;br /&gt;
The proposed solution restructures the due date system by separating concerns across dedicated models and mixin modules. Deadline definitions (&amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt;), core deadline data (&amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;), and behavioral logic (&amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;) are isolated so each component has a single clear responsibility. This modular design improves readability, reduces coupling, and makes deadline-related behavior easier to extend and maintain.&lt;br /&gt;
&lt;br /&gt;
=== Architecture Overview ===&lt;br /&gt;
&lt;br /&gt;
[[File:Duedate-diagram.png|800px]]&lt;br /&gt;
&lt;br /&gt;
This diagram visualizes the redesigned architecture with five distinct layers:&lt;br /&gt;
&lt;br /&gt;
* '''Layer 1 (Blue)''': Parent models (Assignment, SignUpTopic) that own due dates&lt;br /&gt;
* '''Layer 2 (Orange)''': DueDateActions module providing action-level queries&lt;br /&gt;
* '''Layer 3 (Red)''': DueDate model managing core deadline data&lt;br /&gt;
* '''Layer 4 (Orange)''': DueDatePermissions module evaluating permissions&lt;br /&gt;
* '''Layer 5 (Green)''': Reference data models (DeadlineType, DeadlineRight, Participant)&lt;br /&gt;
&lt;br /&gt;
The flow works as follows: Parent models include DueDateActions, which queries DueDate objects. Each DueDate includes DueDatePermissions to evaluate whether actions are allowed based on DeadlineRight states and Participant role flags.&lt;br /&gt;
&lt;br /&gt;
=== DeadlineType Model ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Although the current codebase includes a &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model, it only serves as a passive lookup table. In practice, most parts of the system still rely on hard-coded numeric IDs or manual lookups:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Example of current usage&lt;br /&gt;
deadline_type_id = DeadlineType.find_by(name: 'review').id&lt;br /&gt;
if due_date.deadline_type_id == deadline_type_id&lt;br /&gt;
  # Perform review-related logic&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This leads to several structural issues:&lt;br /&gt;
* Inconsistent references (some use IDs, others strings)&lt;br /&gt;
* Tight coupling between &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
* Lack of semantic helper methods (e.g., &amp;lt;code&amp;gt;review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;submission?&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Data duplication and integrity risks (two &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; rows)&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; Table Structure&lt;br /&gt;
|- &lt;br /&gt;
! Column !! Type !! Description&lt;br /&gt;
|-&lt;br /&gt;
| id || BIGINT || Primary key, referenced by &amp;lt;code&amp;gt;due_dates.deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| name || VARCHAR(255) || Unique symbolic name (e.g., &amp;lt;code&amp;gt;submission&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| description || TEXT || Human-readable explanation of the deadline category&lt;br /&gt;
|-&lt;br /&gt;
| created_at || TIMESTAMP || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| updated_at || TIMESTAMP || Last update timestamp&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Deadline Type Definitions ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Canonical Deadline Type Entries&lt;br /&gt;
|-&lt;br /&gt;
! ID !! Name !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 1 || submission || Student submission deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 2 || review || Peer review deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 3 || teammate_review || Team member evaluation deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 5 || metareview || Meta-review deadlines (backward compatibility)&lt;br /&gt;
|-&lt;br /&gt;
| 6 || drop_topic || Topic drop deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 7 || signup || Topic signup deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 8 || team_formation || Team formation deadlines (duplicate cleaned)&lt;br /&gt;
|-&lt;br /&gt;
| 11 || quiz || Quiz completion deadlines&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== DeadlineRight Model ===&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model represents the permission level for a specific action at a given deadline:&lt;br /&gt;
&lt;br /&gt;
* '''OK''': Action is allowed without penalty&lt;br /&gt;
* '''Late''': Action is allowed but marked as late&lt;br /&gt;
* '''No''': Action is not permitted&lt;br /&gt;
&lt;br /&gt;
These values are referenced by &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; through fields such as &amp;lt;code&amp;gt;submission_allowed_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review_allowed_id&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;quiz_allowed_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_rights&amp;lt;/code&amp;gt; Table Structure&lt;br /&gt;
|-&lt;br /&gt;
! Column !! Type !! Description&lt;br /&gt;
|-&lt;br /&gt;
| id || BIGINT || Primary key, referenced by &amp;lt;code&amp;gt;due_dates.*_allowed_id&amp;lt;/code&amp;gt; fields&lt;br /&gt;
|-&lt;br /&gt;
| name || VARCHAR(255) || Unique permission name (&amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| created_at || TIMESTAMP || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| updated_at || TIMESTAMP || Last update timestamp&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== DueDate Model Refactoring ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model currently mixes persistence logic, deadline calculation, and permission checking, violating the Single Responsibility Principle. Most of its logic is implemented as class methods, which makes testing and extension difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Old design: class method that mixes concerns&lt;br /&gt;
def self.copy(old_assignment_id, new_assignment_id)&lt;br /&gt;
  duedates = where(parent_id: old_assignment_id)&lt;br /&gt;
  duedates.each do |orig_due_date|&lt;br /&gt;
    new_due_date = orig_due_date.dup&lt;br /&gt;
    new_due_date.parent_id = new_assignment_id&lt;br /&gt;
    new_due_date.save&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Improved Design ====&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model is focused on representing a single deadline entry. Behavior has been extracted into appropriate modules:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Responsibility Distribution After Refactoring&lt;br /&gt;
|-&lt;br /&gt;
! Concern !! Where It Lives&lt;br /&gt;
|-&lt;br /&gt;
| Permission checking || &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module (included in DueDate)&lt;br /&gt;
|-&lt;br /&gt;
| Deadline-related actions || &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module (included in parent models)&lt;br /&gt;
|-&lt;br /&gt;
| Deadline type semantics || &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model&lt;br /&gt;
|-&lt;br /&gt;
| Core deadline data || &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Key improvements:&lt;br /&gt;
* '''Instance-based behavior''': Replaced class methods with instance methods&lt;br /&gt;
* '''Clear query methods''': Simple predicates like &amp;lt;code&amp;gt;upcoming?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;overdue?&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Scopes for common queries''': &amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DueDatePermissions Module ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Currently, permission checks are split into two disconnected layers:&lt;br /&gt;
&lt;br /&gt;
'''Role-based permissions''' (in participant_helpers.rb):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def participant_permissions(authorization)&lt;br /&gt;
  case authorization&lt;br /&gt;
  when 'reader' then { can_submit: false, can_review: true, can_take_quiz: true }&lt;br /&gt;
  # ...&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Deadline-based checks''' (in Assignment model):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def submission_allowed(topic_id = nil)&lt;br /&gt;
  check_condition('submission_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These two layers never interact, leading to inconsistencies.&lt;br /&gt;
&lt;br /&gt;
==== Proposed Solution ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module integrates both layers:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Permission Methods&lt;br /&gt;
|-&lt;br /&gt;
! Method !! What It Checks&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_submit?(participant)&amp;lt;/code&amp;gt; || Submission deadline is OK/Late AND participant can submit&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_review?(participant)&amp;lt;/code&amp;gt; || Review deadline is OK/Late AND participant can review&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_take_quiz?(participant)&amp;lt;/code&amp;gt; || Quiz deadline is OK/Late AND participant can take quiz&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_review_teammate?(participant)&amp;lt;/code&amp;gt; || Teammate review deadline is OK/Late AND participant can review&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;activity_permissible?(activity)&amp;lt;/code&amp;gt; || Generic checker for any activity&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;permission_status_for(action)&amp;lt;/code&amp;gt; || Returns 'OK', 'Late', or 'No' for display&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== DueDateActions Module ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module provides a unified interface for models that own due dates (&amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt;). This centralizes the logic for querying upcoming deadlines and determining whether specific activities are currently allowed.&lt;br /&gt;
&lt;br /&gt;
==== Proposed Solution ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Methods in DueDateActions&lt;br /&gt;
|-&lt;br /&gt;
! Method !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;activity_permissible?(activity)&amp;lt;/code&amp;gt; || Checks if an activity is permitted at the current time&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;submission_permissible?&amp;lt;/code&amp;gt; || Convenience wrapper for submission checking&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;review_permissible?&amp;lt;/code&amp;gt; || Convenience wrapper for review checking&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;next_due_date(topic_id)&amp;lt;/code&amp;gt; || Resolves the next applicable deadline&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;current_stage&amp;lt;/code&amp;gt; || Returns human-readable stage name (e.g., &amp;quot;review&amp;quot;, &amp;quot;submission&amp;quot;)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;has_topic_specific_deadlines?&amp;lt;/code&amp;gt; || Determines if assignment uses topic-specific deadlines&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;copy_due_dates_to(new_parent)&amp;lt;/code&amp;gt; || Utility for cloning due dates to another assignment&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Deadline Resolution Flow ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ How next_due_date Works&lt;br /&gt;
|-&lt;br /&gt;
! Step !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 1. Topic-specific check || If topic ID provided and assignment uses staggered deadlines, check topic-level deadlines first&lt;br /&gt;
|-&lt;br /&gt;
| 2. Assignment-level fallback || If no topic deadline exists, use nearest assignment-level deadline&lt;br /&gt;
|-&lt;br /&gt;
| 3. Permission evaluation || Delegate to DueDatePermissions to determine if action is allowed&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Implementation Summary ==&lt;br /&gt;
&lt;br /&gt;
=== Models Created/Modified ===&lt;br /&gt;
&lt;br /&gt;
* '''DeadlineType''' — Canonical deadline type definitions with semantic helper methods&lt;br /&gt;
* '''DeadlineRight''' — Permission state model (OK/Late/No) with comparison methods&lt;br /&gt;
* '''DueDate''' — Refactored with instance methods, scopes, and permission checking&lt;br /&gt;
* '''TopicDueDate''' — STI subclass for topic-specific deadlines&lt;br /&gt;
* '''Assignment''' — Includes DueDateActions mixin&lt;br /&gt;
* '''SignUpTopic''' — Includes DueDateActions mixin&lt;br /&gt;
&lt;br /&gt;
=== Modules Created ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions''' — Permission checking combining deadline and role constraints&lt;br /&gt;
* '''DueDateActions''' — Deadline-related operations for parent models&lt;br /&gt;
&lt;br /&gt;
=== Key Improvements ===&lt;br /&gt;
&lt;br /&gt;
# '''Separation of Concerns''': Permission logic separated into dedicated modules&lt;br /&gt;
# '''Instance Methods''': Replaced class methods with testable instance methods&lt;br /&gt;
# '''Polymorphic Design''': Single DueDate model serves both Assignment and SignUpTopic&lt;br /&gt;
# '''Unified Permissions''': Role-based and time-based checks combined in one interface&lt;br /&gt;
# '''Data Integrity''': Removed duplicate deadline types, enforced foreign key constraints&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
The test suite includes comprehensive RSpec tests for the DueDate model using nested contexts, edge case coverage, and error handling.&lt;br /&gt;
&lt;br /&gt;
=== Current Coverage ===&lt;br /&gt;
&lt;br /&gt;
* '''Validations''': Required fields (parent, due_at, deadline_type_id), round validation, error messages&lt;br /&gt;
* '''Scopes''': &amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt; with edge cases&lt;br /&gt;
* '''Instance Methods''': &amp;lt;code&amp;gt;#overdue?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#upcoming?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#set&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#copy&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#deadline_type_name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#last_deadline?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#&amp;lt;=&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Class Methods''': &amp;lt;code&amp;gt;.sort_due_dates&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.any_future_due_dates?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.copy&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Callbacks''': &amp;lt;code&amp;gt;before_save :set_default_round&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
Tests follow RSpec best practices with nested contexts:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
describe 'validations' do&lt;br /&gt;
  context 'when parent is missing' do&lt;br /&gt;
    it 'is invalid' do&lt;br /&gt;
      expect(due_date).to be_invalid&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'has error on parent field' do&lt;br /&gt;
      due_date.valid?&lt;br /&gt;
      expect(due_date.errors[:parent]).to include('must exist')&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when round validation' do&lt;br /&gt;
    context 'with round value of 0' do&lt;br /&gt;
      it 'is invalid' do&lt;br /&gt;
        expect(due_date).to be_invalid&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'with nil round' do&lt;br /&gt;
      it 'sets default round to 1' do&lt;br /&gt;
        expect(due_date.round).to eq(1)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Test Metrics ===&lt;br /&gt;
&lt;br /&gt;
* '''Total examples''': 438 tests&lt;br /&gt;
* '''DueDate model''': 60+ examples&lt;br /&gt;
* '''Code coverage''': 100% of DueDate methods&lt;br /&gt;
* '''Execution time''': ~12 seconds&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
=== Demo Link ===&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add deadline notification system using the new permission framework&lt;br /&gt;
* Implement deadline templates for common assignment patterns&lt;br /&gt;
* Add API endpoints for mobile app integration&lt;br /&gt;
* Create admin dashboard for monitoring deadline compliance across courses&lt;br /&gt;
* Add automated deadline extension workflows based on configurable rules&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167222</id>
		<title>CSC/ECE 517 Fall 2025 - E2566. Finish DueDates</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167222"/>
		<updated>2025-12-02T15:36:47Z</updated>

		<summary type="html">&lt;p&gt;Skim253: /* Testing */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
This project aims to complete the implementation of the DueDate system in Expertiza. The current implementation consists mostly of class methods and lacks proper definition of different deadline types and permission checking functionality. This redesign will create a more robust, readable, and maintainable due date system.&lt;br /&gt;
&lt;br /&gt;
=== Existing Components ===&lt;br /&gt;
* &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model with polymorphic parent association (Assignment/SignUpTopic)&lt;br /&gt;
* &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; subclasses&lt;br /&gt;
* Basic CRUD operations (within &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AssignmentForm&amp;lt;/code&amp;gt;) and sorting functionality (&amp;lt;code&amp;gt;deadline_sort&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Database schema with &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt; field&lt;br /&gt;
&lt;br /&gt;
=== Current Behavior ===&lt;br /&gt;
&lt;br /&gt;
====Admin's View====&lt;br /&gt;
[[File:Duedates.png|800px]]&lt;br /&gt;
&lt;br /&gt;
When editing an assignment, due dates can be modified under the Due Dates tab. Each row contains the following columns: &amp;lt;code&amp;gt;Date &amp;amp; Time&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Use Date Updater&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Submission Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Teammate Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Meta-Review Allowed&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Reminder&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:Assignments-CurrentStage.png|800px]]&lt;br /&gt;
&lt;br /&gt;
In the Assignments tab, the current stage and stage deadline of each assignment is displayed.&lt;br /&gt;
&lt;br /&gt;
[[File:Due-date-topic-instructor.png|800px]]&lt;br /&gt;
&lt;br /&gt;
[[File:Due-date-topic-instructor-2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
In the Topics subtab, due dates for each topic are displayed.&lt;br /&gt;
&lt;br /&gt;
====Student's View====&lt;br /&gt;
[[File:Student-assignments.png|800px]]&lt;br /&gt;
&lt;br /&gt;
Students can view due date information under &amp;lt;code&amp;gt;Current State&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Stage Deadline&amp;lt;/code&amp;gt;. The actions available to students are determined by the current DueDate status.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-not-over.png|800px]]&lt;br /&gt;
&lt;br /&gt;
During the submission period, students can access the &amp;lt;code&amp;gt;Submit a hyperlink&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Submit a file&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-over.png|800px]]&lt;br /&gt;
&lt;br /&gt;
Once the due date has passed, students cannot submit either a hyperlink or a file.&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
&lt;br /&gt;
Currently, there are some glaring issues with how due_dates was created in the first place, including redundancy and lack of modularity.&lt;br /&gt;
&lt;br /&gt;
'''Modeling &amp;amp; Structural Issues'''&lt;br /&gt;
# No dedicated &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to define different kinds of deadlines. The existing &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; class only serves as an association for &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; models, and is never referenced in the current &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; implementation.&lt;br /&gt;
# Duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table, which may lead to potential bugs.&lt;br /&gt;
# Incomplete deadline type definitions — &amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt; need to be added.&lt;br /&gt;
# No clear separation of concerns — data persistence, business logic, and permission logic are mixed in a single model.&lt;br /&gt;
&lt;br /&gt;
'''Permission &amp;amp; Behavior Issues'''&lt;br /&gt;
# Overuse of class methods making the code difficult to maintain.&lt;br /&gt;
# Missing permission checking logic for user actions that combines both role-based and time-based constraints.&lt;br /&gt;
&lt;br /&gt;
=== Tasks Identified ===&lt;br /&gt;
&lt;br /&gt;
'''Modeling &amp;amp; Structural'''&lt;br /&gt;
# Refactor the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model to separate persistence, business logic, and permission logic into distinct layers.&lt;br /&gt;
# Implement reusable mixins (&amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;) to handle permission evaluation and deadline-related operations.&lt;br /&gt;
# Introduce instance-level methods (e.g., &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;) to replace class-level utilities and improve testability.&lt;br /&gt;
&lt;br /&gt;
'''Permission &amp;amp; Behavior'''&lt;br /&gt;
# Integrate role-based and time-based permission checks within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; to ensure consistent access control.&lt;br /&gt;
# Redesign the &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.&lt;br /&gt;
# Add missing deadline types (&amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt;) and remove duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries for data integrity.&lt;br /&gt;
&lt;br /&gt;
== Proposed Solution ==&lt;br /&gt;
&lt;br /&gt;
The proposed solution restructures the due date system by separating concerns across dedicated models and mixin modules. Deadline definitions (&amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt;), core deadline data (&amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;), and behavioral logic (&amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;) are isolated so each component has a single clear responsibility. This modular design improves readability, reduces coupling, and makes deadline-related behavior easier to extend and maintain.&lt;br /&gt;
&lt;br /&gt;
=== Architecture Overview ===&lt;br /&gt;
&lt;br /&gt;
[[File:Duedate-diagram.png|800px]]&lt;br /&gt;
&lt;br /&gt;
This diagram visualizes the redesigned architecture with five distinct layers:&lt;br /&gt;
&lt;br /&gt;
* '''Layer 1 (Blue)''': Parent models (Assignment, SignUpTopic) that own due dates&lt;br /&gt;
* '''Layer 2 (Orange)''': DueDateActions module providing action-level queries&lt;br /&gt;
* '''Layer 3 (Red)''': DueDate model managing core deadline data&lt;br /&gt;
* '''Layer 4 (Orange)''': DueDatePermissions module evaluating permissions&lt;br /&gt;
* '''Layer 5 (Green)''': Reference data models (DeadlineType, DeadlineRight, Participant)&lt;br /&gt;
&lt;br /&gt;
The flow works as follows: Parent models include DueDateActions, which queries DueDate objects. Each DueDate includes DueDatePermissions to evaluate whether actions are allowed based on DeadlineRight states and Participant role flags.&lt;br /&gt;
&lt;br /&gt;
=== 1. DeadlineType Model ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Although the current codebase includes a &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model, it only serves as a passive lookup table. In practice, most parts of the system still rely on hard-coded numeric IDs or manual lookups:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Example of current usage&lt;br /&gt;
deadline_type_id = DeadlineType.find_by(name: 'review').id&lt;br /&gt;
if due_date.deadline_type_id == deadline_type_id&lt;br /&gt;
  # Perform review-related logic&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This leads to several structural issues:&lt;br /&gt;
* Inconsistent references (some use IDs, others strings)&lt;br /&gt;
* Tight coupling between &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
* Lack of semantic helper methods (e.g., &amp;lt;code&amp;gt;review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;submission?&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Data duplication and integrity risks (two &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; rows)&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; Table Structure&lt;br /&gt;
|- &lt;br /&gt;
! Column !! Type !! Description&lt;br /&gt;
|-&lt;br /&gt;
| id || BIGINT || Primary key, referenced by &amp;lt;code&amp;gt;due_dates.deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| name || VARCHAR(255) || Unique symbolic name (e.g., &amp;lt;code&amp;gt;submission&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| description || TEXT || Human-readable explanation of the deadline category&lt;br /&gt;
|-&lt;br /&gt;
| created_at || TIMESTAMP || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| updated_at || TIMESTAMP || Last update timestamp&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Deadline Type Definitions ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Canonical Deadline Type Entries&lt;br /&gt;
|-&lt;br /&gt;
! ID !! Name !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 1 || submission || Student submission deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 2 || review || Peer review deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 3 || teammate_review || Team member evaluation deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 5 || metareview || Meta-review deadlines (backward compatibility)&lt;br /&gt;
|-&lt;br /&gt;
| 6 || drop_topic || Topic drop deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 7 || signup || Topic signup deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 8 || team_formation || Team formation deadlines (duplicate cleaned)&lt;br /&gt;
|-&lt;br /&gt;
| 11 || quiz || Quiz completion deadlines&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== 2. DeadlineRight Model ===&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model represents the permission level for a specific action at a given deadline:&lt;br /&gt;
&lt;br /&gt;
* '''OK''': Action is allowed without penalty&lt;br /&gt;
* '''Late''': Action is allowed but marked as late&lt;br /&gt;
* '''No''': Action is not permitted&lt;br /&gt;
&lt;br /&gt;
These values are referenced by &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; through fields such as &amp;lt;code&amp;gt;submission_allowed_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review_allowed_id&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;quiz_allowed_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_rights&amp;lt;/code&amp;gt; Table Structure&lt;br /&gt;
|-&lt;br /&gt;
! Column !! Type !! Description&lt;br /&gt;
|-&lt;br /&gt;
| id || BIGINT || Primary key, referenced by &amp;lt;code&amp;gt;due_dates.*_allowed_id&amp;lt;/code&amp;gt; fields&lt;br /&gt;
|-&lt;br /&gt;
| name || VARCHAR(255) || Unique permission name (&amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| created_at || TIMESTAMP || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| updated_at || TIMESTAMP || Last update timestamp&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== 3. DueDate Model Refactoring ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model currently mixes persistence logic, deadline calculation, and permission checking, violating the Single Responsibility Principle. Most of its logic is implemented as class methods, which makes testing and extension difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Old design: class method that mixes concerns&lt;br /&gt;
def self.copy(old_assignment_id, new_assignment_id)&lt;br /&gt;
  duedates = where(parent_id: old_assignment_id)&lt;br /&gt;
  duedates.each do |orig_due_date|&lt;br /&gt;
    new_due_date = orig_due_date.dup&lt;br /&gt;
    new_due_date.parent_id = new_assignment_id&lt;br /&gt;
    new_due_date.save&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Improved Design ====&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model is focused on representing a single deadline entry. Behavior has been extracted into appropriate modules:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Responsibility Distribution After Refactoring&lt;br /&gt;
|-&lt;br /&gt;
! Concern !! Where It Lives&lt;br /&gt;
|-&lt;br /&gt;
| Permission checking || &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module (included in DueDate)&lt;br /&gt;
|-&lt;br /&gt;
| Deadline-related actions || &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module (included in parent models)&lt;br /&gt;
|-&lt;br /&gt;
| Deadline type semantics || &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model&lt;br /&gt;
|-&lt;br /&gt;
| Core deadline data || &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Key improvements:&lt;br /&gt;
* '''Instance-based behavior''': Replaced class methods with instance methods&lt;br /&gt;
* '''Clear query methods''': Simple predicates like &amp;lt;code&amp;gt;upcoming?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;overdue?&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Scopes for common queries''': &amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 4. DueDatePermissions Module ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Currently, permission checks are split into two disconnected layers:&lt;br /&gt;
&lt;br /&gt;
'''Role-based permissions''' (in participant_helpers.rb):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def participant_permissions(authorization)&lt;br /&gt;
  case authorization&lt;br /&gt;
  when 'reader' then { can_submit: false, can_review: true, can_take_quiz: true }&lt;br /&gt;
  # ...&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Deadline-based checks''' (in Assignment model):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def submission_allowed(topic_id = nil)&lt;br /&gt;
  check_condition('submission_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These two layers never interact, leading to inconsistencies.&lt;br /&gt;
&lt;br /&gt;
==== Proposed Solution ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module integrates both layers:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Permission Methods&lt;br /&gt;
|-&lt;br /&gt;
! Method !! What It Checks&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_submit?(participant)&amp;lt;/code&amp;gt; || Submission deadline is OK/Late AND participant can submit&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_review?(participant)&amp;lt;/code&amp;gt; || Review deadline is OK/Late AND participant can review&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_take_quiz?(participant)&amp;lt;/code&amp;gt; || Quiz deadline is OK/Late AND participant can take quiz&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_review_teammate?(participant)&amp;lt;/code&amp;gt; || Teammate review deadline is OK/Late AND participant can review&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;activity_permissible?(activity)&amp;lt;/code&amp;gt; || Generic checker for any activity&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;permission_status_for(action)&amp;lt;/code&amp;gt; || Returns 'OK', 'Late', or 'No' for display&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== 5. DueDateActions Module ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module provides a unified interface for models that own due dates (&amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt;). This centralizes the logic for querying upcoming deadlines and determining whether specific activities are currently allowed.&lt;br /&gt;
&lt;br /&gt;
==== Proposed Solution ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Methods in DueDateActions&lt;br /&gt;
|-&lt;br /&gt;
! Method !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;activity_permissible?(activity)&amp;lt;/code&amp;gt; || Checks if an activity is permitted at the current time&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;submission_permissible?&amp;lt;/code&amp;gt; || Convenience wrapper for submission checking&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;review_permissible?&amp;lt;/code&amp;gt; || Convenience wrapper for review checking&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;next_due_date(topic_id)&amp;lt;/code&amp;gt; || Resolves the next applicable deadline&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;current_stage&amp;lt;/code&amp;gt; || Returns human-readable stage name (e.g., &amp;quot;review&amp;quot;, &amp;quot;submission&amp;quot;)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;has_topic_specific_deadlines?&amp;lt;/code&amp;gt; || Determines if assignment uses topic-specific deadlines&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;copy_due_dates_to(new_parent)&amp;lt;/code&amp;gt; || Utility for cloning due dates to another assignment&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Deadline Resolution Flow ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ How next_due_date Works&lt;br /&gt;
|-&lt;br /&gt;
! Step !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 1. Topic-specific check || If topic ID provided and assignment uses staggered deadlines, check topic-level deadlines first&lt;br /&gt;
|-&lt;br /&gt;
| 2. Assignment-level fallback || If no topic deadline exists, use nearest assignment-level deadline&lt;br /&gt;
|-&lt;br /&gt;
| 3. Permission evaluation || Delegate to DueDatePermissions to determine if action is allowed&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Implementation Summary ==&lt;br /&gt;
&lt;br /&gt;
=== Models Created/Modified ===&lt;br /&gt;
&lt;br /&gt;
* '''DeadlineType''' — Canonical deadline type definitions with semantic helper methods&lt;br /&gt;
* '''DeadlineRight''' — Permission state model (OK/Late/No) with comparison methods&lt;br /&gt;
* '''DueDate''' — Refactored with instance methods, scopes, and permission checking&lt;br /&gt;
* '''TopicDueDate''' — STI subclass for topic-specific deadlines&lt;br /&gt;
* '''Assignment''' — Includes DueDateActions mixin&lt;br /&gt;
* '''SignUpTopic''' — Includes DueDateActions mixin&lt;br /&gt;
&lt;br /&gt;
=== Modules Created ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions''' — Permission checking combining deadline and role constraints&lt;br /&gt;
* '''DueDateActions''' — Deadline-related operations for parent models&lt;br /&gt;
&lt;br /&gt;
=== Key Improvements ===&lt;br /&gt;
&lt;br /&gt;
# '''Separation of Concerns''': Permission logic separated into dedicated modules&lt;br /&gt;
# '''Instance Methods''': Replaced class methods with testable instance methods&lt;br /&gt;
# '''Polymorphic Design''': Single DueDate model serves both Assignment and SignUpTopic&lt;br /&gt;
# '''Unified Permissions''': Role-based and time-based checks combined in one interface&lt;br /&gt;
# '''Data Integrity''': Removed duplicate deadline types, enforced foreign key constraints&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
The test suite includes comprehensive RSpec tests for the DueDate model using nested contexts, edge case coverage, and error handling.&lt;br /&gt;
&lt;br /&gt;
=== Current Coverage ===&lt;br /&gt;
&lt;br /&gt;
* '''Validations''': Required fields (parent, due_at, deadline_type_id), round validation, error messages&lt;br /&gt;
* '''Scopes''': &amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt; with edge cases&lt;br /&gt;
* '''Instance Methods''': &amp;lt;code&amp;gt;#overdue?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#upcoming?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#set&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#copy&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#deadline_type_name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#last_deadline?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#&amp;lt;=&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Class Methods''': &amp;lt;code&amp;gt;.sort_due_dates&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.any_future_due_dates?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.copy&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Callbacks''': &amp;lt;code&amp;gt;before_save :set_default_round&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
Tests follow RSpec best practices with nested contexts:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
describe 'validations' do&lt;br /&gt;
  context 'when parent is missing' do&lt;br /&gt;
    it 'is invalid' do&lt;br /&gt;
      expect(due_date).to be_invalid&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'has error on parent field' do&lt;br /&gt;
      due_date.valid?&lt;br /&gt;
      expect(due_date.errors[:parent]).to include('must exist')&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when round validation' do&lt;br /&gt;
    context 'with round value of 0' do&lt;br /&gt;
      it 'is invalid' do&lt;br /&gt;
        expect(due_date).to be_invalid&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'with nil round' do&lt;br /&gt;
      it 'sets default round to 1' do&lt;br /&gt;
        expect(due_date.round).to eq(1)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Test Metrics ===&lt;br /&gt;
&lt;br /&gt;
* '''Total examples''': 438 tests&lt;br /&gt;
* '''DueDate model''': 60+ examples&lt;br /&gt;
* '''Code coverage''': 100% of DueDate methods&lt;br /&gt;
* '''Execution time''': ~12 seconds&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
=== Demo Link ===&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add deadline notification system using the new permission framework&lt;br /&gt;
* Implement deadline templates for common assignment patterns&lt;br /&gt;
* Add API endpoints for mobile app integration&lt;br /&gt;
* Create admin dashboard for monitoring deadline compliance across courses&lt;br /&gt;
* Add automated deadline extension workflows based on configurable rules&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167221</id>
		<title>CSC/ECE 517 Fall 2025 - E2566. Finish DueDates</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167221"/>
		<updated>2025-12-02T15:36:13Z</updated>

		<summary type="html">&lt;p&gt;Skim253: /* Purpose */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
This project aims to complete the implementation of the DueDate system in Expertiza. The current implementation consists mostly of class methods and lacks proper definition of different deadline types and permission checking functionality. This redesign will create a more robust, readable, and maintainable due date system.&lt;br /&gt;
&lt;br /&gt;
=== Existing Components ===&lt;br /&gt;
* &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model with polymorphic parent association (Assignment/SignUpTopic)&lt;br /&gt;
* &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; subclasses&lt;br /&gt;
* Basic CRUD operations (within &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AssignmentForm&amp;lt;/code&amp;gt;) and sorting functionality (&amp;lt;code&amp;gt;deadline_sort&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Database schema with &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt; field&lt;br /&gt;
&lt;br /&gt;
=== Current Behavior ===&lt;br /&gt;
&lt;br /&gt;
====Admin's View====&lt;br /&gt;
[[File:Duedates.png|800px]]&lt;br /&gt;
&lt;br /&gt;
When editing an assignment, due dates can be modified under the Due Dates tab. Each row contains the following columns: &amp;lt;code&amp;gt;Date &amp;amp; Time&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Use Date Updater&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Submission Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Teammate Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Meta-Review Allowed&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Reminder&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:Assignments-CurrentStage.png|800px]]&lt;br /&gt;
&lt;br /&gt;
In the Assignments tab, the current stage and stage deadline of each assignment is displayed.&lt;br /&gt;
&lt;br /&gt;
[[File:Due-date-topic-instructor.png|800px]]&lt;br /&gt;
&lt;br /&gt;
[[File:Due-date-topic-instructor-2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
In the Topics subtab, due dates for each topic are displayed.&lt;br /&gt;
&lt;br /&gt;
====Student's View====&lt;br /&gt;
[[File:Student-assignments.png|800px]]&lt;br /&gt;
&lt;br /&gt;
Students can view due date information under &amp;lt;code&amp;gt;Current State&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Stage Deadline&amp;lt;/code&amp;gt;. The actions available to students are determined by the current DueDate status.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-not-over.png|800px]]&lt;br /&gt;
&lt;br /&gt;
During the submission period, students can access the &amp;lt;code&amp;gt;Submit a hyperlink&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Submit a file&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-over.png|800px]]&lt;br /&gt;
&lt;br /&gt;
Once the due date has passed, students cannot submit either a hyperlink or a file.&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
&lt;br /&gt;
Currently, there are some glaring issues with how due_dates was created in the first place, including redundancy and lack of modularity.&lt;br /&gt;
&lt;br /&gt;
'''Modeling &amp;amp; Structural Issues'''&lt;br /&gt;
# No dedicated &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to define different kinds of deadlines. The existing &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; class only serves as an association for &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; models, and is never referenced in the current &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; implementation.&lt;br /&gt;
# Duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table, which may lead to potential bugs.&lt;br /&gt;
# Incomplete deadline type definitions — &amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt; need to be added.&lt;br /&gt;
# No clear separation of concerns — data persistence, business logic, and permission logic are mixed in a single model.&lt;br /&gt;
&lt;br /&gt;
'''Permission &amp;amp; Behavior Issues'''&lt;br /&gt;
# Overuse of class methods making the code difficult to maintain.&lt;br /&gt;
# Missing permission checking logic for user actions that combines both role-based and time-based constraints.&lt;br /&gt;
&lt;br /&gt;
=== Tasks Identified ===&lt;br /&gt;
&lt;br /&gt;
'''Modeling &amp;amp; Structural'''&lt;br /&gt;
# Refactor the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model to separate persistence, business logic, and permission logic into distinct layers.&lt;br /&gt;
# Implement reusable mixins (&amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;) to handle permission evaluation and deadline-related operations.&lt;br /&gt;
# Introduce instance-level methods (e.g., &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;) to replace class-level utilities and improve testability.&lt;br /&gt;
&lt;br /&gt;
'''Permission &amp;amp; Behavior'''&lt;br /&gt;
# Integrate role-based and time-based permission checks within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; to ensure consistent access control.&lt;br /&gt;
# Redesign the &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.&lt;br /&gt;
# Add missing deadline types (&amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt;) and remove duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries for data integrity.&lt;br /&gt;
&lt;br /&gt;
== Proposed Solution ==&lt;br /&gt;
&lt;br /&gt;
The proposed solution restructures the due date system by separating concerns across dedicated models and mixin modules. Deadline definitions (&amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt;), core deadline data (&amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;), and behavioral logic (&amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;) are isolated so each component has a single clear responsibility. This modular design improves readability, reduces coupling, and makes deadline-related behavior easier to extend and maintain.&lt;br /&gt;
&lt;br /&gt;
=== Architecture Overview ===&lt;br /&gt;
&lt;br /&gt;
[[File:Duedate-diagram.png|800px]]&lt;br /&gt;
&lt;br /&gt;
This diagram visualizes the redesigned architecture with five distinct layers:&lt;br /&gt;
&lt;br /&gt;
* '''Layer 1 (Blue)''': Parent models (Assignment, SignUpTopic) that own due dates&lt;br /&gt;
* '''Layer 2 (Orange)''': DueDateActions module providing action-level queries&lt;br /&gt;
* '''Layer 3 (Red)''': DueDate model managing core deadline data&lt;br /&gt;
* '''Layer 4 (Orange)''': DueDatePermissions module evaluating permissions&lt;br /&gt;
* '''Layer 5 (Green)''': Reference data models (DeadlineType, DeadlineRight, Participant)&lt;br /&gt;
&lt;br /&gt;
The flow works as follows: Parent models include DueDateActions, which queries DueDate objects. Each DueDate includes DueDatePermissions to evaluate whether actions are allowed based on DeadlineRight states and Participant role flags.&lt;br /&gt;
&lt;br /&gt;
=== 1. DeadlineType Model ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Although the current codebase includes a &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model, it only serves as a passive lookup table. In practice, most parts of the system still rely on hard-coded numeric IDs or manual lookups:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Example of current usage&lt;br /&gt;
deadline_type_id = DeadlineType.find_by(name: 'review').id&lt;br /&gt;
if due_date.deadline_type_id == deadline_type_id&lt;br /&gt;
  # Perform review-related logic&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This leads to several structural issues:&lt;br /&gt;
* Inconsistent references (some use IDs, others strings)&lt;br /&gt;
* Tight coupling between &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
* Lack of semantic helper methods (e.g., &amp;lt;code&amp;gt;review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;submission?&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Data duplication and integrity risks (two &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; rows)&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; Table Structure&lt;br /&gt;
|- &lt;br /&gt;
! Column !! Type !! Description&lt;br /&gt;
|-&lt;br /&gt;
| id || BIGINT || Primary key, referenced by &amp;lt;code&amp;gt;due_dates.deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| name || VARCHAR(255) || Unique symbolic name (e.g., &amp;lt;code&amp;gt;submission&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| description || TEXT || Human-readable explanation of the deadline category&lt;br /&gt;
|-&lt;br /&gt;
| created_at || TIMESTAMP || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| updated_at || TIMESTAMP || Last update timestamp&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Deadline Type Definitions ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Canonical Deadline Type Entries&lt;br /&gt;
|-&lt;br /&gt;
! ID !! Name !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 1 || submission || Student submission deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 2 || review || Peer review deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 3 || teammate_review || Team member evaluation deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 5 || metareview || Meta-review deadlines (backward compatibility)&lt;br /&gt;
|-&lt;br /&gt;
| 6 || drop_topic || Topic drop deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 7 || signup || Topic signup deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 8 || team_formation || Team formation deadlines (duplicate cleaned)&lt;br /&gt;
|-&lt;br /&gt;
| 11 || quiz || Quiz completion deadlines&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== 2. DeadlineRight Model ===&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model represents the permission level for a specific action at a given deadline:&lt;br /&gt;
&lt;br /&gt;
* '''OK''': Action is allowed without penalty&lt;br /&gt;
* '''Late''': Action is allowed but marked as late&lt;br /&gt;
* '''No''': Action is not permitted&lt;br /&gt;
&lt;br /&gt;
These values are referenced by &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; through fields such as &amp;lt;code&amp;gt;submission_allowed_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review_allowed_id&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;quiz_allowed_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_rights&amp;lt;/code&amp;gt; Table Structure&lt;br /&gt;
|-&lt;br /&gt;
! Column !! Type !! Description&lt;br /&gt;
|-&lt;br /&gt;
| id || BIGINT || Primary key, referenced by &amp;lt;code&amp;gt;due_dates.*_allowed_id&amp;lt;/code&amp;gt; fields&lt;br /&gt;
|-&lt;br /&gt;
| name || VARCHAR(255) || Unique permission name (&amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| created_at || TIMESTAMP || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| updated_at || TIMESTAMP || Last update timestamp&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== 3. DueDate Model Refactoring ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model currently mixes persistence logic, deadline calculation, and permission checking, violating the Single Responsibility Principle. Most of its logic is implemented as class methods, which makes testing and extension difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Old design: class method that mixes concerns&lt;br /&gt;
def self.copy(old_assignment_id, new_assignment_id)&lt;br /&gt;
  duedates = where(parent_id: old_assignment_id)&lt;br /&gt;
  duedates.each do |orig_due_date|&lt;br /&gt;
    new_due_date = orig_due_date.dup&lt;br /&gt;
    new_due_date.parent_id = new_assignment_id&lt;br /&gt;
    new_due_date.save&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Improved Design ====&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model is focused on representing a single deadline entry. Behavior has been extracted into appropriate modules:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Responsibility Distribution After Refactoring&lt;br /&gt;
|-&lt;br /&gt;
! Concern !! Where It Lives&lt;br /&gt;
|-&lt;br /&gt;
| Permission checking || &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module (included in DueDate)&lt;br /&gt;
|-&lt;br /&gt;
| Deadline-related actions || &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module (included in parent models)&lt;br /&gt;
|-&lt;br /&gt;
| Deadline type semantics || &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model&lt;br /&gt;
|-&lt;br /&gt;
| Core deadline data || &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Key improvements:&lt;br /&gt;
* '''Instance-based behavior''': Replaced class methods with instance methods&lt;br /&gt;
* '''Clear query methods''': Simple predicates like &amp;lt;code&amp;gt;upcoming?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;overdue?&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Scopes for common queries''': &amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 4. DueDatePermissions Module ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Currently, permission checks are split into two disconnected layers:&lt;br /&gt;
&lt;br /&gt;
'''Role-based permissions''' (in participant_helpers.rb):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def participant_permissions(authorization)&lt;br /&gt;
  case authorization&lt;br /&gt;
  when 'reader' then { can_submit: false, can_review: true, can_take_quiz: true }&lt;br /&gt;
  # ...&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Deadline-based checks''' (in Assignment model):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def submission_allowed(topic_id = nil)&lt;br /&gt;
  check_condition('submission_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These two layers never interact, leading to inconsistencies.&lt;br /&gt;
&lt;br /&gt;
==== Proposed Solution ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module integrates both layers:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Permission Methods&lt;br /&gt;
|-&lt;br /&gt;
! Method !! What It Checks&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_submit?(participant)&amp;lt;/code&amp;gt; || Submission deadline is OK/Late AND participant can submit&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_review?(participant)&amp;lt;/code&amp;gt; || Review deadline is OK/Late AND participant can review&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_take_quiz?(participant)&amp;lt;/code&amp;gt; || Quiz deadline is OK/Late AND participant can take quiz&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_review_teammate?(participant)&amp;lt;/code&amp;gt; || Teammate review deadline is OK/Late AND participant can review&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;activity_permissible?(activity)&amp;lt;/code&amp;gt; || Generic checker for any activity&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;permission_status_for(action)&amp;lt;/code&amp;gt; || Returns 'OK', 'Late', or 'No' for display&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== 5. DueDateActions Module ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module provides a unified interface for models that own due dates (&amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt;). This centralizes the logic for querying upcoming deadlines and determining whether specific activities are currently allowed.&lt;br /&gt;
&lt;br /&gt;
==== Proposed Solution ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Methods in DueDateActions&lt;br /&gt;
|-&lt;br /&gt;
! Method !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;activity_permissible?(activity)&amp;lt;/code&amp;gt; || Checks if an activity is permitted at the current time&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;submission_permissible?&amp;lt;/code&amp;gt; || Convenience wrapper for submission checking&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;review_permissible?&amp;lt;/code&amp;gt; || Convenience wrapper for review checking&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;next_due_date(topic_id)&amp;lt;/code&amp;gt; || Resolves the next applicable deadline&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;current_stage&amp;lt;/code&amp;gt; || Returns human-readable stage name (e.g., &amp;quot;review&amp;quot;, &amp;quot;submission&amp;quot;)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;has_topic_specific_deadlines?&amp;lt;/code&amp;gt; || Determines if assignment uses topic-specific deadlines&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;copy_due_dates_to(new_parent)&amp;lt;/code&amp;gt; || Utility for cloning due dates to another assignment&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Deadline Resolution Flow ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ How next_due_date Works&lt;br /&gt;
|-&lt;br /&gt;
! Step !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 1. Topic-specific check || If topic ID provided and assignment uses staggered deadlines, check topic-level deadlines first&lt;br /&gt;
|-&lt;br /&gt;
| 2. Assignment-level fallback || If no topic deadline exists, use nearest assignment-level deadline&lt;br /&gt;
|-&lt;br /&gt;
| 3. Permission evaluation || Delegate to DueDatePermissions to determine if action is allowed&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Implementation Summary ==&lt;br /&gt;
&lt;br /&gt;
=== Models Created/Modified ===&lt;br /&gt;
&lt;br /&gt;
* '''DeadlineType''' — Canonical deadline type definitions with semantic helper methods&lt;br /&gt;
* '''DeadlineRight''' — Permission state model (OK/Late/No) with comparison methods&lt;br /&gt;
* '''DueDate''' — Refactored with instance methods, scopes, and permission checking&lt;br /&gt;
* '''TopicDueDate''' — STI subclass for topic-specific deadlines&lt;br /&gt;
* '''Assignment''' — Includes DueDateActions mixin&lt;br /&gt;
* '''SignUpTopic''' — Includes DueDateActions mixin&lt;br /&gt;
&lt;br /&gt;
=== Modules Created ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions''' — Permission checking combining deadline and role constraints&lt;br /&gt;
* '''DueDateActions''' — Deadline-related operations for parent models&lt;br /&gt;
&lt;br /&gt;
=== Key Improvements ===&lt;br /&gt;
&lt;br /&gt;
# '''Separation of Concerns''': Permission logic separated into dedicated modules&lt;br /&gt;
# '''Instance Methods''': Replaced class methods with testable instance methods&lt;br /&gt;
# '''Polymorphic Design''': Single DueDate model serves both Assignment and SignUpTopic&lt;br /&gt;
# '''Unified Permissions''': Role-based and time-based checks combined in one interface&lt;br /&gt;
# '''Data Integrity''': Removed duplicate deadline types, enforced foreign key constraints&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
The test suite includes comprehensive RSpec tests for the DueDate model using nested contexts, edge case coverage, and error handling.&lt;br /&gt;
&lt;br /&gt;
=== Current Coverage ===&lt;br /&gt;
&lt;br /&gt;
* '''Validations''': Required fields (parent, due_at, deadline_type_id), round validation, error messages&lt;br /&gt;
* '''Scopes''': &amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt; with edge cases&lt;br /&gt;
* '''Instance Methods''': &amp;lt;code&amp;gt;#overdue?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#upcoming?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#set&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#copy&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#deadline_type_name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#last_deadline?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#&amp;lt;=&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Class Methods''': &amp;lt;code&amp;gt;.sort_due_dates&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.any_future_due_dates?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.copy&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Callbacks''': &amp;lt;code&amp;gt;before_save :set_default_round&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
Tests follow RSpec best practices with nested contexts:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
describe 'validations' do&lt;br /&gt;
  context 'when parent is missing' do&lt;br /&gt;
    it 'is invalid' do&lt;br /&gt;
      expect(due_date).to be_invalid&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'has error on parent field' do&lt;br /&gt;
      due_date.valid?&lt;br /&gt;
      expect(due_date.errors[:parent]).to include('must exist')&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when round validation' do&lt;br /&gt;
    context 'with round value of 0' do&lt;br /&gt;
      it 'is invalid' do&lt;br /&gt;
        expect(due_date).to be_invalid&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'with nil round' do&lt;br /&gt;
      it 'sets default round to 1' do&lt;br /&gt;
        expect(due_date.round).to eq(1)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Planned Future Tests ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions module''': Test &amp;lt;code&amp;gt;can_submit?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;can_review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;can_take_quiz?&amp;lt;/code&amp;gt; with participant parameters&lt;br /&gt;
* '''DueDateActions module''': Test &amp;lt;code&amp;gt;activity_permissible?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt; in Assignment context&lt;br /&gt;
* '''Integration tests''': Combined role-based and time-based permission logic&lt;br /&gt;
&lt;br /&gt;
=== Test Metrics ===&lt;br /&gt;
&lt;br /&gt;
* '''Total examples''': 438 tests&lt;br /&gt;
* '''DueDate model''': 60+ examples&lt;br /&gt;
* '''Code coverage''': 100% of DueDate methods&lt;br /&gt;
* '''Execution time''': ~12 seconds&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
=== Demo Link ===&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add deadline notification system using the new permission framework&lt;br /&gt;
* Implement deadline templates for common assignment patterns&lt;br /&gt;
* Add API endpoints for mobile app integration&lt;br /&gt;
* Create admin dashboard for monitoring deadline compliance across courses&lt;br /&gt;
* Add automated deadline extension workflows based on configurable rules&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167220</id>
		<title>CSC/ECE 517 Fall 2025 - E2566. Finish DueDates</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167220"/>
		<updated>2025-12-02T15:35:31Z</updated>

		<summary type="html">&lt;p&gt;Skim253: /* Deadline Resolution Flow */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
This project aims to complete the implementation of the DueDate system in Expertiza. The current implementation consists mostly of class methods and lacks proper definition of different deadline types and permission checking functionality. This redesign will create a more robust, readable, and maintainable due date system.&lt;br /&gt;
&lt;br /&gt;
=== Existing Components ===&lt;br /&gt;
* &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model with polymorphic parent association (Assignment/SignUpTopic)&lt;br /&gt;
* &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; subclasses&lt;br /&gt;
* Basic CRUD operations (within &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AssignmentForm&amp;lt;/code&amp;gt;) and sorting functionality (&amp;lt;code&amp;gt;deadline_sort&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Database schema with &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt; field&lt;br /&gt;
&lt;br /&gt;
=== Current Behavior ===&lt;br /&gt;
&lt;br /&gt;
====Admin's View====&lt;br /&gt;
[[File:Duedates.png|800px]]&lt;br /&gt;
&lt;br /&gt;
When editing an assignment, due dates can be modified under the Due Dates tab. Each row contains the following columns: &amp;lt;code&amp;gt;Date &amp;amp; Time&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Use Date Updater&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Submission Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Teammate Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Meta-Review Allowed&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Reminder&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:Assignments-CurrentStage.png|800px]]&lt;br /&gt;
&lt;br /&gt;
In the Assignments tab, the current stage and stage deadline of each assignment is displayed.&lt;br /&gt;
&lt;br /&gt;
[[File:Due-date-topic-instructor.png|800px]]&lt;br /&gt;
&lt;br /&gt;
[[File:Due-date-topic-instructor-2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
In the Topics subtab, due dates for each topic are displayed.&lt;br /&gt;
&lt;br /&gt;
====Student's View====&lt;br /&gt;
[[File:Student-assignments.png|800px]]&lt;br /&gt;
&lt;br /&gt;
Students can view due date information under &amp;lt;code&amp;gt;Current State&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Stage Deadline&amp;lt;/code&amp;gt;. The actions available to students are determined by the current DueDate status.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-not-over.png|800px]]&lt;br /&gt;
&lt;br /&gt;
During the submission period, students can access the &amp;lt;code&amp;gt;Submit a hyperlink&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Submit a file&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-over.png|800px]]&lt;br /&gt;
&lt;br /&gt;
Once the due date has passed, students cannot submit either a hyperlink or a file.&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
&lt;br /&gt;
Currently, there are some glaring issues with how due_dates was created in the first place, including redundancy and lack of modularity.&lt;br /&gt;
&lt;br /&gt;
'''Modeling &amp;amp; Structural Issues'''&lt;br /&gt;
# No dedicated &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to define different kinds of deadlines. The existing &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; class only serves as an association for &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; models, and is never referenced in the current &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; implementation.&lt;br /&gt;
# Duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table, which may lead to potential bugs.&lt;br /&gt;
# Incomplete deadline type definitions — &amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt; need to be added.&lt;br /&gt;
# No clear separation of concerns — data persistence, business logic, and permission logic are mixed in a single model.&lt;br /&gt;
&lt;br /&gt;
'''Permission &amp;amp; Behavior Issues'''&lt;br /&gt;
# Overuse of class methods making the code difficult to maintain.&lt;br /&gt;
# Missing permission checking logic for user actions that combines both role-based and time-based constraints.&lt;br /&gt;
&lt;br /&gt;
=== Tasks Identified ===&lt;br /&gt;
&lt;br /&gt;
'''Modeling &amp;amp; Structural'''&lt;br /&gt;
# Refactor the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model to separate persistence, business logic, and permission logic into distinct layers.&lt;br /&gt;
# Implement reusable mixins (&amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;) to handle permission evaluation and deadline-related operations.&lt;br /&gt;
# Introduce instance-level methods (e.g., &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;) to replace class-level utilities and improve testability.&lt;br /&gt;
&lt;br /&gt;
'''Permission &amp;amp; Behavior'''&lt;br /&gt;
# Integrate role-based and time-based permission checks within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; to ensure consistent access control.&lt;br /&gt;
# Redesign the &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.&lt;br /&gt;
# Add missing deadline types (&amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt;) and remove duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries for data integrity.&lt;br /&gt;
&lt;br /&gt;
== Proposed Solution ==&lt;br /&gt;
&lt;br /&gt;
The proposed solution restructures the due date system by separating concerns across dedicated models and mixin modules. Deadline definitions (&amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt;), core deadline data (&amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;), and behavioral logic (&amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;) are isolated so each component has a single clear responsibility. This modular design improves readability, reduces coupling, and makes deadline-related behavior easier to extend and maintain.&lt;br /&gt;
&lt;br /&gt;
=== Architecture Overview ===&lt;br /&gt;
&lt;br /&gt;
[[File:Duedate-diagram.png|800px]]&lt;br /&gt;
&lt;br /&gt;
This diagram visualizes the redesigned architecture with five distinct layers:&lt;br /&gt;
&lt;br /&gt;
* '''Layer 1 (Blue)''': Parent models (Assignment, SignUpTopic) that own due dates&lt;br /&gt;
* '''Layer 2 (Orange)''': DueDateActions module providing action-level queries&lt;br /&gt;
* '''Layer 3 (Red)''': DueDate model managing core deadline data&lt;br /&gt;
* '''Layer 4 (Orange)''': DueDatePermissions module evaluating permissions&lt;br /&gt;
* '''Layer 5 (Green)''': Reference data models (DeadlineType, DeadlineRight, Participant)&lt;br /&gt;
&lt;br /&gt;
The flow works as follows: Parent models include DueDateActions, which queries DueDate objects. Each DueDate includes DueDatePermissions to evaluate whether actions are allowed based on DeadlineRight states and Participant role flags.&lt;br /&gt;
&lt;br /&gt;
=== 1. DeadlineType Model ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Although the current codebase includes a &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model, it only serves as a passive lookup table. In practice, most parts of the system still rely on hard-coded numeric IDs or manual lookups:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Example of current usage&lt;br /&gt;
deadline_type_id = DeadlineType.find_by(name: 'review').id&lt;br /&gt;
if due_date.deadline_type_id == deadline_type_id&lt;br /&gt;
  # Perform review-related logic&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This leads to several structural issues:&lt;br /&gt;
* Inconsistent references (some use IDs, others strings)&lt;br /&gt;
* Tight coupling between &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
* Lack of semantic helper methods (e.g., &amp;lt;code&amp;gt;review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;submission?&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Data duplication and integrity risks (two &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; rows)&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; Table Structure&lt;br /&gt;
|- &lt;br /&gt;
! Column !! Type !! Description&lt;br /&gt;
|-&lt;br /&gt;
| id || BIGINT || Primary key, referenced by &amp;lt;code&amp;gt;due_dates.deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| name || VARCHAR(255) || Unique symbolic name (e.g., &amp;lt;code&amp;gt;submission&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| description || TEXT || Human-readable explanation of the deadline category&lt;br /&gt;
|-&lt;br /&gt;
| created_at || TIMESTAMP || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| updated_at || TIMESTAMP || Last update timestamp&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Deadline Type Definitions ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Canonical Deadline Type Entries&lt;br /&gt;
|-&lt;br /&gt;
! ID !! Name !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 1 || submission || Student submission deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 2 || review || Peer review deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 3 || teammate_review || Team member evaluation deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 5 || metareview || Meta-review deadlines (backward compatibility)&lt;br /&gt;
|-&lt;br /&gt;
| 6 || drop_topic || Topic drop deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 7 || signup || Topic signup deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 8 || team_formation || Team formation deadlines (duplicate cleaned)&lt;br /&gt;
|-&lt;br /&gt;
| 11 || quiz || Quiz completion deadlines&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== 2. DeadlineRight Model ===&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model represents the permission level for a specific action at a given deadline:&lt;br /&gt;
&lt;br /&gt;
* '''OK''': Action is allowed without penalty&lt;br /&gt;
* '''Late''': Action is allowed but marked as late&lt;br /&gt;
* '''No''': Action is not permitted&lt;br /&gt;
&lt;br /&gt;
These values are referenced by &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; through fields such as &amp;lt;code&amp;gt;submission_allowed_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review_allowed_id&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;quiz_allowed_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_rights&amp;lt;/code&amp;gt; Table Structure&lt;br /&gt;
|-&lt;br /&gt;
! Column !! Type !! Description&lt;br /&gt;
|-&lt;br /&gt;
| id || BIGINT || Primary key, referenced by &amp;lt;code&amp;gt;due_dates.*_allowed_id&amp;lt;/code&amp;gt; fields&lt;br /&gt;
|-&lt;br /&gt;
| name || VARCHAR(255) || Unique permission name (&amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| created_at || TIMESTAMP || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| updated_at || TIMESTAMP || Last update timestamp&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== 3. DueDate Model Refactoring ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model currently mixes persistence logic, deadline calculation, and permission checking, violating the Single Responsibility Principle. Most of its logic is implemented as class methods, which makes testing and extension difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Old design: class method that mixes concerns&lt;br /&gt;
def self.copy(old_assignment_id, new_assignment_id)&lt;br /&gt;
  duedates = where(parent_id: old_assignment_id)&lt;br /&gt;
  duedates.each do |orig_due_date|&lt;br /&gt;
    new_due_date = orig_due_date.dup&lt;br /&gt;
    new_due_date.parent_id = new_assignment_id&lt;br /&gt;
    new_due_date.save&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Improved Design ====&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model is focused on representing a single deadline entry. Behavior has been extracted into appropriate modules:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Responsibility Distribution After Refactoring&lt;br /&gt;
|-&lt;br /&gt;
! Concern !! Where It Lives&lt;br /&gt;
|-&lt;br /&gt;
| Permission checking || &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module (included in DueDate)&lt;br /&gt;
|-&lt;br /&gt;
| Deadline-related actions || &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module (included in parent models)&lt;br /&gt;
|-&lt;br /&gt;
| Deadline type semantics || &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model&lt;br /&gt;
|-&lt;br /&gt;
| Core deadline data || &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Key improvements:&lt;br /&gt;
* '''Instance-based behavior''': Replaced class methods with instance methods&lt;br /&gt;
* '''Clear query methods''': Simple predicates like &amp;lt;code&amp;gt;upcoming?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;overdue?&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Scopes for common queries''': &amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 4. DueDatePermissions Module ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Currently, permission checks are split into two disconnected layers:&lt;br /&gt;
&lt;br /&gt;
'''Role-based permissions''' (in participant_helpers.rb):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def participant_permissions(authorization)&lt;br /&gt;
  case authorization&lt;br /&gt;
  when 'reader' then { can_submit: false, can_review: true, can_take_quiz: true }&lt;br /&gt;
  # ...&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Deadline-based checks''' (in Assignment model):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def submission_allowed(topic_id = nil)&lt;br /&gt;
  check_condition('submission_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These two layers never interact, leading to inconsistencies.&lt;br /&gt;
&lt;br /&gt;
==== Proposed Solution ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module integrates both layers:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Permission Methods&lt;br /&gt;
|-&lt;br /&gt;
! Method !! What It Checks&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_submit?(participant)&amp;lt;/code&amp;gt; || Submission deadline is OK/Late AND participant can submit&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_review?(participant)&amp;lt;/code&amp;gt; || Review deadline is OK/Late AND participant can review&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_take_quiz?(participant)&amp;lt;/code&amp;gt; || Quiz deadline is OK/Late AND participant can take quiz&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_review_teammate?(participant)&amp;lt;/code&amp;gt; || Teammate review deadline is OK/Late AND participant can review&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;activity_permissible?(activity)&amp;lt;/code&amp;gt; || Generic checker for any activity&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;permission_status_for(action)&amp;lt;/code&amp;gt; || Returns 'OK', 'Late', or 'No' for display&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== 5. DueDateActions Module ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module provides a unified interface for models that own due dates (&amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt;). This centralizes the logic for querying upcoming deadlines and determining whether specific activities are currently allowed.&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Methods in DueDateActions&lt;br /&gt;
|-&lt;br /&gt;
! Method !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;activity_permissible?(activity)&amp;lt;/code&amp;gt; || Checks if an activity is permitted at the current time&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;submission_permissible?&amp;lt;/code&amp;gt; || Convenience wrapper for submission checking&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;review_permissible?&amp;lt;/code&amp;gt; || Convenience wrapper for review checking&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;next_due_date(topic_id)&amp;lt;/code&amp;gt; || Resolves the next applicable deadline&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;current_stage&amp;lt;/code&amp;gt; || Returns human-readable stage name (e.g., &amp;quot;review&amp;quot;, &amp;quot;submission&amp;quot;)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;has_topic_specific_deadlines?&amp;lt;/code&amp;gt; || Determines if assignment uses topic-specific deadlines&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;copy_due_dates_to(new_parent)&amp;lt;/code&amp;gt; || Utility for cloning due dates to another assignment&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Deadline Resolution Flow ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ How next_due_date Works&lt;br /&gt;
|-&lt;br /&gt;
! Step !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 1. Topic-specific check || If topic ID provided and assignment uses staggered deadlines, check topic-level deadlines first&lt;br /&gt;
|-&lt;br /&gt;
| 2. Assignment-level fallback || If no topic deadline exists, use nearest assignment-level deadline&lt;br /&gt;
|-&lt;br /&gt;
| 3. Permission evaluation || Delegate to DueDatePermissions to determine if action is allowed&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Implementation Summary ==&lt;br /&gt;
&lt;br /&gt;
=== Models Created/Modified ===&lt;br /&gt;
&lt;br /&gt;
* '''DeadlineType''' — Canonical deadline type definitions with semantic helper methods&lt;br /&gt;
* '''DeadlineRight''' — Permission state model (OK/Late/No) with comparison methods&lt;br /&gt;
* '''DueDate''' — Refactored with instance methods, scopes, and permission checking&lt;br /&gt;
* '''TopicDueDate''' — STI subclass for topic-specific deadlines&lt;br /&gt;
* '''Assignment''' — Includes DueDateActions mixin&lt;br /&gt;
* '''SignUpTopic''' — Includes DueDateActions mixin&lt;br /&gt;
&lt;br /&gt;
=== Modules Created ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions''' — Permission checking combining deadline and role constraints&lt;br /&gt;
* '''DueDateActions''' — Deadline-related operations for parent models&lt;br /&gt;
&lt;br /&gt;
=== Key Improvements ===&lt;br /&gt;
&lt;br /&gt;
# '''Separation of Concerns''': Permission logic separated into dedicated modules&lt;br /&gt;
# '''Instance Methods''': Replaced class methods with testable instance methods&lt;br /&gt;
# '''Polymorphic Design''': Single DueDate model serves both Assignment and SignUpTopic&lt;br /&gt;
# '''Unified Permissions''': Role-based and time-based checks combined in one interface&lt;br /&gt;
# '''Data Integrity''': Removed duplicate deadline types, enforced foreign key constraints&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
The test suite includes comprehensive RSpec tests for the DueDate model using nested contexts, edge case coverage, and error handling.&lt;br /&gt;
&lt;br /&gt;
=== Current Coverage ===&lt;br /&gt;
&lt;br /&gt;
* '''Validations''': Required fields (parent, due_at, deadline_type_id), round validation, error messages&lt;br /&gt;
* '''Scopes''': &amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt; with edge cases&lt;br /&gt;
* '''Instance Methods''': &amp;lt;code&amp;gt;#overdue?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#upcoming?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#set&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#copy&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#deadline_type_name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#last_deadline?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#&amp;lt;=&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Class Methods''': &amp;lt;code&amp;gt;.sort_due_dates&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.any_future_due_dates?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.copy&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Callbacks''': &amp;lt;code&amp;gt;before_save :set_default_round&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
Tests follow RSpec best practices with nested contexts:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
describe 'validations' do&lt;br /&gt;
  context 'when parent is missing' do&lt;br /&gt;
    it 'is invalid' do&lt;br /&gt;
      expect(due_date).to be_invalid&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'has error on parent field' do&lt;br /&gt;
      due_date.valid?&lt;br /&gt;
      expect(due_date.errors[:parent]).to include('must exist')&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when round validation' do&lt;br /&gt;
    context 'with round value of 0' do&lt;br /&gt;
      it 'is invalid' do&lt;br /&gt;
        expect(due_date).to be_invalid&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'with nil round' do&lt;br /&gt;
      it 'sets default round to 1' do&lt;br /&gt;
        expect(due_date.round).to eq(1)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Planned Future Tests ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions module''': Test &amp;lt;code&amp;gt;can_submit?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;can_review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;can_take_quiz?&amp;lt;/code&amp;gt; with participant parameters&lt;br /&gt;
* '''DueDateActions module''': Test &amp;lt;code&amp;gt;activity_permissible?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt; in Assignment context&lt;br /&gt;
* '''Integration tests''': Combined role-based and time-based permission logic&lt;br /&gt;
&lt;br /&gt;
=== Test Metrics ===&lt;br /&gt;
&lt;br /&gt;
* '''Total examples''': 438 tests&lt;br /&gt;
* '''DueDate model''': 60+ examples&lt;br /&gt;
* '''Code coverage''': 100% of DueDate methods&lt;br /&gt;
* '''Execution time''': ~12 seconds&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
=== Demo Link ===&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add deadline notification system using the new permission framework&lt;br /&gt;
* Implement deadline templates for common assignment patterns&lt;br /&gt;
* Add API endpoints for mobile app integration&lt;br /&gt;
* Create admin dashboard for monitoring deadline compliance across courses&lt;br /&gt;
* Add automated deadline extension workflows based on configurable rules&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167219</id>
		<title>CSC/ECE 517 Fall 2025 - E2566. Finish DueDates</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167219"/>
		<updated>2025-12-02T15:34:53Z</updated>

		<summary type="html">&lt;p&gt;Skim253: /* Proposed Solution */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
This project aims to complete the implementation of the DueDate system in Expertiza. The current implementation consists mostly of class methods and lacks proper definition of different deadline types and permission checking functionality. This redesign will create a more robust, readable, and maintainable due date system.&lt;br /&gt;
&lt;br /&gt;
=== Existing Components ===&lt;br /&gt;
* &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model with polymorphic parent association (Assignment/SignUpTopic)&lt;br /&gt;
* &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; subclasses&lt;br /&gt;
* Basic CRUD operations (within &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AssignmentForm&amp;lt;/code&amp;gt;) and sorting functionality (&amp;lt;code&amp;gt;deadline_sort&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Database schema with &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt; field&lt;br /&gt;
&lt;br /&gt;
=== Current Behavior ===&lt;br /&gt;
&lt;br /&gt;
====Admin's View====&lt;br /&gt;
[[File:Duedates.png|800px]]&lt;br /&gt;
&lt;br /&gt;
When editing an assignment, due dates can be modified under the Due Dates tab. Each row contains the following columns: &amp;lt;code&amp;gt;Date &amp;amp; Time&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Use Date Updater&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Submission Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Teammate Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Meta-Review Allowed&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Reminder&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:Assignments-CurrentStage.png|800px]]&lt;br /&gt;
&lt;br /&gt;
In the Assignments tab, the current stage and stage deadline of each assignment is displayed.&lt;br /&gt;
&lt;br /&gt;
[[File:Due-date-topic-instructor.png|800px]]&lt;br /&gt;
&lt;br /&gt;
[[File:Due-date-topic-instructor-2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
In the Topics subtab, due dates for each topic are displayed.&lt;br /&gt;
&lt;br /&gt;
====Student's View====&lt;br /&gt;
[[File:Student-assignments.png|800px]]&lt;br /&gt;
&lt;br /&gt;
Students can view due date information under &amp;lt;code&amp;gt;Current State&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Stage Deadline&amp;lt;/code&amp;gt;. The actions available to students are determined by the current DueDate status.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-not-over.png|800px]]&lt;br /&gt;
&lt;br /&gt;
During the submission period, students can access the &amp;lt;code&amp;gt;Submit a hyperlink&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Submit a file&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-over.png|800px]]&lt;br /&gt;
&lt;br /&gt;
Once the due date has passed, students cannot submit either a hyperlink or a file.&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
&lt;br /&gt;
Currently, there are some glaring issues with how due_dates was created in the first place, including redundancy and lack of modularity.&lt;br /&gt;
&lt;br /&gt;
'''Modeling &amp;amp; Structural Issues'''&lt;br /&gt;
# No dedicated &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to define different kinds of deadlines. The existing &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; class only serves as an association for &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; models, and is never referenced in the current &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; implementation.&lt;br /&gt;
# Duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table, which may lead to potential bugs.&lt;br /&gt;
# Incomplete deadline type definitions — &amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt; need to be added.&lt;br /&gt;
# No clear separation of concerns — data persistence, business logic, and permission logic are mixed in a single model.&lt;br /&gt;
&lt;br /&gt;
'''Permission &amp;amp; Behavior Issues'''&lt;br /&gt;
# Overuse of class methods making the code difficult to maintain.&lt;br /&gt;
# Missing permission checking logic for user actions that combines both role-based and time-based constraints.&lt;br /&gt;
&lt;br /&gt;
=== Tasks Identified ===&lt;br /&gt;
&lt;br /&gt;
'''Modeling &amp;amp; Structural'''&lt;br /&gt;
# Refactor the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model to separate persistence, business logic, and permission logic into distinct layers.&lt;br /&gt;
# Implement reusable mixins (&amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;) to handle permission evaluation and deadline-related operations.&lt;br /&gt;
# Introduce instance-level methods (e.g., &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;) to replace class-level utilities and improve testability.&lt;br /&gt;
&lt;br /&gt;
'''Permission &amp;amp; Behavior'''&lt;br /&gt;
# Integrate role-based and time-based permission checks within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; to ensure consistent access control.&lt;br /&gt;
# Redesign the &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.&lt;br /&gt;
# Add missing deadline types (&amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt;) and remove duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries for data integrity.&lt;br /&gt;
&lt;br /&gt;
== Proposed Solution ==&lt;br /&gt;
&lt;br /&gt;
The proposed solution restructures the due date system by separating concerns across dedicated models and mixin modules. Deadline definitions (&amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt;), core deadline data (&amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;), and behavioral logic (&amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;) are isolated so each component has a single clear responsibility. This modular design improves readability, reduces coupling, and makes deadline-related behavior easier to extend and maintain.&lt;br /&gt;
&lt;br /&gt;
=== Architecture Overview ===&lt;br /&gt;
&lt;br /&gt;
[[File:Duedate-diagram.png|800px]]&lt;br /&gt;
&lt;br /&gt;
This diagram visualizes the redesigned architecture with five distinct layers:&lt;br /&gt;
&lt;br /&gt;
* '''Layer 1 (Blue)''': Parent models (Assignment, SignUpTopic) that own due dates&lt;br /&gt;
* '''Layer 2 (Orange)''': DueDateActions module providing action-level queries&lt;br /&gt;
* '''Layer 3 (Red)''': DueDate model managing core deadline data&lt;br /&gt;
* '''Layer 4 (Orange)''': DueDatePermissions module evaluating permissions&lt;br /&gt;
* '''Layer 5 (Green)''': Reference data models (DeadlineType, DeadlineRight, Participant)&lt;br /&gt;
&lt;br /&gt;
The flow works as follows: Parent models include DueDateActions, which queries DueDate objects. Each DueDate includes DueDatePermissions to evaluate whether actions are allowed based on DeadlineRight states and Participant role flags.&lt;br /&gt;
&lt;br /&gt;
=== 1. DeadlineType Model ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Although the current codebase includes a &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model, it only serves as a passive lookup table. In practice, most parts of the system still rely on hard-coded numeric IDs or manual lookups:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Example of current usage&lt;br /&gt;
deadline_type_id = DeadlineType.find_by(name: 'review').id&lt;br /&gt;
if due_date.deadline_type_id == deadline_type_id&lt;br /&gt;
  # Perform review-related logic&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This leads to several structural issues:&lt;br /&gt;
* Inconsistent references (some use IDs, others strings)&lt;br /&gt;
* Tight coupling between &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
* Lack of semantic helper methods (e.g., &amp;lt;code&amp;gt;review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;submission?&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Data duplication and integrity risks (two &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; rows)&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; Table Structure&lt;br /&gt;
|- &lt;br /&gt;
! Column !! Type !! Description&lt;br /&gt;
|-&lt;br /&gt;
| id || BIGINT || Primary key, referenced by &amp;lt;code&amp;gt;due_dates.deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| name || VARCHAR(255) || Unique symbolic name (e.g., &amp;lt;code&amp;gt;submission&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| description || TEXT || Human-readable explanation of the deadline category&lt;br /&gt;
|-&lt;br /&gt;
| created_at || TIMESTAMP || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| updated_at || TIMESTAMP || Last update timestamp&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Deadline Type Definitions ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Canonical Deadline Type Entries&lt;br /&gt;
|-&lt;br /&gt;
! ID !! Name !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 1 || submission || Student submission deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 2 || review || Peer review deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 3 || teammate_review || Team member evaluation deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 5 || metareview || Meta-review deadlines (backward compatibility)&lt;br /&gt;
|-&lt;br /&gt;
| 6 || drop_topic || Topic drop deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 7 || signup || Topic signup deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 8 || team_formation || Team formation deadlines (duplicate cleaned)&lt;br /&gt;
|-&lt;br /&gt;
| 11 || quiz || Quiz completion deadlines&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== 2. DeadlineRight Model ===&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model represents the permission level for a specific action at a given deadline:&lt;br /&gt;
&lt;br /&gt;
* '''OK''': Action is allowed without penalty&lt;br /&gt;
* '''Late''': Action is allowed but marked as late&lt;br /&gt;
* '''No''': Action is not permitted&lt;br /&gt;
&lt;br /&gt;
These values are referenced by &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; through fields such as &amp;lt;code&amp;gt;submission_allowed_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review_allowed_id&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;quiz_allowed_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_rights&amp;lt;/code&amp;gt; Table Structure&lt;br /&gt;
|-&lt;br /&gt;
! Column !! Type !! Description&lt;br /&gt;
|-&lt;br /&gt;
| id || BIGINT || Primary key, referenced by &amp;lt;code&amp;gt;due_dates.*_allowed_id&amp;lt;/code&amp;gt; fields&lt;br /&gt;
|-&lt;br /&gt;
| name || VARCHAR(255) || Unique permission name (&amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| created_at || TIMESTAMP || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| updated_at || TIMESTAMP || Last update timestamp&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== 3. DueDate Model Refactoring ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model currently mixes persistence logic, deadline calculation, and permission checking, violating the Single Responsibility Principle. Most of its logic is implemented as class methods, which makes testing and extension difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Old design: class method that mixes concerns&lt;br /&gt;
def self.copy(old_assignment_id, new_assignment_id)&lt;br /&gt;
  duedates = where(parent_id: old_assignment_id)&lt;br /&gt;
  duedates.each do |orig_due_date|&lt;br /&gt;
    new_due_date = orig_due_date.dup&lt;br /&gt;
    new_due_date.parent_id = new_assignment_id&lt;br /&gt;
    new_due_date.save&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Improved Design ====&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model is focused on representing a single deadline entry. Behavior has been extracted into appropriate modules:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Responsibility Distribution After Refactoring&lt;br /&gt;
|-&lt;br /&gt;
! Concern !! Where It Lives&lt;br /&gt;
|-&lt;br /&gt;
| Permission checking || &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module (included in DueDate)&lt;br /&gt;
|-&lt;br /&gt;
| Deadline-related actions || &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module (included in parent models)&lt;br /&gt;
|-&lt;br /&gt;
| Deadline type semantics || &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model&lt;br /&gt;
|-&lt;br /&gt;
| Core deadline data || &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Key improvements:&lt;br /&gt;
* '''Instance-based behavior''': Replaced class methods with instance methods&lt;br /&gt;
* '''Clear query methods''': Simple predicates like &amp;lt;code&amp;gt;upcoming?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;overdue?&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Scopes for common queries''': &amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 4. DueDatePermissions Module ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Currently, permission checks are split into two disconnected layers:&lt;br /&gt;
&lt;br /&gt;
'''Role-based permissions''' (in participant_helpers.rb):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def participant_permissions(authorization)&lt;br /&gt;
  case authorization&lt;br /&gt;
  when 'reader' then { can_submit: false, can_review: true, can_take_quiz: true }&lt;br /&gt;
  # ...&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Deadline-based checks''' (in Assignment model):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def submission_allowed(topic_id = nil)&lt;br /&gt;
  check_condition('submission_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These two layers never interact, leading to inconsistencies.&lt;br /&gt;
&lt;br /&gt;
==== Proposed Solution ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module integrates both layers:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Permission Methods&lt;br /&gt;
|-&lt;br /&gt;
! Method !! What It Checks&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_submit?(participant)&amp;lt;/code&amp;gt; || Submission deadline is OK/Late AND participant can submit&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_review?(participant)&amp;lt;/code&amp;gt; || Review deadline is OK/Late AND participant can review&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_take_quiz?(participant)&amp;lt;/code&amp;gt; || Quiz deadline is OK/Late AND participant can take quiz&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_review_teammate?(participant)&amp;lt;/code&amp;gt; || Teammate review deadline is OK/Late AND participant can review&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;activity_permissible?(activity)&amp;lt;/code&amp;gt; || Generic checker for any activity&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;permission_status_for(action)&amp;lt;/code&amp;gt; || Returns 'OK', 'Late', or 'No' for display&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== 5. DueDateActions Module ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module provides a unified interface for models that own due dates (&amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt;). This centralizes the logic for querying upcoming deadlines and determining whether specific activities are currently allowed.&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Methods in DueDateActions&lt;br /&gt;
|-&lt;br /&gt;
! Method !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;activity_permissible?(activity)&amp;lt;/code&amp;gt; || Checks if an activity is permitted at the current time&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;submission_permissible?&amp;lt;/code&amp;gt; || Convenience wrapper for submission checking&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;review_permissible?&amp;lt;/code&amp;gt; || Convenience wrapper for review checking&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;next_due_date(topic_id)&amp;lt;/code&amp;gt; || Resolves the next applicable deadline&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;current_stage&amp;lt;/code&amp;gt; || Returns human-readable stage name (e.g., &amp;quot;review&amp;quot;, &amp;quot;submission&amp;quot;)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;has_topic_specific_deadlines?&amp;lt;/code&amp;gt; || Determines if assignment uses topic-specific deadlines&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;copy_due_dates_to(new_parent)&amp;lt;/code&amp;gt; || Utility for cloning due dates to another assignment&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Deadline Resolution Flow ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ How next_due_date Works&lt;br /&gt;
|-&lt;br /&gt;
! Step !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 1. Topic-specific check || If topic ID provided and assignment uses staggered deadlines, check topic-level deadlines first&lt;br /&gt;
|-&lt;br /&gt;
| 2. Assignment-level fallback || If no topic deadline exists, use nearest assignment-level deadline&lt;br /&gt;
|-&lt;br /&gt;
| 3. Permission evaluation || Delegate to DueDatePermissions to determine if action is allowed&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDateActions&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    current_deadline = next_due_date()&lt;br /&gt;
    return false unless current_deadline&lt;br /&gt;
    current_deadline.activity_permissible?(activity)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def submission_permissible?&lt;br /&gt;
    activity_permissible?(:submission)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def next_due_date(topic_id = nil)&lt;br /&gt;
    if topic_id &amp;amp;&amp;amp; has_topic_specific_deadlines?&lt;br /&gt;
      topic_deadline = due_dates.where(parent_id: topic_id, parent_type: 'SignUpTopic')&lt;br /&gt;
                                .upcoming.first&lt;br /&gt;
      return topic_deadline if topic_deadline&lt;br /&gt;
    end&lt;br /&gt;
    due_dates.upcoming.first&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Implementation Summary ==&lt;br /&gt;
&lt;br /&gt;
=== Models Created/Modified ===&lt;br /&gt;
&lt;br /&gt;
* '''DeadlineType''' — Canonical deadline type definitions with semantic helper methods&lt;br /&gt;
* '''DeadlineRight''' — Permission state model (OK/Late/No) with comparison methods&lt;br /&gt;
* '''DueDate''' — Refactored with instance methods, scopes, and permission checking&lt;br /&gt;
* '''TopicDueDate''' — STI subclass for topic-specific deadlines&lt;br /&gt;
* '''Assignment''' — Includes DueDateActions mixin&lt;br /&gt;
* '''SignUpTopic''' — Includes DueDateActions mixin&lt;br /&gt;
&lt;br /&gt;
=== Modules Created ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions''' — Permission checking combining deadline and role constraints&lt;br /&gt;
* '''DueDateActions''' — Deadline-related operations for parent models&lt;br /&gt;
&lt;br /&gt;
=== Key Improvements ===&lt;br /&gt;
&lt;br /&gt;
# '''Separation of Concerns''': Permission logic separated into dedicated modules&lt;br /&gt;
# '''Instance Methods''': Replaced class methods with testable instance methods&lt;br /&gt;
# '''Polymorphic Design''': Single DueDate model serves both Assignment and SignUpTopic&lt;br /&gt;
# '''Unified Permissions''': Role-based and time-based checks combined in one interface&lt;br /&gt;
# '''Data Integrity''': Removed duplicate deadline types, enforced foreign key constraints&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
The test suite includes comprehensive RSpec tests for the DueDate model using nested contexts, edge case coverage, and error handling.&lt;br /&gt;
&lt;br /&gt;
=== Current Coverage ===&lt;br /&gt;
&lt;br /&gt;
* '''Validations''': Required fields (parent, due_at, deadline_type_id), round validation, error messages&lt;br /&gt;
* '''Scopes''': &amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt; with edge cases&lt;br /&gt;
* '''Instance Methods''': &amp;lt;code&amp;gt;#overdue?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#upcoming?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#set&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#copy&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#deadline_type_name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#last_deadline?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#&amp;lt;=&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Class Methods''': &amp;lt;code&amp;gt;.sort_due_dates&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.any_future_due_dates?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.copy&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Callbacks''': &amp;lt;code&amp;gt;before_save :set_default_round&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
Tests follow RSpec best practices with nested contexts:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
describe 'validations' do&lt;br /&gt;
  context 'when parent is missing' do&lt;br /&gt;
    it 'is invalid' do&lt;br /&gt;
      expect(due_date).to be_invalid&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'has error on parent field' do&lt;br /&gt;
      due_date.valid?&lt;br /&gt;
      expect(due_date.errors[:parent]).to include('must exist')&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when round validation' do&lt;br /&gt;
    context 'with round value of 0' do&lt;br /&gt;
      it 'is invalid' do&lt;br /&gt;
        expect(due_date).to be_invalid&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'with nil round' do&lt;br /&gt;
      it 'sets default round to 1' do&lt;br /&gt;
        expect(due_date.round).to eq(1)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Planned Future Tests ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions module''': Test &amp;lt;code&amp;gt;can_submit?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;can_review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;can_take_quiz?&amp;lt;/code&amp;gt; with participant parameters&lt;br /&gt;
* '''DueDateActions module''': Test &amp;lt;code&amp;gt;activity_permissible?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt; in Assignment context&lt;br /&gt;
* '''Integration tests''': Combined role-based and time-based permission logic&lt;br /&gt;
&lt;br /&gt;
=== Test Metrics ===&lt;br /&gt;
&lt;br /&gt;
* '''Total examples''': 438 tests&lt;br /&gt;
* '''DueDate model''': 60+ examples&lt;br /&gt;
* '''Code coverage''': 100% of DueDate methods&lt;br /&gt;
* '''Execution time''': ~12 seconds&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
=== Demo Link ===&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add deadline notification system using the new permission framework&lt;br /&gt;
* Implement deadline templates for common assignment patterns&lt;br /&gt;
* Add API endpoints for mobile app integration&lt;br /&gt;
* Create admin dashboard for monitoring deadline compliance across courses&lt;br /&gt;
* Add automated deadline extension workflows based on configurable rules&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167218</id>
		<title>CSC/ECE 517 Fall 2025 - E2566. Finish DueDates</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167218"/>
		<updated>2025-12-02T15:34:36Z</updated>

		<summary type="html">&lt;p&gt;Skim253: /* Improved Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
This project aims to complete the implementation of the DueDate system in Expertiza. The current implementation consists mostly of class methods and lacks proper definition of different deadline types and permission checking functionality. This redesign will create a more robust, readable, and maintainable due date system.&lt;br /&gt;
&lt;br /&gt;
=== Existing Components ===&lt;br /&gt;
* &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model with polymorphic parent association (Assignment/SignUpTopic)&lt;br /&gt;
* &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; subclasses&lt;br /&gt;
* Basic CRUD operations (within &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AssignmentForm&amp;lt;/code&amp;gt;) and sorting functionality (&amp;lt;code&amp;gt;deadline_sort&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Database schema with &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt; field&lt;br /&gt;
&lt;br /&gt;
=== Current Behavior ===&lt;br /&gt;
&lt;br /&gt;
====Admin's View====&lt;br /&gt;
[[File:Duedates.png|800px]]&lt;br /&gt;
&lt;br /&gt;
When editing an assignment, due dates can be modified under the Due Dates tab. Each row contains the following columns: &amp;lt;code&amp;gt;Date &amp;amp; Time&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Use Date Updater&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Submission Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Teammate Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Meta-Review Allowed&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Reminder&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:Assignments-CurrentStage.png|800px]]&lt;br /&gt;
&lt;br /&gt;
In the Assignments tab, the current stage and stage deadline of each assignment is displayed.&lt;br /&gt;
&lt;br /&gt;
[[File:Due-date-topic-instructor.png|800px]]&lt;br /&gt;
&lt;br /&gt;
[[File:Due-date-topic-instructor-2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
In the Topics subtab, due dates for each topic are displayed.&lt;br /&gt;
&lt;br /&gt;
====Student's View====&lt;br /&gt;
[[File:Student-assignments.png|800px]]&lt;br /&gt;
&lt;br /&gt;
Students can view due date information under &amp;lt;code&amp;gt;Current State&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Stage Deadline&amp;lt;/code&amp;gt;. The actions available to students are determined by the current DueDate status.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-not-over.png|800px]]&lt;br /&gt;
&lt;br /&gt;
During the submission period, students can access the &amp;lt;code&amp;gt;Submit a hyperlink&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Submit a file&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-over.png|800px]]&lt;br /&gt;
&lt;br /&gt;
Once the due date has passed, students cannot submit either a hyperlink or a file.&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
&lt;br /&gt;
Currently, there are some glaring issues with how due_dates was created in the first place, including redundancy and lack of modularity.&lt;br /&gt;
&lt;br /&gt;
'''Modeling &amp;amp; Structural Issues'''&lt;br /&gt;
# No dedicated &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to define different kinds of deadlines. The existing &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; class only serves as an association for &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; models, and is never referenced in the current &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; implementation.&lt;br /&gt;
# Duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table, which may lead to potential bugs.&lt;br /&gt;
# Incomplete deadline type definitions — &amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt; need to be added.&lt;br /&gt;
# No clear separation of concerns — data persistence, business logic, and permission logic are mixed in a single model.&lt;br /&gt;
&lt;br /&gt;
'''Permission &amp;amp; Behavior Issues'''&lt;br /&gt;
# Overuse of class methods making the code difficult to maintain.&lt;br /&gt;
# Missing permission checking logic for user actions that combines both role-based and time-based constraints.&lt;br /&gt;
&lt;br /&gt;
=== Tasks Identified ===&lt;br /&gt;
&lt;br /&gt;
'''Modeling &amp;amp; Structural'''&lt;br /&gt;
# Refactor the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model to separate persistence, business logic, and permission logic into distinct layers.&lt;br /&gt;
# Implement reusable mixins (&amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;) to handle permission evaluation and deadline-related operations.&lt;br /&gt;
# Introduce instance-level methods (e.g., &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;) to replace class-level utilities and improve testability.&lt;br /&gt;
&lt;br /&gt;
'''Permission &amp;amp; Behavior'''&lt;br /&gt;
# Integrate role-based and time-based permission checks within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; to ensure consistent access control.&lt;br /&gt;
# Redesign the &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.&lt;br /&gt;
# Add missing deadline types (&amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt;) and remove duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries for data integrity.&lt;br /&gt;
&lt;br /&gt;
== Proposed Solution ==&lt;br /&gt;
&lt;br /&gt;
The proposed solution restructures the due date system by separating concerns across dedicated models and mixin modules. Deadline definitions (&amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt;), core deadline data (&amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;), and behavioral logic (&amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;) are isolated so each component has a single clear responsibility. This modular design improves readability, reduces coupling, and makes deadline-related behavior easier to extend and maintain.&lt;br /&gt;
&lt;br /&gt;
=== Architecture Overview ===&lt;br /&gt;
&lt;br /&gt;
[[File:Duedate-diagram.png|800px]]&lt;br /&gt;
&lt;br /&gt;
This diagram visualizes the redesigned architecture with five distinct layers:&lt;br /&gt;
&lt;br /&gt;
* '''Layer 1 (Blue)''': Parent models (Assignment, SignUpTopic) that own due dates&lt;br /&gt;
* '''Layer 2 (Orange)''': DueDateActions module providing action-level queries&lt;br /&gt;
* '''Layer 3 (Red)''': DueDate model managing core deadline data&lt;br /&gt;
* '''Layer 4 (Orange)''': DueDatePermissions module evaluating permissions&lt;br /&gt;
* '''Layer 5 (Green)''': Reference data models (DeadlineType, DeadlineRight, Participant)&lt;br /&gt;
&lt;br /&gt;
The flow works as follows: Parent models include DueDateActions, which queries DueDate objects. Each DueDate includes DueDatePermissions to evaluate whether actions are allowed based on DeadlineRight states and Participant role flags.&lt;br /&gt;
&lt;br /&gt;
=== 1. DeadlineType Model ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Although the current codebase includes a &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model, it only serves as a passive lookup table. In practice, most parts of the system still rely on hard-coded numeric IDs or manual lookups:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Example of current usage&lt;br /&gt;
deadline_type_id = DeadlineType.find_by(name: 'review').id&lt;br /&gt;
if due_date.deadline_type_id == deadline_type_id&lt;br /&gt;
  # Perform review-related logic&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This leads to several structural issues:&lt;br /&gt;
* Inconsistent references (some use IDs, others strings)&lt;br /&gt;
* Tight coupling between &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
* Lack of semantic helper methods (e.g., &amp;lt;code&amp;gt;review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;submission?&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Data duplication and integrity risks (two &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; rows)&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; Table Structure&lt;br /&gt;
|- &lt;br /&gt;
! Column !! Type !! Description&lt;br /&gt;
|-&lt;br /&gt;
| id || BIGINT || Primary key, referenced by &amp;lt;code&amp;gt;due_dates.deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| name || VARCHAR(255) || Unique symbolic name (e.g., &amp;lt;code&amp;gt;submission&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| description || TEXT || Human-readable explanation of the deadline category&lt;br /&gt;
|-&lt;br /&gt;
| created_at || TIMESTAMP || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| updated_at || TIMESTAMP || Last update timestamp&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Deadline Type Definitions ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Canonical Deadline Type Entries&lt;br /&gt;
|-&lt;br /&gt;
! ID !! Name !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 1 || submission || Student submission deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 2 || review || Peer review deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 3 || teammate_review || Team member evaluation deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 5 || metareview || Meta-review deadlines (backward compatibility)&lt;br /&gt;
|-&lt;br /&gt;
| 6 || drop_topic || Topic drop deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 7 || signup || Topic signup deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 8 || team_formation || Team formation deadlines (duplicate cleaned)&lt;br /&gt;
|-&lt;br /&gt;
| 11 || quiz || Quiz completion deadlines&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== 2. DeadlineRight Model ===&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model represents the permission level for a specific action at a given deadline:&lt;br /&gt;
&lt;br /&gt;
* '''OK''': Action is allowed without penalty&lt;br /&gt;
* '''Late''': Action is allowed but marked as late&lt;br /&gt;
* '''No''': Action is not permitted&lt;br /&gt;
&lt;br /&gt;
These values are referenced by &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; through fields such as &amp;lt;code&amp;gt;submission_allowed_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review_allowed_id&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;quiz_allowed_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_rights&amp;lt;/code&amp;gt; Table Structure&lt;br /&gt;
|-&lt;br /&gt;
! Column !! Type !! Description&lt;br /&gt;
|-&lt;br /&gt;
| id || BIGINT || Primary key, referenced by &amp;lt;code&amp;gt;due_dates.*_allowed_id&amp;lt;/code&amp;gt; fields&lt;br /&gt;
|-&lt;br /&gt;
| name || VARCHAR(255) || Unique permission name (&amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| created_at || TIMESTAMP || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| updated_at || TIMESTAMP || Last update timestamp&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== 3. DueDate Model Refactoring ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model currently mixes persistence logic, deadline calculation, and permission checking, violating the Single Responsibility Principle. Most of its logic is implemented as class methods, which makes testing and extension difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Old design: class method that mixes concerns&lt;br /&gt;
def self.copy(old_assignment_id, new_assignment_id)&lt;br /&gt;
  duedates = where(parent_id: old_assignment_id)&lt;br /&gt;
  duedates.each do |orig_due_date|&lt;br /&gt;
    new_due_date = orig_due_date.dup&lt;br /&gt;
    new_due_date.parent_id = new_assignment_id&lt;br /&gt;
    new_due_date.save&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Improved Design ====&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model is focused on representing a single deadline entry. Behavior has been extracted into appropriate modules:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Responsibility Distribution After Refactoring&lt;br /&gt;
|-&lt;br /&gt;
! Concern !! Where It Lives&lt;br /&gt;
|-&lt;br /&gt;
| Permission checking || &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module (included in DueDate)&lt;br /&gt;
|-&lt;br /&gt;
| Deadline-related actions || &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module (included in parent models)&lt;br /&gt;
|-&lt;br /&gt;
| Deadline type semantics || &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model&lt;br /&gt;
|-&lt;br /&gt;
| Core deadline data || &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Key improvements:&lt;br /&gt;
* '''Instance-based behavior''': Replaced class methods with instance methods&lt;br /&gt;
* '''Clear query methods''': Simple predicates like &amp;lt;code&amp;gt;upcoming?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;overdue?&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Scopes for common queries''': &amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 4. DueDatePermissions Module ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Currently, permission checks are split into two disconnected layers:&lt;br /&gt;
&lt;br /&gt;
'''Role-based permissions''' (in participant_helpers.rb):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def participant_permissions(authorization)&lt;br /&gt;
  case authorization&lt;br /&gt;
  when 'reader' then { can_submit: false, can_review: true, can_take_quiz: true }&lt;br /&gt;
  # ...&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Deadline-based checks''' (in Assignment model):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def submission_allowed(topic_id = nil)&lt;br /&gt;
  check_condition('submission_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These two layers never interact, leading to inconsistencies.&lt;br /&gt;
&lt;br /&gt;
==== Proposed Solution ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module integrates both layers:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Permission Methods&lt;br /&gt;
|-&lt;br /&gt;
! Method !! What It Checks&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_submit?(participant)&amp;lt;/code&amp;gt; || Submission deadline is OK/Late AND participant can submit&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_review?(participant)&amp;lt;/code&amp;gt; || Review deadline is OK/Late AND participant can review&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_take_quiz?(participant)&amp;lt;/code&amp;gt; || Quiz deadline is OK/Late AND participant can take quiz&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_review_teammate?(participant)&amp;lt;/code&amp;gt; || Teammate review deadline is OK/Late AND participant can review&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;activity_permissible?(activity)&amp;lt;/code&amp;gt; || Generic checker for any activity&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;permission_status_for(action)&amp;lt;/code&amp;gt; || Returns 'OK', 'Late', or 'No' for display&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDatePermissions&lt;br /&gt;
  def can_submit?(participant = nil)&lt;br /&gt;
    return false unless submission_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_submit&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: submission_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Similar implementations for can_review?, can_take_quiz?, etc.&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 5. DueDateActions Module ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module provides a unified interface for models that own due dates (&amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt;). This centralizes the logic for querying upcoming deadlines and determining whether specific activities are currently allowed.&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Methods in DueDateActions&lt;br /&gt;
|-&lt;br /&gt;
! Method !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;activity_permissible?(activity)&amp;lt;/code&amp;gt; || Checks if an activity is permitted at the current time&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;submission_permissible?&amp;lt;/code&amp;gt; || Convenience wrapper for submission checking&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;review_permissible?&amp;lt;/code&amp;gt; || Convenience wrapper for review checking&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;next_due_date(topic_id)&amp;lt;/code&amp;gt; || Resolves the next applicable deadline&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;current_stage&amp;lt;/code&amp;gt; || Returns human-readable stage name (e.g., &amp;quot;review&amp;quot;, &amp;quot;submission&amp;quot;)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;has_topic_specific_deadlines?&amp;lt;/code&amp;gt; || Determines if assignment uses topic-specific deadlines&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;copy_due_dates_to(new_parent)&amp;lt;/code&amp;gt; || Utility for cloning due dates to another assignment&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Deadline Resolution Flow ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ How next_due_date Works&lt;br /&gt;
|-&lt;br /&gt;
! Step !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 1. Topic-specific check || If topic ID provided and assignment uses staggered deadlines, check topic-level deadlines first&lt;br /&gt;
|-&lt;br /&gt;
| 2. Assignment-level fallback || If no topic deadline exists, use nearest assignment-level deadline&lt;br /&gt;
|-&lt;br /&gt;
| 3. Permission evaluation || Delegate to DueDatePermissions to determine if action is allowed&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDateActions&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    current_deadline = next_due_date()&lt;br /&gt;
    return false unless current_deadline&lt;br /&gt;
    current_deadline.activity_permissible?(activity)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def submission_permissible?&lt;br /&gt;
    activity_permissible?(:submission)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def next_due_date(topic_id = nil)&lt;br /&gt;
    if topic_id &amp;amp;&amp;amp; has_topic_specific_deadlines?&lt;br /&gt;
      topic_deadline = due_dates.where(parent_id: topic_id, parent_type: 'SignUpTopic')&lt;br /&gt;
                                .upcoming.first&lt;br /&gt;
      return topic_deadline if topic_deadline&lt;br /&gt;
    end&lt;br /&gt;
    due_dates.upcoming.first&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Implementation Summary ==&lt;br /&gt;
&lt;br /&gt;
=== Models Created/Modified ===&lt;br /&gt;
&lt;br /&gt;
* '''DeadlineType''' — Canonical deadline type definitions with semantic helper methods&lt;br /&gt;
* '''DeadlineRight''' — Permission state model (OK/Late/No) with comparison methods&lt;br /&gt;
* '''DueDate''' — Refactored with instance methods, scopes, and permission checking&lt;br /&gt;
* '''TopicDueDate''' — STI subclass for topic-specific deadlines&lt;br /&gt;
* '''Assignment''' — Includes DueDateActions mixin&lt;br /&gt;
* '''SignUpTopic''' — Includes DueDateActions mixin&lt;br /&gt;
&lt;br /&gt;
=== Modules Created ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions''' — Permission checking combining deadline and role constraints&lt;br /&gt;
* '''DueDateActions''' — Deadline-related operations for parent models&lt;br /&gt;
&lt;br /&gt;
=== Key Improvements ===&lt;br /&gt;
&lt;br /&gt;
# '''Separation of Concerns''': Permission logic separated into dedicated modules&lt;br /&gt;
# '''Instance Methods''': Replaced class methods with testable instance methods&lt;br /&gt;
# '''Polymorphic Design''': Single DueDate model serves both Assignment and SignUpTopic&lt;br /&gt;
# '''Unified Permissions''': Role-based and time-based checks combined in one interface&lt;br /&gt;
# '''Data Integrity''': Removed duplicate deadline types, enforced foreign key constraints&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
The test suite includes comprehensive RSpec tests for the DueDate model using nested contexts, edge case coverage, and error handling.&lt;br /&gt;
&lt;br /&gt;
=== Current Coverage ===&lt;br /&gt;
&lt;br /&gt;
* '''Validations''': Required fields (parent, due_at, deadline_type_id), round validation, error messages&lt;br /&gt;
* '''Scopes''': &amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt; with edge cases&lt;br /&gt;
* '''Instance Methods''': &amp;lt;code&amp;gt;#overdue?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#upcoming?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#set&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#copy&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#deadline_type_name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#last_deadline?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#&amp;lt;=&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Class Methods''': &amp;lt;code&amp;gt;.sort_due_dates&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.any_future_due_dates?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.copy&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Callbacks''': &amp;lt;code&amp;gt;before_save :set_default_round&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
Tests follow RSpec best practices with nested contexts:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
describe 'validations' do&lt;br /&gt;
  context 'when parent is missing' do&lt;br /&gt;
    it 'is invalid' do&lt;br /&gt;
      expect(due_date).to be_invalid&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'has error on parent field' do&lt;br /&gt;
      due_date.valid?&lt;br /&gt;
      expect(due_date.errors[:parent]).to include('must exist')&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when round validation' do&lt;br /&gt;
    context 'with round value of 0' do&lt;br /&gt;
      it 'is invalid' do&lt;br /&gt;
        expect(due_date).to be_invalid&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'with nil round' do&lt;br /&gt;
      it 'sets default round to 1' do&lt;br /&gt;
        expect(due_date.round).to eq(1)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Planned Future Tests ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions module''': Test &amp;lt;code&amp;gt;can_submit?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;can_review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;can_take_quiz?&amp;lt;/code&amp;gt; with participant parameters&lt;br /&gt;
* '''DueDateActions module''': Test &amp;lt;code&amp;gt;activity_permissible?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt; in Assignment context&lt;br /&gt;
* '''Integration tests''': Combined role-based and time-based permission logic&lt;br /&gt;
&lt;br /&gt;
=== Test Metrics ===&lt;br /&gt;
&lt;br /&gt;
* '''Total examples''': 438 tests&lt;br /&gt;
* '''DueDate model''': 60+ examples&lt;br /&gt;
* '''Code coverage''': 100% of DueDate methods&lt;br /&gt;
* '''Execution time''': ~12 seconds&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
=== Demo Link ===&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add deadline notification system using the new permission framework&lt;br /&gt;
* Implement deadline templates for common assignment patterns&lt;br /&gt;
* Add API endpoints for mobile app integration&lt;br /&gt;
* Create admin dashboard for monitoring deadline compliance across courses&lt;br /&gt;
* Add automated deadline extension workflows based on configurable rules&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167217</id>
		<title>CSC/ECE 517 Fall 2025 - E2566. Finish DueDates</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167217"/>
		<updated>2025-12-02T15:34:10Z</updated>

		<summary type="html">&lt;p&gt;Skim253: /* 2. DeadlineRight Model */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
This project aims to complete the implementation of the DueDate system in Expertiza. The current implementation consists mostly of class methods and lacks proper definition of different deadline types and permission checking functionality. This redesign will create a more robust, readable, and maintainable due date system.&lt;br /&gt;
&lt;br /&gt;
=== Existing Components ===&lt;br /&gt;
* &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model with polymorphic parent association (Assignment/SignUpTopic)&lt;br /&gt;
* &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; subclasses&lt;br /&gt;
* Basic CRUD operations (within &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AssignmentForm&amp;lt;/code&amp;gt;) and sorting functionality (&amp;lt;code&amp;gt;deadline_sort&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Database schema with &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt; field&lt;br /&gt;
&lt;br /&gt;
=== Current Behavior ===&lt;br /&gt;
&lt;br /&gt;
====Admin's View====&lt;br /&gt;
[[File:Duedates.png|800px]]&lt;br /&gt;
&lt;br /&gt;
When editing an assignment, due dates can be modified under the Due Dates tab. Each row contains the following columns: &amp;lt;code&amp;gt;Date &amp;amp; Time&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Use Date Updater&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Submission Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Teammate Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Meta-Review Allowed&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Reminder&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:Assignments-CurrentStage.png|800px]]&lt;br /&gt;
&lt;br /&gt;
In the Assignments tab, the current stage and stage deadline of each assignment is displayed.&lt;br /&gt;
&lt;br /&gt;
[[File:Due-date-topic-instructor.png|800px]]&lt;br /&gt;
&lt;br /&gt;
[[File:Due-date-topic-instructor-2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
In the Topics subtab, due dates for each topic are displayed.&lt;br /&gt;
&lt;br /&gt;
====Student's View====&lt;br /&gt;
[[File:Student-assignments.png|800px]]&lt;br /&gt;
&lt;br /&gt;
Students can view due date information under &amp;lt;code&amp;gt;Current State&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Stage Deadline&amp;lt;/code&amp;gt;. The actions available to students are determined by the current DueDate status.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-not-over.png|800px]]&lt;br /&gt;
&lt;br /&gt;
During the submission period, students can access the &amp;lt;code&amp;gt;Submit a hyperlink&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Submit a file&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-over.png|800px]]&lt;br /&gt;
&lt;br /&gt;
Once the due date has passed, students cannot submit either a hyperlink or a file.&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
&lt;br /&gt;
Currently, there are some glaring issues with how due_dates was created in the first place, including redundancy and lack of modularity.&lt;br /&gt;
&lt;br /&gt;
'''Modeling &amp;amp; Structural Issues'''&lt;br /&gt;
# No dedicated &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to define different kinds of deadlines. The existing &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; class only serves as an association for &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; models, and is never referenced in the current &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; implementation.&lt;br /&gt;
# Duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table, which may lead to potential bugs.&lt;br /&gt;
# Incomplete deadline type definitions — &amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt; need to be added.&lt;br /&gt;
# No clear separation of concerns — data persistence, business logic, and permission logic are mixed in a single model.&lt;br /&gt;
&lt;br /&gt;
'''Permission &amp;amp; Behavior Issues'''&lt;br /&gt;
# Overuse of class methods making the code difficult to maintain.&lt;br /&gt;
# Missing permission checking logic for user actions that combines both role-based and time-based constraints.&lt;br /&gt;
&lt;br /&gt;
=== Tasks Identified ===&lt;br /&gt;
&lt;br /&gt;
'''Modeling &amp;amp; Structural'''&lt;br /&gt;
# Refactor the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model to separate persistence, business logic, and permission logic into distinct layers.&lt;br /&gt;
# Implement reusable mixins (&amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;) to handle permission evaluation and deadline-related operations.&lt;br /&gt;
# Introduce instance-level methods (e.g., &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;) to replace class-level utilities and improve testability.&lt;br /&gt;
&lt;br /&gt;
'''Permission &amp;amp; Behavior'''&lt;br /&gt;
# Integrate role-based and time-based permission checks within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; to ensure consistent access control.&lt;br /&gt;
# Redesign the &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.&lt;br /&gt;
# Add missing deadline types (&amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt;) and remove duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries for data integrity.&lt;br /&gt;
&lt;br /&gt;
== Proposed Solution ==&lt;br /&gt;
&lt;br /&gt;
The proposed solution restructures the due date system by separating concerns across dedicated models and mixin modules. Deadline definitions (&amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt;), core deadline data (&amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;), and behavioral logic (&amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;) are isolated so each component has a single clear responsibility. This modular design improves readability, reduces coupling, and makes deadline-related behavior easier to extend and maintain.&lt;br /&gt;
&lt;br /&gt;
=== Architecture Overview ===&lt;br /&gt;
&lt;br /&gt;
[[File:Duedate-diagram.png|800px]]&lt;br /&gt;
&lt;br /&gt;
This diagram visualizes the redesigned architecture with five distinct layers:&lt;br /&gt;
&lt;br /&gt;
* '''Layer 1 (Blue)''': Parent models (Assignment, SignUpTopic) that own due dates&lt;br /&gt;
* '''Layer 2 (Orange)''': DueDateActions module providing action-level queries&lt;br /&gt;
* '''Layer 3 (Red)''': DueDate model managing core deadline data&lt;br /&gt;
* '''Layer 4 (Orange)''': DueDatePermissions module evaluating permissions&lt;br /&gt;
* '''Layer 5 (Green)''': Reference data models (DeadlineType, DeadlineRight, Participant)&lt;br /&gt;
&lt;br /&gt;
The flow works as follows: Parent models include DueDateActions, which queries DueDate objects. Each DueDate includes DueDatePermissions to evaluate whether actions are allowed based on DeadlineRight states and Participant role flags.&lt;br /&gt;
&lt;br /&gt;
=== 1. DeadlineType Model ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Although the current codebase includes a &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model, it only serves as a passive lookup table. In practice, most parts of the system still rely on hard-coded numeric IDs or manual lookups:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Example of current usage&lt;br /&gt;
deadline_type_id = DeadlineType.find_by(name: 'review').id&lt;br /&gt;
if due_date.deadline_type_id == deadline_type_id&lt;br /&gt;
  # Perform review-related logic&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This leads to several structural issues:&lt;br /&gt;
* Inconsistent references (some use IDs, others strings)&lt;br /&gt;
* Tight coupling between &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
* Lack of semantic helper methods (e.g., &amp;lt;code&amp;gt;review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;submission?&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Data duplication and integrity risks (two &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; rows)&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; Table Structure&lt;br /&gt;
|- &lt;br /&gt;
! Column !! Type !! Description&lt;br /&gt;
|-&lt;br /&gt;
| id || BIGINT || Primary key, referenced by &amp;lt;code&amp;gt;due_dates.deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| name || VARCHAR(255) || Unique symbolic name (e.g., &amp;lt;code&amp;gt;submission&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| description || TEXT || Human-readable explanation of the deadline category&lt;br /&gt;
|-&lt;br /&gt;
| created_at || TIMESTAMP || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| updated_at || TIMESTAMP || Last update timestamp&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Deadline Type Definitions ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Canonical Deadline Type Entries&lt;br /&gt;
|-&lt;br /&gt;
! ID !! Name !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 1 || submission || Student submission deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 2 || review || Peer review deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 3 || teammate_review || Team member evaluation deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 5 || metareview || Meta-review deadlines (backward compatibility)&lt;br /&gt;
|-&lt;br /&gt;
| 6 || drop_topic || Topic drop deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 7 || signup || Topic signup deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 8 || team_formation || Team formation deadlines (duplicate cleaned)&lt;br /&gt;
|-&lt;br /&gt;
| 11 || quiz || Quiz completion deadlines&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== 2. DeadlineRight Model ===&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model represents the permission level for a specific action at a given deadline:&lt;br /&gt;
&lt;br /&gt;
* '''OK''': Action is allowed without penalty&lt;br /&gt;
* '''Late''': Action is allowed but marked as late&lt;br /&gt;
* '''No''': Action is not permitted&lt;br /&gt;
&lt;br /&gt;
These values are referenced by &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; through fields such as &amp;lt;code&amp;gt;submission_allowed_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review_allowed_id&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;quiz_allowed_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_rights&amp;lt;/code&amp;gt; Table Structure&lt;br /&gt;
|-&lt;br /&gt;
! Column !! Type !! Description&lt;br /&gt;
|-&lt;br /&gt;
| id || BIGINT || Primary key, referenced by &amp;lt;code&amp;gt;due_dates.*_allowed_id&amp;lt;/code&amp;gt; fields&lt;br /&gt;
|-&lt;br /&gt;
| name || VARCHAR(255) || Unique permission name (&amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| created_at || TIMESTAMP || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| updated_at || TIMESTAMP || Last update timestamp&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== 3. DueDate Model Refactoring ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model currently mixes persistence logic, deadline calculation, and permission checking, violating the Single Responsibility Principle. Most of its logic is implemented as class methods, which makes testing and extension difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Old design: class method that mixes concerns&lt;br /&gt;
def self.copy(old_assignment_id, new_assignment_id)&lt;br /&gt;
  duedates = where(parent_id: old_assignment_id)&lt;br /&gt;
  duedates.each do |orig_due_date|&lt;br /&gt;
    new_due_date = orig_due_date.dup&lt;br /&gt;
    new_due_date.parent_id = new_assignment_id&lt;br /&gt;
    new_due_date.save&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Improved Design ====&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model is focused on representing a single deadline entry. Behavior has been extracted into appropriate modules:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Responsibility Distribution After Refactoring&lt;br /&gt;
|-&lt;br /&gt;
! Concern !! Where It Lives&lt;br /&gt;
|-&lt;br /&gt;
| Permission checking || &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module (included in DueDate)&lt;br /&gt;
|-&lt;br /&gt;
| Deadline-related actions || &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module (included in parent models)&lt;br /&gt;
|-&lt;br /&gt;
| Deadline type semantics || &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model&lt;br /&gt;
|-&lt;br /&gt;
| Core deadline data || &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Key improvements:&lt;br /&gt;
* '''Instance-based behavior''': Replaced class methods with instance methods&lt;br /&gt;
* '''Clear query methods''': Simple predicates like &amp;lt;code&amp;gt;upcoming?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;overdue?&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Scopes for common queries''': &amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
&lt;br /&gt;
  belongs_to :parent, polymorphic: true&lt;br /&gt;
  belongs_to :deadline_type&lt;br /&gt;
  validates :due_at, :deadline_type_id, :parent, presence: true&lt;br /&gt;
&lt;br /&gt;
  scope :upcoming, -&amp;gt; { where('due_at &amp;gt; ?', Time.current).order(:due_at) }&lt;br /&gt;
  scope :overdue, -&amp;gt; { where('due_at &amp;lt; ?', Time.current).order(:due_at) }&lt;br /&gt;
&lt;br /&gt;
  def overdue?&lt;br /&gt;
    due_at &amp;lt; Time.current&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def copy(to_assignment_id)&lt;br /&gt;
    new_due_date = dup&lt;br /&gt;
    new_due_date.parent = Assignment.find(to_assignment_id)&lt;br /&gt;
    new_due_date.save!&lt;br /&gt;
    new_due_date&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 4. DueDatePermissions Module ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Currently, permission checks are split into two disconnected layers:&lt;br /&gt;
&lt;br /&gt;
'''Role-based permissions''' (in participant_helpers.rb):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def participant_permissions(authorization)&lt;br /&gt;
  case authorization&lt;br /&gt;
  when 'reader' then { can_submit: false, can_review: true, can_take_quiz: true }&lt;br /&gt;
  # ...&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Deadline-based checks''' (in Assignment model):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def submission_allowed(topic_id = nil)&lt;br /&gt;
  check_condition('submission_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These two layers never interact, leading to inconsistencies.&lt;br /&gt;
&lt;br /&gt;
==== Proposed Solution ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module integrates both layers:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Permission Methods&lt;br /&gt;
|-&lt;br /&gt;
! Method !! What It Checks&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_submit?(participant)&amp;lt;/code&amp;gt; || Submission deadline is OK/Late AND participant can submit&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_review?(participant)&amp;lt;/code&amp;gt; || Review deadline is OK/Late AND participant can review&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_take_quiz?(participant)&amp;lt;/code&amp;gt; || Quiz deadline is OK/Late AND participant can take quiz&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_review_teammate?(participant)&amp;lt;/code&amp;gt; || Teammate review deadline is OK/Late AND participant can review&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;activity_permissible?(activity)&amp;lt;/code&amp;gt; || Generic checker for any activity&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;permission_status_for(action)&amp;lt;/code&amp;gt; || Returns 'OK', 'Late', or 'No' for display&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDatePermissions&lt;br /&gt;
  def can_submit?(participant = nil)&lt;br /&gt;
    return false unless submission_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_submit&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: submission_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Similar implementations for can_review?, can_take_quiz?, etc.&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 5. DueDateActions Module ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module provides a unified interface for models that own due dates (&amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt;). This centralizes the logic for querying upcoming deadlines and determining whether specific activities are currently allowed.&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Methods in DueDateActions&lt;br /&gt;
|-&lt;br /&gt;
! Method !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;activity_permissible?(activity)&amp;lt;/code&amp;gt; || Checks if an activity is permitted at the current time&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;submission_permissible?&amp;lt;/code&amp;gt; || Convenience wrapper for submission checking&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;review_permissible?&amp;lt;/code&amp;gt; || Convenience wrapper for review checking&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;next_due_date(topic_id)&amp;lt;/code&amp;gt; || Resolves the next applicable deadline&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;current_stage&amp;lt;/code&amp;gt; || Returns human-readable stage name (e.g., &amp;quot;review&amp;quot;, &amp;quot;submission&amp;quot;)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;has_topic_specific_deadlines?&amp;lt;/code&amp;gt; || Determines if assignment uses topic-specific deadlines&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;copy_due_dates_to(new_parent)&amp;lt;/code&amp;gt; || Utility for cloning due dates to another assignment&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Deadline Resolution Flow ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ How next_due_date Works&lt;br /&gt;
|-&lt;br /&gt;
! Step !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 1. Topic-specific check || If topic ID provided and assignment uses staggered deadlines, check topic-level deadlines first&lt;br /&gt;
|-&lt;br /&gt;
| 2. Assignment-level fallback || If no topic deadline exists, use nearest assignment-level deadline&lt;br /&gt;
|-&lt;br /&gt;
| 3. Permission evaluation || Delegate to DueDatePermissions to determine if action is allowed&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDateActions&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    current_deadline = next_due_date()&lt;br /&gt;
    return false unless current_deadline&lt;br /&gt;
    current_deadline.activity_permissible?(activity)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def submission_permissible?&lt;br /&gt;
    activity_permissible?(:submission)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def next_due_date(topic_id = nil)&lt;br /&gt;
    if topic_id &amp;amp;&amp;amp; has_topic_specific_deadlines?&lt;br /&gt;
      topic_deadline = due_dates.where(parent_id: topic_id, parent_type: 'SignUpTopic')&lt;br /&gt;
                                .upcoming.first&lt;br /&gt;
      return topic_deadline if topic_deadline&lt;br /&gt;
    end&lt;br /&gt;
    due_dates.upcoming.first&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Implementation Summary ==&lt;br /&gt;
&lt;br /&gt;
=== Models Created/Modified ===&lt;br /&gt;
&lt;br /&gt;
* '''DeadlineType''' — Canonical deadline type definitions with semantic helper methods&lt;br /&gt;
* '''DeadlineRight''' — Permission state model (OK/Late/No) with comparison methods&lt;br /&gt;
* '''DueDate''' — Refactored with instance methods, scopes, and permission checking&lt;br /&gt;
* '''TopicDueDate''' — STI subclass for topic-specific deadlines&lt;br /&gt;
* '''Assignment''' — Includes DueDateActions mixin&lt;br /&gt;
* '''SignUpTopic''' — Includes DueDateActions mixin&lt;br /&gt;
&lt;br /&gt;
=== Modules Created ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions''' — Permission checking combining deadline and role constraints&lt;br /&gt;
* '''DueDateActions''' — Deadline-related operations for parent models&lt;br /&gt;
&lt;br /&gt;
=== Key Improvements ===&lt;br /&gt;
&lt;br /&gt;
# '''Separation of Concerns''': Permission logic separated into dedicated modules&lt;br /&gt;
# '''Instance Methods''': Replaced class methods with testable instance methods&lt;br /&gt;
# '''Polymorphic Design''': Single DueDate model serves both Assignment and SignUpTopic&lt;br /&gt;
# '''Unified Permissions''': Role-based and time-based checks combined in one interface&lt;br /&gt;
# '''Data Integrity''': Removed duplicate deadline types, enforced foreign key constraints&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
The test suite includes comprehensive RSpec tests for the DueDate model using nested contexts, edge case coverage, and error handling.&lt;br /&gt;
&lt;br /&gt;
=== Current Coverage ===&lt;br /&gt;
&lt;br /&gt;
* '''Validations''': Required fields (parent, due_at, deadline_type_id), round validation, error messages&lt;br /&gt;
* '''Scopes''': &amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt; with edge cases&lt;br /&gt;
* '''Instance Methods''': &amp;lt;code&amp;gt;#overdue?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#upcoming?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#set&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#copy&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#deadline_type_name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#last_deadline?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#&amp;lt;=&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Class Methods''': &amp;lt;code&amp;gt;.sort_due_dates&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.any_future_due_dates?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.copy&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Callbacks''': &amp;lt;code&amp;gt;before_save :set_default_round&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
Tests follow RSpec best practices with nested contexts:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
describe 'validations' do&lt;br /&gt;
  context 'when parent is missing' do&lt;br /&gt;
    it 'is invalid' do&lt;br /&gt;
      expect(due_date).to be_invalid&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'has error on parent field' do&lt;br /&gt;
      due_date.valid?&lt;br /&gt;
      expect(due_date.errors[:parent]).to include('must exist')&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when round validation' do&lt;br /&gt;
    context 'with round value of 0' do&lt;br /&gt;
      it 'is invalid' do&lt;br /&gt;
        expect(due_date).to be_invalid&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'with nil round' do&lt;br /&gt;
      it 'sets default round to 1' do&lt;br /&gt;
        expect(due_date.round).to eq(1)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Planned Future Tests ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions module''': Test &amp;lt;code&amp;gt;can_submit?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;can_review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;can_take_quiz?&amp;lt;/code&amp;gt; with participant parameters&lt;br /&gt;
* '''DueDateActions module''': Test &amp;lt;code&amp;gt;activity_permissible?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt; in Assignment context&lt;br /&gt;
* '''Integration tests''': Combined role-based and time-based permission logic&lt;br /&gt;
&lt;br /&gt;
=== Test Metrics ===&lt;br /&gt;
&lt;br /&gt;
* '''Total examples''': 438 tests&lt;br /&gt;
* '''DueDate model''': 60+ examples&lt;br /&gt;
* '''Code coverage''': 100% of DueDate methods&lt;br /&gt;
* '''Execution time''': ~12 seconds&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
=== Demo Link ===&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add deadline notification system using the new permission framework&lt;br /&gt;
* Implement deadline templates for common assignment patterns&lt;br /&gt;
* Add API endpoints for mobile app integration&lt;br /&gt;
* Create admin dashboard for monitoring deadline compliance across courses&lt;br /&gt;
* Add automated deadline extension workflows based on configurable rules&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167216</id>
		<title>CSC/ECE 517 Fall 2025 - E2566. Finish DueDates</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167216"/>
		<updated>2025-12-02T15:33:14Z</updated>

		<summary type="html">&lt;p&gt;Skim253: /* 1. DeadlineType Model */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
This project aims to complete the implementation of the DueDate system in Expertiza. The current implementation consists mostly of class methods and lacks proper definition of different deadline types and permission checking functionality. This redesign will create a more robust, readable, and maintainable due date system.&lt;br /&gt;
&lt;br /&gt;
=== Existing Components ===&lt;br /&gt;
* &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model with polymorphic parent association (Assignment/SignUpTopic)&lt;br /&gt;
* &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; subclasses&lt;br /&gt;
* Basic CRUD operations (within &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AssignmentForm&amp;lt;/code&amp;gt;) and sorting functionality (&amp;lt;code&amp;gt;deadline_sort&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Database schema with &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt; field&lt;br /&gt;
&lt;br /&gt;
=== Current Behavior ===&lt;br /&gt;
&lt;br /&gt;
====Admin's View====&lt;br /&gt;
[[File:Duedates.png|800px]]&lt;br /&gt;
&lt;br /&gt;
When editing an assignment, due dates can be modified under the Due Dates tab. Each row contains the following columns: &amp;lt;code&amp;gt;Date &amp;amp; Time&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Use Date Updater&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Submission Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Teammate Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Meta-Review Allowed&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Reminder&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:Assignments-CurrentStage.png|800px]]&lt;br /&gt;
&lt;br /&gt;
In the Assignments tab, the current stage and stage deadline of each assignment is displayed.&lt;br /&gt;
&lt;br /&gt;
[[File:Due-date-topic-instructor.png|800px]]&lt;br /&gt;
&lt;br /&gt;
[[File:Due-date-topic-instructor-2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
In the Topics subtab, due dates for each topic are displayed.&lt;br /&gt;
&lt;br /&gt;
====Student's View====&lt;br /&gt;
[[File:Student-assignments.png|800px]]&lt;br /&gt;
&lt;br /&gt;
Students can view due date information under &amp;lt;code&amp;gt;Current State&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Stage Deadline&amp;lt;/code&amp;gt;. The actions available to students are determined by the current DueDate status.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-not-over.png|800px]]&lt;br /&gt;
&lt;br /&gt;
During the submission period, students can access the &amp;lt;code&amp;gt;Submit a hyperlink&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Submit a file&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-over.png|800px]]&lt;br /&gt;
&lt;br /&gt;
Once the due date has passed, students cannot submit either a hyperlink or a file.&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
&lt;br /&gt;
Currently, there are some glaring issues with how due_dates was created in the first place, including redundancy and lack of modularity.&lt;br /&gt;
&lt;br /&gt;
'''Modeling &amp;amp; Structural Issues'''&lt;br /&gt;
# No dedicated &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to define different kinds of deadlines. The existing &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; class only serves as an association for &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; models, and is never referenced in the current &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; implementation.&lt;br /&gt;
# Duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table, which may lead to potential bugs.&lt;br /&gt;
# Incomplete deadline type definitions — &amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt; need to be added.&lt;br /&gt;
# No clear separation of concerns — data persistence, business logic, and permission logic are mixed in a single model.&lt;br /&gt;
&lt;br /&gt;
'''Permission &amp;amp; Behavior Issues'''&lt;br /&gt;
# Overuse of class methods making the code difficult to maintain.&lt;br /&gt;
# Missing permission checking logic for user actions that combines both role-based and time-based constraints.&lt;br /&gt;
&lt;br /&gt;
=== Tasks Identified ===&lt;br /&gt;
&lt;br /&gt;
'''Modeling &amp;amp; Structural'''&lt;br /&gt;
# Refactor the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model to separate persistence, business logic, and permission logic into distinct layers.&lt;br /&gt;
# Implement reusable mixins (&amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;) to handle permission evaluation and deadline-related operations.&lt;br /&gt;
# Introduce instance-level methods (e.g., &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;) to replace class-level utilities and improve testability.&lt;br /&gt;
&lt;br /&gt;
'''Permission &amp;amp; Behavior'''&lt;br /&gt;
# Integrate role-based and time-based permission checks within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; to ensure consistent access control.&lt;br /&gt;
# Redesign the &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.&lt;br /&gt;
# Add missing deadline types (&amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt;) and remove duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries for data integrity.&lt;br /&gt;
&lt;br /&gt;
== Proposed Solution ==&lt;br /&gt;
&lt;br /&gt;
The proposed solution restructures the due date system by separating concerns across dedicated models and mixin modules. Deadline definitions (&amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt;), core deadline data (&amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;), and behavioral logic (&amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;) are isolated so each component has a single clear responsibility. This modular design improves readability, reduces coupling, and makes deadline-related behavior easier to extend and maintain.&lt;br /&gt;
&lt;br /&gt;
=== Architecture Overview ===&lt;br /&gt;
&lt;br /&gt;
[[File:Duedate-diagram.png|800px]]&lt;br /&gt;
&lt;br /&gt;
This diagram visualizes the redesigned architecture with five distinct layers:&lt;br /&gt;
&lt;br /&gt;
* '''Layer 1 (Blue)''': Parent models (Assignment, SignUpTopic) that own due dates&lt;br /&gt;
* '''Layer 2 (Orange)''': DueDateActions module providing action-level queries&lt;br /&gt;
* '''Layer 3 (Red)''': DueDate model managing core deadline data&lt;br /&gt;
* '''Layer 4 (Orange)''': DueDatePermissions module evaluating permissions&lt;br /&gt;
* '''Layer 5 (Green)''': Reference data models (DeadlineType, DeadlineRight, Participant)&lt;br /&gt;
&lt;br /&gt;
The flow works as follows: Parent models include DueDateActions, which queries DueDate objects. Each DueDate includes DueDatePermissions to evaluate whether actions are allowed based on DeadlineRight states and Participant role flags.&lt;br /&gt;
&lt;br /&gt;
=== 1. DeadlineType Model ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Although the current codebase includes a &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model, it only serves as a passive lookup table. In practice, most parts of the system still rely on hard-coded numeric IDs or manual lookups:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Example of current usage&lt;br /&gt;
deadline_type_id = DeadlineType.find_by(name: 'review').id&lt;br /&gt;
if due_date.deadline_type_id == deadline_type_id&lt;br /&gt;
  # Perform review-related logic&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This leads to several structural issues:&lt;br /&gt;
* Inconsistent references (some use IDs, others strings)&lt;br /&gt;
* Tight coupling between &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
* Lack of semantic helper methods (e.g., &amp;lt;code&amp;gt;review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;submission?&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Data duplication and integrity risks (two &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; rows)&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; Table Structure&lt;br /&gt;
|- &lt;br /&gt;
! Column !! Type !! Description&lt;br /&gt;
|-&lt;br /&gt;
| id || BIGINT || Primary key, referenced by &amp;lt;code&amp;gt;due_dates.deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| name || VARCHAR(255) || Unique symbolic name (e.g., &amp;lt;code&amp;gt;submission&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| description || TEXT || Human-readable explanation of the deadline category&lt;br /&gt;
|-&lt;br /&gt;
| created_at || TIMESTAMP || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| updated_at || TIMESTAMP || Last update timestamp&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Deadline Type Definitions ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Canonical Deadline Type Entries&lt;br /&gt;
|-&lt;br /&gt;
! ID !! Name !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 1 || submission || Student submission deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 2 || review || Peer review deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 3 || teammate_review || Team member evaluation deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 5 || metareview || Meta-review deadlines (backward compatibility)&lt;br /&gt;
|-&lt;br /&gt;
| 6 || drop_topic || Topic drop deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 7 || signup || Topic signup deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 8 || team_formation || Team formation deadlines (duplicate cleaned)&lt;br /&gt;
|-&lt;br /&gt;
| 11 || quiz || Quiz completion deadlines&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== 2. DeadlineRight Model ===&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model represents the permission level for a specific action at a given deadline:&lt;br /&gt;
&lt;br /&gt;
* '''OK''': Action is allowed without penalty&lt;br /&gt;
* '''Late''': Action is allowed but marked as late&lt;br /&gt;
* '''No''': Action is not permitted&lt;br /&gt;
&lt;br /&gt;
These values are referenced by &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; through fields such as &amp;lt;code&amp;gt;submission_allowed_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review_allowed_id&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;quiz_allowed_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_rights&amp;lt;/code&amp;gt; Table Structure&lt;br /&gt;
|-&lt;br /&gt;
! Column !! Type !! Description&lt;br /&gt;
|-&lt;br /&gt;
| id || BIGINT || Primary key, referenced by &amp;lt;code&amp;gt;due_dates.*_allowed_id&amp;lt;/code&amp;gt; fields&lt;br /&gt;
|-&lt;br /&gt;
| name || VARCHAR(255) || Unique permission name (&amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| created_at || TIMESTAMP || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| updated_at || TIMESTAMP || Last update timestamp&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Implementation ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineRight &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
&lt;br /&gt;
  def allows_action?&lt;br /&gt;
    %w[OK Late].include?(name)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def denies_action?&lt;br /&gt;
    name == 'No'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def allows_with_penalty?&lt;br /&gt;
    name == 'Late'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def allows_without_penalty?&lt;br /&gt;
    name == 'OK'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def permission_level&lt;br /&gt;
    case name&lt;br /&gt;
    when 'No' then 0&lt;br /&gt;
    when 'Late' then 1&lt;br /&gt;
    when 'OK' then 2&lt;br /&gt;
    else -1&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 3. DueDate Model Refactoring ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model currently mixes persistence logic, deadline calculation, and permission checking, violating the Single Responsibility Principle. Most of its logic is implemented as class methods, which makes testing and extension difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Old design: class method that mixes concerns&lt;br /&gt;
def self.copy(old_assignment_id, new_assignment_id)&lt;br /&gt;
  duedates = where(parent_id: old_assignment_id)&lt;br /&gt;
  duedates.each do |orig_due_date|&lt;br /&gt;
    new_due_date = orig_due_date.dup&lt;br /&gt;
    new_due_date.parent_id = new_assignment_id&lt;br /&gt;
    new_due_date.save&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Improved Design ====&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model is focused on representing a single deadline entry. Behavior has been extracted into appropriate modules:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Responsibility Distribution After Refactoring&lt;br /&gt;
|-&lt;br /&gt;
! Concern !! Where It Lives&lt;br /&gt;
|-&lt;br /&gt;
| Permission checking || &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module (included in DueDate)&lt;br /&gt;
|-&lt;br /&gt;
| Deadline-related actions || &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module (included in parent models)&lt;br /&gt;
|-&lt;br /&gt;
| Deadline type semantics || &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model&lt;br /&gt;
|-&lt;br /&gt;
| Core deadline data || &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Key improvements:&lt;br /&gt;
* '''Instance-based behavior''': Replaced class methods with instance methods&lt;br /&gt;
* '''Clear query methods''': Simple predicates like &amp;lt;code&amp;gt;upcoming?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;overdue?&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Scopes for common queries''': &amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
&lt;br /&gt;
  belongs_to :parent, polymorphic: true&lt;br /&gt;
  belongs_to :deadline_type&lt;br /&gt;
  validates :due_at, :deadline_type_id, :parent, presence: true&lt;br /&gt;
&lt;br /&gt;
  scope :upcoming, -&amp;gt; { where('due_at &amp;gt; ?', Time.current).order(:due_at) }&lt;br /&gt;
  scope :overdue, -&amp;gt; { where('due_at &amp;lt; ?', Time.current).order(:due_at) }&lt;br /&gt;
&lt;br /&gt;
  def overdue?&lt;br /&gt;
    due_at &amp;lt; Time.current&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def copy(to_assignment_id)&lt;br /&gt;
    new_due_date = dup&lt;br /&gt;
    new_due_date.parent = Assignment.find(to_assignment_id)&lt;br /&gt;
    new_due_date.save!&lt;br /&gt;
    new_due_date&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 4. DueDatePermissions Module ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Currently, permission checks are split into two disconnected layers:&lt;br /&gt;
&lt;br /&gt;
'''Role-based permissions''' (in participant_helpers.rb):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def participant_permissions(authorization)&lt;br /&gt;
  case authorization&lt;br /&gt;
  when 'reader' then { can_submit: false, can_review: true, can_take_quiz: true }&lt;br /&gt;
  # ...&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Deadline-based checks''' (in Assignment model):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def submission_allowed(topic_id = nil)&lt;br /&gt;
  check_condition('submission_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These two layers never interact, leading to inconsistencies.&lt;br /&gt;
&lt;br /&gt;
==== Proposed Solution ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module integrates both layers:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Permission Methods&lt;br /&gt;
|-&lt;br /&gt;
! Method !! What It Checks&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_submit?(participant)&amp;lt;/code&amp;gt; || Submission deadline is OK/Late AND participant can submit&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_review?(participant)&amp;lt;/code&amp;gt; || Review deadline is OK/Late AND participant can review&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_take_quiz?(participant)&amp;lt;/code&amp;gt; || Quiz deadline is OK/Late AND participant can take quiz&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_review_teammate?(participant)&amp;lt;/code&amp;gt; || Teammate review deadline is OK/Late AND participant can review&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;activity_permissible?(activity)&amp;lt;/code&amp;gt; || Generic checker for any activity&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;permission_status_for(action)&amp;lt;/code&amp;gt; || Returns 'OK', 'Late', or 'No' for display&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDatePermissions&lt;br /&gt;
  def can_submit?(participant = nil)&lt;br /&gt;
    return false unless submission_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_submit&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: submission_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Similar implementations for can_review?, can_take_quiz?, etc.&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 5. DueDateActions Module ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module provides a unified interface for models that own due dates (&amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt;). This centralizes the logic for querying upcoming deadlines and determining whether specific activities are currently allowed.&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Methods in DueDateActions&lt;br /&gt;
|-&lt;br /&gt;
! Method !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;activity_permissible?(activity)&amp;lt;/code&amp;gt; || Checks if an activity is permitted at the current time&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;submission_permissible?&amp;lt;/code&amp;gt; || Convenience wrapper for submission checking&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;review_permissible?&amp;lt;/code&amp;gt; || Convenience wrapper for review checking&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;next_due_date(topic_id)&amp;lt;/code&amp;gt; || Resolves the next applicable deadline&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;current_stage&amp;lt;/code&amp;gt; || Returns human-readable stage name (e.g., &amp;quot;review&amp;quot;, &amp;quot;submission&amp;quot;)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;has_topic_specific_deadlines?&amp;lt;/code&amp;gt; || Determines if assignment uses topic-specific deadlines&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;copy_due_dates_to(new_parent)&amp;lt;/code&amp;gt; || Utility for cloning due dates to another assignment&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Deadline Resolution Flow ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ How next_due_date Works&lt;br /&gt;
|-&lt;br /&gt;
! Step !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 1. Topic-specific check || If topic ID provided and assignment uses staggered deadlines, check topic-level deadlines first&lt;br /&gt;
|-&lt;br /&gt;
| 2. Assignment-level fallback || If no topic deadline exists, use nearest assignment-level deadline&lt;br /&gt;
|-&lt;br /&gt;
| 3. Permission evaluation || Delegate to DueDatePermissions to determine if action is allowed&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDateActions&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    current_deadline = next_due_date()&lt;br /&gt;
    return false unless current_deadline&lt;br /&gt;
    current_deadline.activity_permissible?(activity)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def submission_permissible?&lt;br /&gt;
    activity_permissible?(:submission)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def next_due_date(topic_id = nil)&lt;br /&gt;
    if topic_id &amp;amp;&amp;amp; has_topic_specific_deadlines?&lt;br /&gt;
      topic_deadline = due_dates.where(parent_id: topic_id, parent_type: 'SignUpTopic')&lt;br /&gt;
                                .upcoming.first&lt;br /&gt;
      return topic_deadline if topic_deadline&lt;br /&gt;
    end&lt;br /&gt;
    due_dates.upcoming.first&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Implementation Summary ==&lt;br /&gt;
&lt;br /&gt;
=== Models Created/Modified ===&lt;br /&gt;
&lt;br /&gt;
* '''DeadlineType''' — Canonical deadline type definitions with semantic helper methods&lt;br /&gt;
* '''DeadlineRight''' — Permission state model (OK/Late/No) with comparison methods&lt;br /&gt;
* '''DueDate''' — Refactored with instance methods, scopes, and permission checking&lt;br /&gt;
* '''TopicDueDate''' — STI subclass for topic-specific deadlines&lt;br /&gt;
* '''Assignment''' — Includes DueDateActions mixin&lt;br /&gt;
* '''SignUpTopic''' — Includes DueDateActions mixin&lt;br /&gt;
&lt;br /&gt;
=== Modules Created ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions''' — Permission checking combining deadline and role constraints&lt;br /&gt;
* '''DueDateActions''' — Deadline-related operations for parent models&lt;br /&gt;
&lt;br /&gt;
=== Key Improvements ===&lt;br /&gt;
&lt;br /&gt;
# '''Separation of Concerns''': Permission logic separated into dedicated modules&lt;br /&gt;
# '''Instance Methods''': Replaced class methods with testable instance methods&lt;br /&gt;
# '''Polymorphic Design''': Single DueDate model serves both Assignment and SignUpTopic&lt;br /&gt;
# '''Unified Permissions''': Role-based and time-based checks combined in one interface&lt;br /&gt;
# '''Data Integrity''': Removed duplicate deadline types, enforced foreign key constraints&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
The test suite includes comprehensive RSpec tests for the DueDate model using nested contexts, edge case coverage, and error handling.&lt;br /&gt;
&lt;br /&gt;
=== Current Coverage ===&lt;br /&gt;
&lt;br /&gt;
* '''Validations''': Required fields (parent, due_at, deadline_type_id), round validation, error messages&lt;br /&gt;
* '''Scopes''': &amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt; with edge cases&lt;br /&gt;
* '''Instance Methods''': &amp;lt;code&amp;gt;#overdue?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#upcoming?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#set&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#copy&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#deadline_type_name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#last_deadline?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#&amp;lt;=&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Class Methods''': &amp;lt;code&amp;gt;.sort_due_dates&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.any_future_due_dates?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.copy&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Callbacks''': &amp;lt;code&amp;gt;before_save :set_default_round&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
Tests follow RSpec best practices with nested contexts:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
describe 'validations' do&lt;br /&gt;
  context 'when parent is missing' do&lt;br /&gt;
    it 'is invalid' do&lt;br /&gt;
      expect(due_date).to be_invalid&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'has error on parent field' do&lt;br /&gt;
      due_date.valid?&lt;br /&gt;
      expect(due_date.errors[:parent]).to include('must exist')&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when round validation' do&lt;br /&gt;
    context 'with round value of 0' do&lt;br /&gt;
      it 'is invalid' do&lt;br /&gt;
        expect(due_date).to be_invalid&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'with nil round' do&lt;br /&gt;
      it 'sets default round to 1' do&lt;br /&gt;
        expect(due_date.round).to eq(1)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Planned Future Tests ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions module''': Test &amp;lt;code&amp;gt;can_submit?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;can_review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;can_take_quiz?&amp;lt;/code&amp;gt; with participant parameters&lt;br /&gt;
* '''DueDateActions module''': Test &amp;lt;code&amp;gt;activity_permissible?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt; in Assignment context&lt;br /&gt;
* '''Integration tests''': Combined role-based and time-based permission logic&lt;br /&gt;
&lt;br /&gt;
=== Test Metrics ===&lt;br /&gt;
&lt;br /&gt;
* '''Total examples''': 438 tests&lt;br /&gt;
* '''DueDate model''': 60+ examples&lt;br /&gt;
* '''Code coverage''': 100% of DueDate methods&lt;br /&gt;
* '''Execution time''': ~12 seconds&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
=== Demo Link ===&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add deadline notification system using the new permission framework&lt;br /&gt;
* Implement deadline templates for common assignment patterns&lt;br /&gt;
* Add API endpoints for mobile app integration&lt;br /&gt;
* Create admin dashboard for monitoring deadline compliance across courses&lt;br /&gt;
* Add automated deadline extension workflows based on configurable rules&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167215</id>
		<title>CSC/ECE 517 Fall 2025 - E2566. Finish DueDates</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167215"/>
		<updated>2025-12-02T15:31:48Z</updated>

		<summary type="html">&lt;p&gt;Skim253: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
This project aims to complete the implementation of the DueDate system in Expertiza. The current implementation consists mostly of class methods and lacks proper definition of different deadline types and permission checking functionality. This redesign will create a more robust, readable, and maintainable due date system.&lt;br /&gt;
&lt;br /&gt;
=== Existing Components ===&lt;br /&gt;
* &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model with polymorphic parent association (Assignment/SignUpTopic)&lt;br /&gt;
* &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; subclasses&lt;br /&gt;
* Basic CRUD operations (within &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AssignmentForm&amp;lt;/code&amp;gt;) and sorting functionality (&amp;lt;code&amp;gt;deadline_sort&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Database schema with &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt; field&lt;br /&gt;
&lt;br /&gt;
=== Current Behavior ===&lt;br /&gt;
&lt;br /&gt;
====Admin's View====&lt;br /&gt;
[[File:Duedates.png|800px]]&lt;br /&gt;
&lt;br /&gt;
When editing an assignment, due dates can be modified under the Due Dates tab. Each row contains the following columns: &amp;lt;code&amp;gt;Date &amp;amp; Time&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Use Date Updater&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Submission Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Teammate Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Meta-Review Allowed&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Reminder&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:Assignments-CurrentStage.png|800px]]&lt;br /&gt;
&lt;br /&gt;
In the Assignments tab, the current stage and stage deadline of each assignment is displayed.&lt;br /&gt;
&lt;br /&gt;
[[File:Due-date-topic-instructor.png|800px]]&lt;br /&gt;
&lt;br /&gt;
[[File:Due-date-topic-instructor-2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
In the Topics subtab, due dates for each topic are displayed.&lt;br /&gt;
&lt;br /&gt;
====Student's View====&lt;br /&gt;
[[File:Student-assignments.png|800px]]&lt;br /&gt;
&lt;br /&gt;
Students can view due date information under &amp;lt;code&amp;gt;Current State&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Stage Deadline&amp;lt;/code&amp;gt;. The actions available to students are determined by the current DueDate status.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-not-over.png|800px]]&lt;br /&gt;
&lt;br /&gt;
During the submission period, students can access the &amp;lt;code&amp;gt;Submit a hyperlink&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Submit a file&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-over.png|800px]]&lt;br /&gt;
&lt;br /&gt;
Once the due date has passed, students cannot submit either a hyperlink or a file.&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
&lt;br /&gt;
Currently, there are some glaring issues with how due_dates was created in the first place, including redundancy and lack of modularity.&lt;br /&gt;
&lt;br /&gt;
'''Modeling &amp;amp; Structural Issues'''&lt;br /&gt;
# No dedicated &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to define different kinds of deadlines. The existing &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; class only serves as an association for &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; models, and is never referenced in the current &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; implementation.&lt;br /&gt;
# Duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table, which may lead to potential bugs.&lt;br /&gt;
# Incomplete deadline type definitions — &amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt; need to be added.&lt;br /&gt;
# No clear separation of concerns — data persistence, business logic, and permission logic are mixed in a single model.&lt;br /&gt;
&lt;br /&gt;
'''Permission &amp;amp; Behavior Issues'''&lt;br /&gt;
# Overuse of class methods making the code difficult to maintain.&lt;br /&gt;
# Missing permission checking logic for user actions that combines both role-based and time-based constraints.&lt;br /&gt;
&lt;br /&gt;
=== Tasks Identified ===&lt;br /&gt;
&lt;br /&gt;
'''Modeling &amp;amp; Structural'''&lt;br /&gt;
# Refactor the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model to separate persistence, business logic, and permission logic into distinct layers.&lt;br /&gt;
# Implement reusable mixins (&amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;) to handle permission evaluation and deadline-related operations.&lt;br /&gt;
# Introduce instance-level methods (e.g., &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;) to replace class-level utilities and improve testability.&lt;br /&gt;
&lt;br /&gt;
'''Permission &amp;amp; Behavior'''&lt;br /&gt;
# Integrate role-based and time-based permission checks within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; to ensure consistent access control.&lt;br /&gt;
# Redesign the &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.&lt;br /&gt;
# Add missing deadline types (&amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt;) and remove duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries for data integrity.&lt;br /&gt;
&lt;br /&gt;
== Proposed Solution ==&lt;br /&gt;
&lt;br /&gt;
The proposed solution restructures the due date system by separating concerns across dedicated models and mixin modules. Deadline definitions (&amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt;), core deadline data (&amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;), and behavioral logic (&amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;) are isolated so each component has a single clear responsibility. This modular design improves readability, reduces coupling, and makes deadline-related behavior easier to extend and maintain.&lt;br /&gt;
&lt;br /&gt;
=== Architecture Overview ===&lt;br /&gt;
&lt;br /&gt;
[[File:Duedate-diagram.png|800px]]&lt;br /&gt;
&lt;br /&gt;
This diagram visualizes the redesigned architecture with five distinct layers:&lt;br /&gt;
&lt;br /&gt;
* '''Layer 1 (Blue)''': Parent models (Assignment, SignUpTopic) that own due dates&lt;br /&gt;
* '''Layer 2 (Orange)''': DueDateActions module providing action-level queries&lt;br /&gt;
* '''Layer 3 (Red)''': DueDate model managing core deadline data&lt;br /&gt;
* '''Layer 4 (Orange)''': DueDatePermissions module evaluating permissions&lt;br /&gt;
* '''Layer 5 (Green)''': Reference data models (DeadlineType, DeadlineRight, Participant)&lt;br /&gt;
&lt;br /&gt;
The flow works as follows: Parent models include DueDateActions, which queries DueDate objects. Each DueDate includes DueDatePermissions to evaluate whether actions are allowed based on DeadlineRight states and Participant role flags.&lt;br /&gt;
&lt;br /&gt;
=== 1. DeadlineType Model ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Although the current codebase includes a &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model, it only serves as a passive lookup table. In practice, most parts of the system still rely on hard-coded numeric IDs or manual lookups:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Example of current usage&lt;br /&gt;
deadline_type_id = DeadlineType.find_by(name: 'review').id&lt;br /&gt;
if due_date.deadline_type_id == deadline_type_id&lt;br /&gt;
  # Perform review-related logic&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This leads to several structural issues:&lt;br /&gt;
* Inconsistent references (some use IDs, others strings)&lt;br /&gt;
* Tight coupling between &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
* Lack of semantic helper methods (e.g., &amp;lt;code&amp;gt;review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;submission?&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Data duplication and integrity risks (two &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; rows)&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; Table Structure&lt;br /&gt;
|- &lt;br /&gt;
! Column !! Type !! Description&lt;br /&gt;
|-&lt;br /&gt;
| id || BIGINT || Primary key, referenced by &amp;lt;code&amp;gt;due_dates.deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| name || VARCHAR(255) || Unique symbolic name (e.g., &amp;lt;code&amp;gt;submission&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| description || TEXT || Human-readable explanation of the deadline category&lt;br /&gt;
|-&lt;br /&gt;
| created_at || TIMESTAMP || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| updated_at || TIMESTAMP || Last update timestamp&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Deadline Type Definitions ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Canonical Deadline Type Entries&lt;br /&gt;
|-&lt;br /&gt;
! ID !! Name !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 1 || submission || Student submission deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 2 || review || Peer review deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 3 || teammate_review || Team member evaluation deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 5 || metareview || Meta-review deadlines (backward compatibility)&lt;br /&gt;
|-&lt;br /&gt;
| 6 || drop_topic || Topic drop deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 7 || signup || Topic signup deadlines&lt;br /&gt;
|-&lt;br /&gt;
| 8 || team_formation || Team formation deadlines (duplicate cleaned)&lt;br /&gt;
|-&lt;br /&gt;
| 11 || quiz || Quiz completion deadlines&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Implementation ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineType &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
  validates :description, presence: true&lt;br /&gt;
  has_many :due_dates, foreign_key: :deadline_type_id, dependent: :restrict_with_exception&lt;br /&gt;
&lt;br /&gt;
  # Semantic helper methods&lt;br /&gt;
  def submission?&lt;br /&gt;
    name == 'submission'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def review?&lt;br /&gt;
    name == 'review'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Additional helpers: teammate_review?, quiz?, team_formation?, signup?, drop_topic?&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 2. DeadlineRight Model ===&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model represents the permission level for a specific action at a given deadline:&lt;br /&gt;
&lt;br /&gt;
* '''OK''': Action is allowed without penalty&lt;br /&gt;
* '''Late''': Action is allowed but marked as late&lt;br /&gt;
* '''No''': Action is not permitted&lt;br /&gt;
&lt;br /&gt;
These values are referenced by &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; through fields such as &amp;lt;code&amp;gt;submission_allowed_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review_allowed_id&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;quiz_allowed_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_rights&amp;lt;/code&amp;gt; Table Structure&lt;br /&gt;
|-&lt;br /&gt;
! Column !! Type !! Description&lt;br /&gt;
|-&lt;br /&gt;
| id || BIGINT || Primary key, referenced by &amp;lt;code&amp;gt;due_dates.*_allowed_id&amp;lt;/code&amp;gt; fields&lt;br /&gt;
|-&lt;br /&gt;
| name || VARCHAR(255) || Unique permission name (&amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;)&lt;br /&gt;
|-&lt;br /&gt;
| created_at || TIMESTAMP || Creation timestamp&lt;br /&gt;
|-&lt;br /&gt;
| updated_at || TIMESTAMP || Last update timestamp&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Implementation ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineRight &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
&lt;br /&gt;
  def allows_action?&lt;br /&gt;
    %w[OK Late].include?(name)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def denies_action?&lt;br /&gt;
    name == 'No'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def allows_with_penalty?&lt;br /&gt;
    name == 'Late'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def allows_without_penalty?&lt;br /&gt;
    name == 'OK'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def permission_level&lt;br /&gt;
    case name&lt;br /&gt;
    when 'No' then 0&lt;br /&gt;
    when 'Late' then 1&lt;br /&gt;
    when 'OK' then 2&lt;br /&gt;
    else -1&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 3. DueDate Model Refactoring ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model currently mixes persistence logic, deadline calculation, and permission checking, violating the Single Responsibility Principle. Most of its logic is implemented as class methods, which makes testing and extension difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Old design: class method that mixes concerns&lt;br /&gt;
def self.copy(old_assignment_id, new_assignment_id)&lt;br /&gt;
  duedates = where(parent_id: old_assignment_id)&lt;br /&gt;
  duedates.each do |orig_due_date|&lt;br /&gt;
    new_due_date = orig_due_date.dup&lt;br /&gt;
    new_due_date.parent_id = new_assignment_id&lt;br /&gt;
    new_due_date.save&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Improved Design ====&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model is focused on representing a single deadline entry. Behavior has been extracted into appropriate modules:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Responsibility Distribution After Refactoring&lt;br /&gt;
|-&lt;br /&gt;
! Concern !! Where It Lives&lt;br /&gt;
|-&lt;br /&gt;
| Permission checking || &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module (included in DueDate)&lt;br /&gt;
|-&lt;br /&gt;
| Deadline-related actions || &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module (included in parent models)&lt;br /&gt;
|-&lt;br /&gt;
| Deadline type semantics || &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model&lt;br /&gt;
|-&lt;br /&gt;
| Core deadline data || &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
Key improvements:&lt;br /&gt;
* '''Instance-based behavior''': Replaced class methods with instance methods&lt;br /&gt;
* '''Clear query methods''': Simple predicates like &amp;lt;code&amp;gt;upcoming?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;overdue?&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Scopes for common queries''': &amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
&lt;br /&gt;
  belongs_to :parent, polymorphic: true&lt;br /&gt;
  belongs_to :deadline_type&lt;br /&gt;
  validates :due_at, :deadline_type_id, :parent, presence: true&lt;br /&gt;
&lt;br /&gt;
  scope :upcoming, -&amp;gt; { where('due_at &amp;gt; ?', Time.current).order(:due_at) }&lt;br /&gt;
  scope :overdue, -&amp;gt; { where('due_at &amp;lt; ?', Time.current).order(:due_at) }&lt;br /&gt;
&lt;br /&gt;
  def overdue?&lt;br /&gt;
    due_at &amp;lt; Time.current&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def copy(to_assignment_id)&lt;br /&gt;
    new_due_date = dup&lt;br /&gt;
    new_due_date.parent = Assignment.find(to_assignment_id)&lt;br /&gt;
    new_due_date.save!&lt;br /&gt;
    new_due_date&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 4. DueDatePermissions Module ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Currently, permission checks are split into two disconnected layers:&lt;br /&gt;
&lt;br /&gt;
'''Role-based permissions''' (in participant_helpers.rb):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def participant_permissions(authorization)&lt;br /&gt;
  case authorization&lt;br /&gt;
  when 'reader' then { can_submit: false, can_review: true, can_take_quiz: true }&lt;br /&gt;
  # ...&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Deadline-based checks''' (in Assignment model):&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def submission_allowed(topic_id = nil)&lt;br /&gt;
  check_condition('submission_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These two layers never interact, leading to inconsistencies.&lt;br /&gt;
&lt;br /&gt;
==== Proposed Solution ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module integrates both layers:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Permission Methods&lt;br /&gt;
|-&lt;br /&gt;
! Method !! What It Checks&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_submit?(participant)&amp;lt;/code&amp;gt; || Submission deadline is OK/Late AND participant can submit&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_review?(participant)&amp;lt;/code&amp;gt; || Review deadline is OK/Late AND participant can review&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_take_quiz?(participant)&amp;lt;/code&amp;gt; || Quiz deadline is OK/Late AND participant can take quiz&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;can_review_teammate?(participant)&amp;lt;/code&amp;gt; || Teammate review deadline is OK/Late AND participant can review&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;activity_permissible?(activity)&amp;lt;/code&amp;gt; || Generic checker for any activity&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;permission_status_for(action)&amp;lt;/code&amp;gt; || Returns 'OK', 'Late', or 'No' for display&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDatePermissions&lt;br /&gt;
  def can_submit?(participant = nil)&lt;br /&gt;
    return false unless submission_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_submit&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: submission_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Similar implementations for can_review?, can_take_quiz?, etc.&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 5. DueDateActions Module ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module provides a unified interface for models that own due dates (&amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt;). This centralizes the logic for querying upcoming deadlines and determining whether specific activities are currently allowed.&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Methods in DueDateActions&lt;br /&gt;
|-&lt;br /&gt;
! Method !! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;activity_permissible?(activity)&amp;lt;/code&amp;gt; || Checks if an activity is permitted at the current time&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;submission_permissible?&amp;lt;/code&amp;gt; || Convenience wrapper for submission checking&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;review_permissible?&amp;lt;/code&amp;gt; || Convenience wrapper for review checking&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;next_due_date(topic_id)&amp;lt;/code&amp;gt; || Resolves the next applicable deadline&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;current_stage&amp;lt;/code&amp;gt; || Returns human-readable stage name (e.g., &amp;quot;review&amp;quot;, &amp;quot;submission&amp;quot;)&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;has_topic_specific_deadlines?&amp;lt;/code&amp;gt; || Determines if assignment uses topic-specific deadlines&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;copy_due_dates_to(new_parent)&amp;lt;/code&amp;gt; || Utility for cloning due dates to another assignment&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==== Deadline Resolution Flow ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ How next_due_date Works&lt;br /&gt;
|-&lt;br /&gt;
! Step !! Description&lt;br /&gt;
|-&lt;br /&gt;
| 1. Topic-specific check || If topic ID provided and assignment uses staggered deadlines, check topic-level deadlines first&lt;br /&gt;
|-&lt;br /&gt;
| 2. Assignment-level fallback || If no topic deadline exists, use nearest assignment-level deadline&lt;br /&gt;
|-&lt;br /&gt;
| 3. Permission evaluation || Delegate to DueDatePermissions to determine if action is allowed&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDateActions&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    current_deadline = next_due_date()&lt;br /&gt;
    return false unless current_deadline&lt;br /&gt;
    current_deadline.activity_permissible?(activity)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def submission_permissible?&lt;br /&gt;
    activity_permissible?(:submission)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def next_due_date(topic_id = nil)&lt;br /&gt;
    if topic_id &amp;amp;&amp;amp; has_topic_specific_deadlines?&lt;br /&gt;
      topic_deadline = due_dates.where(parent_id: topic_id, parent_type: 'SignUpTopic')&lt;br /&gt;
                                .upcoming.first&lt;br /&gt;
      return topic_deadline if topic_deadline&lt;br /&gt;
    end&lt;br /&gt;
    due_dates.upcoming.first&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Implementation Summary ==&lt;br /&gt;
&lt;br /&gt;
=== Models Created/Modified ===&lt;br /&gt;
&lt;br /&gt;
* '''DeadlineType''' — Canonical deadline type definitions with semantic helper methods&lt;br /&gt;
* '''DeadlineRight''' — Permission state model (OK/Late/No) with comparison methods&lt;br /&gt;
* '''DueDate''' — Refactored with instance methods, scopes, and permission checking&lt;br /&gt;
* '''TopicDueDate''' — STI subclass for topic-specific deadlines&lt;br /&gt;
* '''Assignment''' — Includes DueDateActions mixin&lt;br /&gt;
* '''SignUpTopic''' — Includes DueDateActions mixin&lt;br /&gt;
&lt;br /&gt;
=== Modules Created ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions''' — Permission checking combining deadline and role constraints&lt;br /&gt;
* '''DueDateActions''' — Deadline-related operations for parent models&lt;br /&gt;
&lt;br /&gt;
=== Key Improvements ===&lt;br /&gt;
&lt;br /&gt;
# '''Separation of Concerns''': Permission logic separated into dedicated modules&lt;br /&gt;
# '''Instance Methods''': Replaced class methods with testable instance methods&lt;br /&gt;
# '''Polymorphic Design''': Single DueDate model serves both Assignment and SignUpTopic&lt;br /&gt;
# '''Unified Permissions''': Role-based and time-based checks combined in one interface&lt;br /&gt;
# '''Data Integrity''': Removed duplicate deadline types, enforced foreign key constraints&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
The test suite includes comprehensive RSpec tests for the DueDate model using nested contexts, edge case coverage, and error handling.&lt;br /&gt;
&lt;br /&gt;
=== Current Coverage ===&lt;br /&gt;
&lt;br /&gt;
* '''Validations''': Required fields (parent, due_at, deadline_type_id), round validation, error messages&lt;br /&gt;
* '''Scopes''': &amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt; with edge cases&lt;br /&gt;
* '''Instance Methods''': &amp;lt;code&amp;gt;#overdue?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#upcoming?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#set&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#copy&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#deadline_type_name&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#last_deadline?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;#&amp;lt;=&amp;gt;&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Class Methods''': &amp;lt;code&amp;gt;.sort_due_dates&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.any_future_due_dates?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;.copy&amp;lt;/code&amp;gt;&lt;br /&gt;
* '''Callbacks''': &amp;lt;code&amp;gt;before_save :set_default_round&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
Tests follow RSpec best practices with nested contexts:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
describe 'validations' do&lt;br /&gt;
  context 'when parent is missing' do&lt;br /&gt;
    it 'is invalid' do&lt;br /&gt;
      expect(due_date).to be_invalid&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'has error on parent field' do&lt;br /&gt;
      due_date.valid?&lt;br /&gt;
      expect(due_date.errors[:parent]).to include('must exist')&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when round validation' do&lt;br /&gt;
    context 'with round value of 0' do&lt;br /&gt;
      it 'is invalid' do&lt;br /&gt;
        expect(due_date).to be_invalid&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'with nil round' do&lt;br /&gt;
      it 'sets default round to 1' do&lt;br /&gt;
        expect(due_date.round).to eq(1)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Planned Future Tests ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions module''': Test &amp;lt;code&amp;gt;can_submit?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;can_review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;can_take_quiz?&amp;lt;/code&amp;gt; with participant parameters&lt;br /&gt;
* '''DueDateActions module''': Test &amp;lt;code&amp;gt;activity_permissible?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt; in Assignment context&lt;br /&gt;
* '''Integration tests''': Combined role-based and time-based permission logic&lt;br /&gt;
&lt;br /&gt;
=== Test Metrics ===&lt;br /&gt;
&lt;br /&gt;
* '''Total examples''': 438 tests&lt;br /&gt;
* '''DueDate model''': 60+ examples&lt;br /&gt;
* '''Code coverage''': 100% of DueDate methods&lt;br /&gt;
* '''Execution time''': ~12 seconds&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
=== Demo Link ===&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add deadline notification system using the new permission framework&lt;br /&gt;
* Implement deadline templates for common assignment patterns&lt;br /&gt;
* Add API endpoints for mobile app integration&lt;br /&gt;
* Create admin dashboard for monitoring deadline compliance across courses&lt;br /&gt;
* Add automated deadline extension workflows based on configurable rules&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167208</id>
		<title>CSC/ECE 517 Fall 2025 - E2566. Finish DueDates</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167208"/>
		<updated>2025-12-02T05:24:23Z</updated>

		<summary type="html">&lt;p&gt;Skim253: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
This project aims to complete the implementation of the DueDate system in Expertiza. The current implementation consists mostly of class methods and lacks proper definition of different deadline types and permission checking functionality. This redesign will create a more robust, readable, and maintainable due date system.&lt;br /&gt;
&lt;br /&gt;
=== Existing Components ===&lt;br /&gt;
* &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model with polymorphic parent association (Assignment/SignUpTopic)&lt;br /&gt;
* &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; subclasses&lt;br /&gt;
* Basic CRUD operations (within &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AssignmentForm&amp;lt;/code&amp;gt;) and sorting functionality (&amp;lt;code&amp;gt;deadline_sort&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Database schema with &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt; field&lt;br /&gt;
&lt;br /&gt;
=== Current Behavior ===&lt;br /&gt;
&lt;br /&gt;
====Instructor's View====&lt;br /&gt;
[[File:Duedates.png|800px]]&lt;br /&gt;
&lt;br /&gt;
* When editing an assignment, due dates can be modified under the Due Dates tab.&lt;br /&gt;
* Each row contains the following columns: &amp;lt;code&amp;gt;Date &amp;amp; Time&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Use Date Updater&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Submission Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Teammate Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Meta-Review Allowed&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Reminder&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:Assignments-CurrentStage.png|800px]]&lt;br /&gt;
&lt;br /&gt;
* In the Assignments tab, the ''DueDate'' of each assignment is displayed as Current Stage and Stage Deadline.&lt;br /&gt;
&lt;br /&gt;
[[File:Due-date-topic-instructor.png|800px]]&lt;br /&gt;
&lt;br /&gt;
[[File:Due-date-topic-instructor-2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
* In the Topics subtab, the ''DueDate'' of each topics are displayed.&lt;br /&gt;
&lt;br /&gt;
====Student's View====&lt;br /&gt;
[[File:Student-assignments.png|800px]]&lt;br /&gt;
* Students can view the due date information under &amp;lt;code&amp;gt;Current State&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Stage Deadline&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
* The actions available to students are determined by the current DueDate status.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-not-over.png|800px]]&lt;br /&gt;
* For example, during the submission period, students can access the &amp;lt;code&amp;gt;Submit a hyperlink&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Submit a file&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-over.png|800px]]&lt;br /&gt;
* Once the due date has passed, students cannot submit either a hyperlink or a file.&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Currently, there are some glaring issues with how due_dates was created in the first place, including redundancy and lack of modularity.&lt;br /&gt;
&lt;br /&gt;
Modeling &amp;amp; Structural Issues&lt;br /&gt;
# No dedicated &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to define different kinds of deadlines. The existing &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; class only serves as an association for &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; models, and is never referenced in the current &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; implementation.&lt;br /&gt;
# Duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table, which may lead to potential bugs.&lt;br /&gt;
# Incomplete deadline type definitions - &amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt; need to be added.&lt;br /&gt;
# No clear separation of concerns - data persistence, business logic, and permission logic are mixed in a single model.&lt;br /&gt;
&lt;br /&gt;
Permission &amp;amp; Behavior Issues&lt;br /&gt;
# Overuse of class methods making the code difficult to maintain.&lt;br /&gt;
# Missing permission checking logic for user actions.&lt;br /&gt;
&lt;br /&gt;
=== Tasks Identified ===&lt;br /&gt;
&lt;br /&gt;
Modeling &amp;amp; Structural Issues&lt;br /&gt;
# Refactor the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model to separate persistence, business logic, and permission logic into distinct layers.&lt;br /&gt;
# Implement reusable mixins (e.g., &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;) to handle permission evaluation and deadline-related operations.&lt;br /&gt;
# Introduce instance-level methods (e.g., &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;) to replace class-level utilities and improve testability.&lt;br /&gt;
&lt;br /&gt;
Permission &amp;amp; Behavior Issues&lt;br /&gt;
# Integrate role-based and time-based permission checks within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; to ensure consistent access control.&lt;br /&gt;
# Redesign the &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.&lt;br /&gt;
# Add missing deadline types (&amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt;) and remove duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries for data integrity.&lt;br /&gt;
&lt;br /&gt;
== Proposed Solution ==&lt;br /&gt;
The proposed solution restructures the due date system by separating concerns across dedicated models and mixin modules.&lt;br /&gt;
Deadline definitions (&amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt;), core deadline data (&amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;), and behavioral logic (&amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;) are isolated so each component has a single clear responsibility.&lt;br /&gt;
This modular design improves readability, reduces coupling, and makes deadline-related behavior easier to extend and maintain.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Duedate-diagram.png|800px]]&lt;br /&gt;
&lt;br /&gt;
This diagram visualizes the redesigned architecture: blue/red/green indicate Rails models, orange indicates modules. &lt;br /&gt;
Parent models call DueDateActions, which manages DueDate, while DueDatePermissions checks user rights using reference models. &lt;br /&gt;
&lt;br /&gt;
=== DeadlineType Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Although the current codebase includes a &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model, it only serves as a passive lookup table for associations with &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt;.  &lt;br /&gt;
In practice, most parts of the system still rely on hard-coded numeric IDs or manual lookups such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Example of current usage&lt;br /&gt;
deadline_type_id = DeadlineType.find_by(name: 'review').id&lt;br /&gt;
if due_date.deadline_type_id == deadline_type_id&lt;br /&gt;
  # Perform review-related logic&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of using the model as an active domain object, &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; directly compares IDs or string literals.  &lt;br /&gt;
This leads to several structural issues:&lt;br /&gt;
&lt;br /&gt;
* Inconsistent references (some use IDs, others strings)&lt;br /&gt;
* Tight coupling between &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
* Lack of helper semantics (e.g., &amp;lt;code&amp;gt;is_review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;is_submission?&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Data duplication and integrity risks (two &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; rows)&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; will serve as a source of truth, defining all valid deadline types and providing meaningful interfaces.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table defines all canonical deadline categories used by the system.&lt;br /&gt;
Each row represents one distinct deadline type, and every &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; record must reference one of these entries through &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Below is a structural description of the table:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; Table Structure                                                    &lt;br /&gt;
|- &lt;br /&gt;
! Column                                                                                         &lt;br /&gt;
! Type                                                                                           &lt;br /&gt;
! Description                                                                                    &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| id                                                                                               &lt;br /&gt;
| BIGINT (Primary Key)                                                                             &lt;br /&gt;
| Unique identifier for each deadline type; referenced by &amp;lt;code&amp;gt;due_dates.deadline_type_id&amp;lt;/code&amp;gt;. &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| name                                                                                             &lt;br /&gt;
| VARCHAR(255)                                                                                     &lt;br /&gt;
| Symbolic name (e.g., &amp;lt;code&amp;gt;submission&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review&amp;lt;/code&amp;gt;); must be unique.              &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| description                                                                                      &lt;br /&gt;
| TEXT                                                                                             &lt;br /&gt;
| Human-readable explanation of the deadline category.                                             &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| created_at                                                                                       &lt;br /&gt;
| TIMESTAMP                                                                                        &lt;br /&gt;
| Time when the row was created.                                                                   &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| updated_at                                                                                       &lt;br /&gt;
| TIMESTAMP                                                                                        &lt;br /&gt;
| Time when the row was last updated.                                                              &lt;br /&gt;
|}                                                                                                &lt;br /&gt;
&lt;br /&gt;
This schema ensures that:&lt;br /&gt;
&lt;br /&gt;
* Each deadline type has a unique name and description.&lt;br /&gt;
* No duplicate entries can exist (e.g., the prior duplicated &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; was cleaned).&lt;br /&gt;
* All &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; rows reference this master table through &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
* The &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; column acts as the authoritative key used throughout permissions and due-date logic.&lt;br /&gt;
&lt;br /&gt;
Migrated constraints also ensure that &amp;lt;code&amp;gt;due_dates.deadline_type_id&amp;lt;/code&amp;gt; is consistently stored as &amp;lt;code&amp;gt;BIGINT&amp;lt;/code&amp;gt;, allowing a long-term stable keyspace.&lt;br /&gt;
&lt;br /&gt;
==== Deadline Type Definitions ====&lt;br /&gt;
&lt;br /&gt;
Because each row in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; is referenced by &amp;lt;code&amp;gt;DueDate.deadline_type_id&amp;lt;/code&amp;gt;, the following definitions map directly to the schema above.&lt;br /&gt;
&lt;br /&gt;
Each ID corresponds exactly to the &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; field in the table and serves as the system-wide canonical reference for that deadline category.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Canonical Deadline Type Entries                                       &lt;br /&gt;
|-&lt;br /&gt;
! ID                                                                    &lt;br /&gt;
! Name                                                                  &lt;br /&gt;
! Description                                                           &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 1                                                                       &lt;br /&gt;
| submission                                                              &lt;br /&gt;
| Student submission deadlines                                            &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 2                                                                       &lt;br /&gt;
| review                                                                  &lt;br /&gt;
| Peer review deadlines                                                   &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 3                                                                       &lt;br /&gt;
| teammate_review                                                         &lt;br /&gt;
| Team member evaluation deadlines                                        &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 5                                                                       &lt;br /&gt;
| metareview                                                              &lt;br /&gt;
| Meta-review deadlines (kept for backward compatibility)                 &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 6                                                                       &lt;br /&gt;
| drop_topic                                                              &lt;br /&gt;
| Topic drop deadlines                                                    &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 7                                                                       &lt;br /&gt;
| signup                                                                  &lt;br /&gt;
| Topic signup deadlines                                                  &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 8                                                                       &lt;br /&gt;
| team_formation                                                          &lt;br /&gt;
| Team formation deadlines (duplicate entries cleaned; ID 8 is canonical) &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 11                                                                      &lt;br /&gt;
| quiz                                                                    &lt;br /&gt;
| Quiz completion deadlines                                               &lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
These definitions are seeded during migration and act as the source of truth for all deadline-related logic within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;.&lt;br /&gt;
By linking each &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; to one of these entries through &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;, the system standardizes behavior and eliminates inconsistent ID or string comparisons from earlier implementations.&lt;br /&gt;
&lt;br /&gt;
==== DeadlineType Model Implementation ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineType &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
  validates :description, presence: true&lt;br /&gt;
&lt;br /&gt;
  has_many :due_dates, foreign_key: :deadline_type_id, dependent: :restrict_with_exception&lt;br /&gt;
&lt;br /&gt;
  # Semantic helper methods for deadline type identification&lt;br /&gt;
  def submission?&lt;br /&gt;
    name == 'submission'&lt;br /&gt;
  end&lt;br /&gt;
  # same code for review, teammate_review, quiz, team_formation, signup, drop_topic   &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DeadlineRight Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model represents the permission level for a specific action at a given deadline.&lt;br /&gt;
It defines three states: &lt;br /&gt;
* &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt; : Action is allowed without penalty&lt;br /&gt;
* &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt; : Action is allowed but marked as late&lt;br /&gt;
* &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt; : Action is not permitted&lt;br /&gt;
&lt;br /&gt;
These values are referenced by &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; through fields such as &amp;lt;code&amp;gt;submission_allowed_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review_allowed_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz_allowed_id&amp;lt;/code&amp;gt;, and others.&lt;br /&gt;
This allows the system to determine user permissions by combining:&lt;br /&gt;
&lt;br /&gt;
# The current deadline’s allowed state&lt;br /&gt;
# The participant’s role-based ability (submit, review, take quiz)&lt;br /&gt;
&lt;br /&gt;
This combination logic is implemented in &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
The &amp;lt;code&amp;gt;deadline_rights&amp;lt;/code&amp;gt; table stores the canonical permission states used across the entire due-date system.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_rights&amp;lt;/code&amp;gt; Table Structure                                                         &lt;br /&gt;
|-&lt;br /&gt;
! Column                                                                                               &lt;br /&gt;
! Type                                                                                                 &lt;br /&gt;
! Description                                                                                          &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| id                                                                                                     &lt;br /&gt;
| BIGINT (Primary Key)                                                                                  &lt;br /&gt;
| Unique identifier for each permission state. Referenced by &amp;lt;code&amp;gt;due_dates.*_allowed_id&amp;lt;/code&amp;gt; fields. &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| name                                                                                                   &lt;br /&gt;
| VARCHAR(255)                                                                                           &lt;br /&gt;
| Symbolic permission name (&amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;). Must be unique.        &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| description                                                                                            &lt;br /&gt;
| TEXT                                                                                                   &lt;br /&gt;
| Human-readable explanation of what the permission state means.                                         &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| created_at                                                                                             &lt;br /&gt;
| TIMESTAMP                                                                                              &lt;br /&gt;
| Timestamp when the entry was created.                                                                  &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| updated_at                                                                                             &lt;br /&gt;
| TIMESTAMP                                                                                              &lt;br /&gt;
| Timestamp when the entry was last updated.                                                             &lt;br /&gt;
|}  &lt;br /&gt;
&lt;br /&gt;
==== DeadlineRight Model ====&lt;br /&gt;
Instead of embedding permission logic inside &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;, the &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model defines the meaning of each permission state.&lt;br /&gt;
This removes magic strings (&amp;lt;code&amp;gt;&amp;quot;OK&amp;quot;&amp;lt;/code&amp;gt;) and numeric comparisons and replaces them with expressive model-level semantics.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Semantic Meaning Provided by &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt;                    &lt;br /&gt;
|-&lt;br /&gt;
! Concept                                                                    &lt;br /&gt;
! Meaning                                                                    &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| allows_action?                                                               &lt;br /&gt;
| True for &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;; false for &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;.    &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| denies_action?                                                               &lt;br /&gt;
| True only for &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;.                                               &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| allows_with_penalty?                                                         &lt;br /&gt;
| True only for &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;.                                             &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| allows_without_penalty?                                                      &lt;br /&gt;
| True only for &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.                                               &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| permission_level                                                             &lt;br /&gt;
| Numeric ordering: No = 0, Late = 1, OK = 2. Used for comparison and sorting. &lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
These behaviors allow other parts of the system—particularly &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;—to make consistent, readable decisions.&lt;br /&gt;
&lt;br /&gt;
=== DueDate Model Refactoring ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model currently mixes persistence logic, deadline calculation, and permission checking, violating SRP.  &lt;br /&gt;
Most of its logic is implemented as class methods, which makes testing and extension difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Current design&lt;br /&gt;
def self.copy(old_assignment_id, new_assignment_id)&lt;br /&gt;
  duedates = where(parent_id: old_assignment_id)&lt;br /&gt;
  duedates.each do |orig_due_date|&lt;br /&gt;
    new_due_date = orig_due_date.dup&lt;br /&gt;
    new_due_date.parent_id = new_assignment_id&lt;br /&gt;
    new_due_date.save&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Improved Design ====&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model is simpler, more modular, and focused on representing a single deadline entry.&lt;br /&gt;
Behavior previously embedded inside &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; has been extracted into appropriate modules:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Responsibility Distribution After Refactoring                        &lt;br /&gt;
|-&lt;br /&gt;
! Concern                                                              &lt;br /&gt;
! Where It Lives Now                                                   &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Permission checking                                                    &lt;br /&gt;
| &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module                                 &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Deadline-related actions (next deadline, stage name, deadline copying) &lt;br /&gt;
| &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module (included in parent models)         &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Deadline type semantics                                                &lt;br /&gt;
| &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model                                        &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Core deadline data (due_at, round, parent)                             &lt;br /&gt;
| &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model                                             &lt;br /&gt;
|}   &lt;br /&gt;
&lt;br /&gt;
===== Instance-Based Behavior Instead of Class Methods =====&lt;br /&gt;
&lt;br /&gt;
Previously, many behaviors were class methods (e.g., copying deadlines).&lt;br /&gt;
These methods did not reflect object-level behavior and often duplicated logic.&lt;br /&gt;
&lt;br /&gt;
The redesign moves these operations to:&lt;br /&gt;
&lt;br /&gt;
* instance methods for actions on a specific &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;&lt;br /&gt;
* modules (DueDateActions) for higher-level behaviors on assignments or topics&lt;br /&gt;
&lt;br /&gt;
This separation:&lt;br /&gt;
&lt;br /&gt;
* improves testability&lt;br /&gt;
* aligns with Rails conventions&lt;br /&gt;
* avoids state duplication&lt;br /&gt;
&lt;br /&gt;
===== Clearer Query Methods =====&lt;br /&gt;
&lt;br /&gt;
The model now exposes simple, intuitive instance-level questions:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Examples of Simplified State Queries    &lt;br /&gt;
|-&lt;br /&gt;
! Method                                  &lt;br /&gt;
! Meaning                                 &lt;br /&gt;
|-                                         &lt;br /&gt;
| &amp;lt;code&amp;gt;upcoming?&amp;lt;/code&amp;gt;                    &lt;br /&gt;
| Whether the deadline occurs in the future &lt;br /&gt;
|-                                         &lt;br /&gt;
| &amp;lt;code&amp;gt;overdue?&amp;lt;/code&amp;gt;                     &lt;br /&gt;
| Whether the deadline has already passed   &lt;br /&gt;
|-                                         &lt;br /&gt;
| &amp;lt;code&amp;gt;deadline_type_name&amp;lt;/code&amp;gt;           &lt;br /&gt;
| Human-readable deadline type              &lt;br /&gt;
|}                                         &lt;br /&gt;
&lt;br /&gt;
These small, composable predicates replace multiple ad-hoc comparisons scattered across the codebase.&lt;br /&gt;
&lt;br /&gt;
=== DueDatePermissions Mixin ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Currently, permission checks depend only on user roles and ignore the actual deadline.  &lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Role-based permissions (participant_helpers.rb)&lt;br /&gt;
def participant_permissions(authorization)&lt;br /&gt;
  case authorization&lt;br /&gt;
  when 'reader'   then { can_submit: false, can_review: true, can_take_quiz: true }&lt;br /&gt;
  when 'reviewer' then { can_submit: false, can_review: true, can_take_quiz: false }&lt;br /&gt;
  when 'submitter' then { can_submit: true, can_review: false, can_take_quiz: false }&lt;br /&gt;
  else { can_submit: true, can_review: true, can_take_quiz: true }&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And deadline-based checks exist separately in the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def submission_allowed(topic_id = nil)&lt;br /&gt;
  check_condition('submission_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
def can_review(topic_id = nil)&lt;br /&gt;
  check_condition('review_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These two layers (role-based and time-based) never interact, leading to inconsistencies (e.g., a user with &amp;lt;code&amp;gt;can_submit=true&amp;lt;/code&amp;gt; after deadline).&lt;br /&gt;
&lt;br /&gt;
==== Proposed Changes ====&lt;br /&gt;
&lt;br /&gt;
The new &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module integrates role-based and deadline-based checks.&lt;br /&gt;
Instead of embedding checks directly into &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;, all permission logic now flows through &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Meaning of Permission Methods                                             &lt;br /&gt;
! Method                                                                                  &lt;br /&gt;
! What It Determines                                                                                        &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| can_submit?                                                                                                 &lt;br /&gt;
| Submission is allowed only if (1) the submission deadline is OK/Late and (2) the participant’s role permits submitting. &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| can_review?                                                                                                 &lt;br /&gt;
| Review is allowed only if the review deadline is OK/Late and the participant has review privileges.         &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| can_take_quiz?                                                                                              &lt;br /&gt;
| Quiz access is controlled by both quiz deadlines and participant quiz permissions.                          &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| can_review_teammate?                                                                                        &lt;br /&gt;
| Teammate review uses both the teammate-review deadline and participant review rights.                       &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| activity_permissible?                                                                                       &lt;br /&gt;
| Unified checker for any action, mapped through a dynamically computed &amp;lt;code&amp;gt;*_allowed_id&amp;lt;/code&amp;gt;.            &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| permission_status_for(action)                                                                               &lt;br /&gt;
| Returns &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt; for display and UI logic.                    &lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
These methods handle all the interactions between role state, deadline state, and the user’s participant record.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Integration with the DueDate Model ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model includes the &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module so that each deadline object inherently knows:&lt;br /&gt;
&lt;br /&gt;
* whether an action is allowed at that time&lt;br /&gt;
* whether lateness is permitted&lt;br /&gt;
* what the current permission status is&lt;br /&gt;
* how to evaluate participant-specific permissions&lt;br /&gt;
&lt;br /&gt;
=== DueDateActions Mixin ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; mixin provides a unified interface for models that own due dates, such as &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt;.&lt;br /&gt;
This module centralizes the logic for querying upcoming deadlines and determining whether specific activities are currently allowed.&lt;br /&gt;
&lt;br /&gt;
This solves the previous issue where each model re-implemented its own partial or duplicated deadline logic.&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The mixin provides:&lt;br /&gt;
&lt;br /&gt;
* A generic mechanism to determine whether an action is currently permissible&lt;br /&gt;
* Convenience methods for commonly used actions (submission, review, quiz, etc.)&lt;br /&gt;
* Unified logic for resolving topic-specific vs assignment-level deadlines&lt;br /&gt;
* A consistent method for retrieving the “next” deadline&lt;br /&gt;
* Utility methods for copying due dates between models&lt;br /&gt;
&lt;br /&gt;
This leads to cleaner parent models and avoids repeated logic scattered across the codebase.&lt;br /&gt;
&lt;br /&gt;
==== Proposed Changes ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Summary of Methods in &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! Method&lt;br /&gt;
! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;activity_permissible?(activity)&amp;lt;/code&amp;gt;&lt;br /&gt;
| Generic checker that evaluates whether a given activity is permitted at the current time, based on the resolved due date.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;submission_permissible?&amp;lt;/code&amp;gt;&lt;br /&gt;
| rowspan=&amp;quot;6&amp;quot; | Convenience wrappers around &amp;lt;code&amp;gt;activity_permissible?&amp;lt;/code&amp;gt; that provide activity-specific entry points while delegating the actual permission logic to the generic method.  &lt;br /&gt;
Mainly used to keep calling code and views readable, while ensuring all checks still go through the same underlying mechanism.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;review_permissible?&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;teammate_review_permissible?&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;quiz_permissible?&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;team_formation_permissible?&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;drop_topic_permissible?&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;next_due_date(topic_id)&amp;lt;/code&amp;gt;&lt;br /&gt;
| Resolves the next applicable deadline, preferring topic-specific deadlines when they exist and falling back to assignment-level deadlines otherwise.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;current_stage&amp;lt;/code&amp;gt;&lt;br /&gt;
| Returns a human-readable stage name (e.g., “review”, “submission”, or “finished”) based on the next due date. Useful for UI display.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;has_topic_specific_deadlines?&amp;lt;/code&amp;gt;&lt;br /&gt;
| Determines whether the assignment uses staggered or topic-specific deadlines.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;copy_due_dates_to(new_parent)&amp;lt;/code&amp;gt;&lt;br /&gt;
| Utility for cloning an entire set of due dates to another parent object (e.g., duplicating an assignment).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== How Deadline Resolution Works ====&lt;br /&gt;
&lt;br /&gt;
When checking permissions or determining the current stage, the mixin:&lt;br /&gt;
&lt;br /&gt;
# Checks for topic-specific deadlines if a topic ID is provided&lt;br /&gt;
# Selects the earliest upcoming deadline (topic-level or assignment-level)&lt;br /&gt;
# Delegates permission checking to that deadline’s logic&lt;br /&gt;
&lt;br /&gt;
This creates a consistent and predictable flow:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Deadline Resolution Pipeline                                                           &lt;br /&gt;
|-&lt;br /&gt;
! Step                                                                                   &lt;br /&gt;
! Description                                                                            &lt;br /&gt;
|-                                                                                        &lt;br /&gt;
| 1. Topic-specific?                                                                       &lt;br /&gt;
| If the assignment uses staggered/topic deadlines, topic-level entries are checked first. &lt;br /&gt;
|-                                                                                        &lt;br /&gt;
| 2. Assignment-level fallback                                                             &lt;br /&gt;
| If no applicable topic deadline exists, the nearest assignment-level deadline is used.   &lt;br /&gt;
|-                                                                                        &lt;br /&gt;
| 3. Permission evaluation                                                                 &lt;br /&gt;
| &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; decides whether the action is allowed (OK / Late / No).  &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Implementation Summary ==&lt;br /&gt;
&lt;br /&gt;
=== Models Created/Modified ===&lt;br /&gt;
&lt;br /&gt;
* '''DeadlineType''' - Canonical deadline type definitions with semantic helper methods&lt;br /&gt;
* '''DeadlineRight''' - Permission state model (OK/Late/No) with comparison methods&lt;br /&gt;
* '''DueDate''' - Refactored with instance methods, scopes, and permission checking&lt;br /&gt;
* '''TopicDueDate''' - STI subclass for topic-specific deadlines&lt;br /&gt;
* '''Assignment''' - Includes DueDateActions mixin&lt;br /&gt;
* '''SignUpTopic''' - Includes DueDateActions mixin&lt;br /&gt;
&lt;br /&gt;
=== Modules Created ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions''' - Permission checking combining deadline and role constraints&lt;br /&gt;
* '''DueDateActions''' - Deadline-related operations for parent models (Assignment, SignUpTopic)&lt;br /&gt;
&lt;br /&gt;
=== Key Improvements ===&lt;br /&gt;
&lt;br /&gt;
# '''Separation of Concerns''': Permission logic separated into dedicated modules&lt;br /&gt;
# '''Instance Methods''': Replaced class methods with testable instance methods&lt;br /&gt;
# '''Polymorphic Design''': Single DueDate model serves both Assignment and SignUpTopic&lt;br /&gt;
# '''Unified Permissions''': Role-based and time-based checks combined in one interface&lt;br /&gt;
# '''Data Integrity''': Removed duplicate deadline types, enforced foreign key constraints&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
The test suite includes comprehensive RSpec tests for the ''DueDate'' model and its associated models (''DeadlineType'', ''DeadlineRight'').&lt;br /&gt;
These tests verify model validations, scopes, instance methods, class methods, and callbacks with extensive coverage of edge cases and error conditions.&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
The test suite follows RSpec best practices with:&lt;br /&gt;
* '''Nested contexts''' for different scenarios (e.g., &amp;quot;when parent is missing&amp;quot;, &amp;quot;when round is 0&amp;quot;)&lt;br /&gt;
* '''Multiple assertions per context''' to verify behavior from different angles&lt;br /&gt;
* '''Edge case coverage''' including empty collections, nil values, and boundary conditions&lt;br /&gt;
* '''Error case testing''' for invalid data and non-existent records&lt;br /&gt;
* '''Clear descriptive names''' using &amp;quot;context 'when...'&amp;quot; and &amp;quot;it 'does something'&amp;quot; patterns&lt;br /&gt;
&lt;br /&gt;
=== Current Coverage ===&lt;br /&gt;
&lt;br /&gt;
==== Associations ====&lt;br /&gt;
* Tests verify &amp;lt;code&amp;gt;belongs_to :parent&amp;lt;/code&amp;gt; (polymorphic) and &amp;lt;code&amp;gt;belongs_to :deadline_type&amp;lt;/code&amp;gt; relationships&lt;br /&gt;
&lt;br /&gt;
==== Validations ====&lt;br /&gt;
* '''Required field validation''': Tests for parent, due_at, and deadline_type_id presence&lt;br /&gt;
* '''Round validation''': Tests for positive values, zero rejection, negative rejection, and nil handling&lt;br /&gt;
* '''Error messages''': Verifies specific error messages are added to appropriate fields&lt;br /&gt;
* '''Valid record creation''': Confirms records persist when all requirements are met&lt;br /&gt;
&lt;br /&gt;
==== Scopes ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;''': Returns future due dates ordered by due_at, excludes past dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;''': Returns past due dates ordered by due_at, excludes future dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;''': Filters by round number, handles non-existent rounds&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt;''': Filters by deadline type name, handles non-existent types&lt;br /&gt;
&lt;br /&gt;
==== Instance Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#overdue?&amp;lt;/code&amp;gt;''': Tests past dates (true), future dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#upcoming?&amp;lt;/code&amp;gt;''': Tests future dates (true), past dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#set&amp;lt;/code&amp;gt;''': Verifies updating deadline_type_id, parent_id, and round with persistence and error handling&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#copy&amp;lt;/code&amp;gt;''': Tests duplication to new assignment, attribute preservation, error handling for non-existent targets&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#deadline_type_name&amp;lt;/code&amp;gt;''': Returns associated deadline type name, handles nil cases&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#last_deadline?&amp;lt;/code&amp;gt;''': Tests detection of final deadline, handles multiple deadlines and single deadline scenarios&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#&amp;lt;=&amp;gt;&amp;lt;/code&amp;gt;''': Tests comparison by due_at time, handles non-DueDate objects&lt;br /&gt;
&lt;br /&gt;
==== Class Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.sort_due_dates&amp;lt;/code&amp;gt;''': Sorts by due_at ascending, handles empty arrays and single elements&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.any_future_due_dates?&amp;lt;/code&amp;gt;''': Detects future dates in collections, handles empty arrays and mixed date collections&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.copy&amp;lt;/code&amp;gt;''': Copies all due dates between assignments, preserves attributes, handles empty sources and non-existent records&lt;br /&gt;
&lt;br /&gt;
==== Callbacks ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;before_save :set_default_round&amp;lt;/code&amp;gt;''': Sets round to 1 when not specified, preserves explicit values, handles nil&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
Tests follow RSpec best practices with nested contexts for different scenarios:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
describe 'validations' do&lt;br /&gt;
  context 'when parent is missing' do&lt;br /&gt;
    it 'is invalid' do&lt;br /&gt;
      expect(due_date).to be_invalid&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'has error on parent field' do&lt;br /&gt;
      due_date.valid?&lt;br /&gt;
      expect(due_date.errors[:parent]).to include('must exist')&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when round validation' do&lt;br /&gt;
    context 'with round value of 0' do&lt;br /&gt;
      it 'is invalid' do&lt;br /&gt;
        expect(due_date).to be_invalid&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'with nil round' do&lt;br /&gt;
      it 'sets default round to 1' do&lt;br /&gt;
        expect(due_date.round).to eq(1)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
=== Demo Link ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add deadline notification system using the new permission framework&lt;br /&gt;
* Implement deadline templates for common assignment patterns&lt;br /&gt;
* Add API endpoints for mobile app integration&lt;br /&gt;
* Create admin dashboard for monitoring deadline compliance across courses&lt;br /&gt;
* Add automated deadline extension workflows based on configurable rules&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167207</id>
		<title>CSC/ECE 517 Fall 2025 - E2566. Finish DueDates</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167207"/>
		<updated>2025-12-02T05:23:45Z</updated>

		<summary type="html">&lt;p&gt;Skim253: /* Instructor's View */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
This project aims to complete the implementation of the DueDate system in Expertiza. The current implementation consists mostly of class methods and lacks proper definition of different deadline types and permission checking functionality. This redesign will create a more robust, readable, and maintainable due date system.&lt;br /&gt;
&lt;br /&gt;
=== Existing Components ===&lt;br /&gt;
* &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model with polymorphic parent association (Assignment/SignUpTopic)&lt;br /&gt;
* &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; subclasses&lt;br /&gt;
* Basic CRUD operations (within &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AssignmentForm&amp;lt;/code&amp;gt;) and sorting functionality (&amp;lt;code&amp;gt;deadline_sort&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Database schema with &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt; field&lt;br /&gt;
&lt;br /&gt;
=== Current Behavior ===&lt;br /&gt;
&lt;br /&gt;
====Instructor's View====&lt;br /&gt;
[[File:Duedates.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* When editing an assignment, due dates can be modified under the Due Dates tab.&lt;br /&gt;
* Each row contains the following columns: &amp;lt;code&amp;gt;Date &amp;amp; Time&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Use Date Updater&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Submission Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Teammate Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Meta-Review Allowed&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Reminder&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:Assignments-CurrentStage.png|800px]]&lt;br /&gt;
&lt;br /&gt;
* In the Assignments tab, the ''DueDate'' of each assignment is displayed as Current Stage and Stage Deadline.&lt;br /&gt;
&lt;br /&gt;
[[File:Due-date-topic-instructor.png|800px]]&lt;br /&gt;
&lt;br /&gt;
[[File:Due-date-topic-instructor-2.png|800px]]&lt;br /&gt;
&lt;br /&gt;
* In the Topics subtab, the ''DueDate'' of each topics are displayed.&lt;br /&gt;
&lt;br /&gt;
====Student's View====&lt;br /&gt;
[[File:Student-assignments.png|600px]]&lt;br /&gt;
* Students can view the due date information under &amp;lt;code&amp;gt;Current State&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Stage Deadline&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
* The actions available to students are determined by the current DueDate status.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-not-over.png|600px]]&lt;br /&gt;
* For example, during the submission period, students can access the &amp;lt;code&amp;gt;Submit a hyperlink&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Submit a file&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-over.png|600px]]&lt;br /&gt;
* Once the due date has passed, students cannot submit either a hyperlink or a file.&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Currently, there are some glaring issues with how due_dates was created in the first place, including redundancy and lack of modularity.&lt;br /&gt;
&lt;br /&gt;
Modeling &amp;amp; Structural Issues&lt;br /&gt;
# No dedicated &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to define different kinds of deadlines. The existing &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; class only serves as an association for &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; models, and is never referenced in the current &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; implementation.&lt;br /&gt;
# Duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table, which may lead to potential bugs.&lt;br /&gt;
# Incomplete deadline type definitions - &amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt; need to be added.&lt;br /&gt;
# No clear separation of concerns - data persistence, business logic, and permission logic are mixed in a single model.&lt;br /&gt;
&lt;br /&gt;
Permission &amp;amp; Behavior Issues&lt;br /&gt;
# Overuse of class methods making the code difficult to maintain.&lt;br /&gt;
# Missing permission checking logic for user actions.&lt;br /&gt;
&lt;br /&gt;
=== Tasks Identified ===&lt;br /&gt;
&lt;br /&gt;
Modeling &amp;amp; Structural Issues&lt;br /&gt;
# Refactor the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model to separate persistence, business logic, and permission logic into distinct layers.&lt;br /&gt;
# Implement reusable mixins (e.g., &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;) to handle permission evaluation and deadline-related operations.&lt;br /&gt;
# Introduce instance-level methods (e.g., &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;) to replace class-level utilities and improve testability.&lt;br /&gt;
&lt;br /&gt;
Permission &amp;amp; Behavior Issues&lt;br /&gt;
# Integrate role-based and time-based permission checks within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; to ensure consistent access control.&lt;br /&gt;
# Redesign the &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.&lt;br /&gt;
# Add missing deadline types (&amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt;) and remove duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries for data integrity.&lt;br /&gt;
&lt;br /&gt;
== Proposed Solution ==&lt;br /&gt;
The proposed solution restructures the due date system by separating concerns across dedicated models and mixin modules.&lt;br /&gt;
Deadline definitions (&amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt;), core deadline data (&amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;), and behavioral logic (&amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;) are isolated so each component has a single clear responsibility.&lt;br /&gt;
This modular design improves readability, reduces coupling, and makes deadline-related behavior easier to extend and maintain.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Duedate-diagram.png|600px]]&lt;br /&gt;
&lt;br /&gt;
This diagram visualizes the redesigned architecture: blue/red/green indicate Rails models, orange indicates modules. &lt;br /&gt;
Parent models call DueDateActions, which manages DueDate, while DueDatePermissions checks user rights using reference models. &lt;br /&gt;
&lt;br /&gt;
=== DeadlineType Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Although the current codebase includes a &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model, it only serves as a passive lookup table for associations with &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt;.  &lt;br /&gt;
In practice, most parts of the system still rely on hard-coded numeric IDs or manual lookups such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Example of current usage&lt;br /&gt;
deadline_type_id = DeadlineType.find_by(name: 'review').id&lt;br /&gt;
if due_date.deadline_type_id == deadline_type_id&lt;br /&gt;
  # Perform review-related logic&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of using the model as an active domain object, &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; directly compares IDs or string literals.  &lt;br /&gt;
This leads to several structural issues:&lt;br /&gt;
&lt;br /&gt;
* Inconsistent references (some use IDs, others strings)&lt;br /&gt;
* Tight coupling between &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
* Lack of helper semantics (e.g., &amp;lt;code&amp;gt;is_review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;is_submission?&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Data duplication and integrity risks (two &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; rows)&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; will serve as a source of truth, defining all valid deadline types and providing meaningful interfaces.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table defines all canonical deadline categories used by the system.&lt;br /&gt;
Each row represents one distinct deadline type, and every &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; record must reference one of these entries through &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Below is a structural description of the table:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; Table Structure                                                    &lt;br /&gt;
|- &lt;br /&gt;
! Column                                                                                         &lt;br /&gt;
! Type                                                                                           &lt;br /&gt;
! Description                                                                                    &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| id                                                                                               &lt;br /&gt;
| BIGINT (Primary Key)                                                                             &lt;br /&gt;
| Unique identifier for each deadline type; referenced by &amp;lt;code&amp;gt;due_dates.deadline_type_id&amp;lt;/code&amp;gt;. &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| name                                                                                             &lt;br /&gt;
| VARCHAR(255)                                                                                     &lt;br /&gt;
| Symbolic name (e.g., &amp;lt;code&amp;gt;submission&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review&amp;lt;/code&amp;gt;); must be unique.              &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| description                                                                                      &lt;br /&gt;
| TEXT                                                                                             &lt;br /&gt;
| Human-readable explanation of the deadline category.                                             &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| created_at                                                                                       &lt;br /&gt;
| TIMESTAMP                                                                                        &lt;br /&gt;
| Time when the row was created.                                                                   &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| updated_at                                                                                       &lt;br /&gt;
| TIMESTAMP                                                                                        &lt;br /&gt;
| Time when the row was last updated.                                                              &lt;br /&gt;
|}                                                                                                &lt;br /&gt;
&lt;br /&gt;
This schema ensures that:&lt;br /&gt;
&lt;br /&gt;
* Each deadline type has a unique name and description.&lt;br /&gt;
* No duplicate entries can exist (e.g., the prior duplicated &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; was cleaned).&lt;br /&gt;
* All &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; rows reference this master table through &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
* The &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; column acts as the authoritative key used throughout permissions and due-date logic.&lt;br /&gt;
&lt;br /&gt;
Migrated constraints also ensure that &amp;lt;code&amp;gt;due_dates.deadline_type_id&amp;lt;/code&amp;gt; is consistently stored as &amp;lt;code&amp;gt;BIGINT&amp;lt;/code&amp;gt;, allowing a long-term stable keyspace.&lt;br /&gt;
&lt;br /&gt;
==== Deadline Type Definitions ====&lt;br /&gt;
&lt;br /&gt;
Because each row in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; is referenced by &amp;lt;code&amp;gt;DueDate.deadline_type_id&amp;lt;/code&amp;gt;, the following definitions map directly to the schema above.&lt;br /&gt;
&lt;br /&gt;
Each ID corresponds exactly to the &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; field in the table and serves as the system-wide canonical reference for that deadline category.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Canonical Deadline Type Entries                                       &lt;br /&gt;
|-&lt;br /&gt;
! ID                                                                    &lt;br /&gt;
! Name                                                                  &lt;br /&gt;
! Description                                                           &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 1                                                                       &lt;br /&gt;
| submission                                                              &lt;br /&gt;
| Student submission deadlines                                            &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 2                                                                       &lt;br /&gt;
| review                                                                  &lt;br /&gt;
| Peer review deadlines                                                   &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 3                                                                       &lt;br /&gt;
| teammate_review                                                         &lt;br /&gt;
| Team member evaluation deadlines                                        &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 5                                                                       &lt;br /&gt;
| metareview                                                              &lt;br /&gt;
| Meta-review deadlines (kept for backward compatibility)                 &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 6                                                                       &lt;br /&gt;
| drop_topic                                                              &lt;br /&gt;
| Topic drop deadlines                                                    &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 7                                                                       &lt;br /&gt;
| signup                                                                  &lt;br /&gt;
| Topic signup deadlines                                                  &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 8                                                                       &lt;br /&gt;
| team_formation                                                          &lt;br /&gt;
| Team formation deadlines (duplicate entries cleaned; ID 8 is canonical) &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 11                                                                      &lt;br /&gt;
| quiz                                                                    &lt;br /&gt;
| Quiz completion deadlines                                               &lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
These definitions are seeded during migration and act as the source of truth for all deadline-related logic within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;.&lt;br /&gt;
By linking each &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; to one of these entries through &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;, the system standardizes behavior and eliminates inconsistent ID or string comparisons from earlier implementations.&lt;br /&gt;
&lt;br /&gt;
==== DeadlineType Model Implementation ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineType &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
  validates :description, presence: true&lt;br /&gt;
&lt;br /&gt;
  has_many :due_dates, foreign_key: :deadline_type_id, dependent: :restrict_with_exception&lt;br /&gt;
&lt;br /&gt;
  # Semantic helper methods for deadline type identification&lt;br /&gt;
  def submission?&lt;br /&gt;
    name == 'submission'&lt;br /&gt;
  end&lt;br /&gt;
  # same code for review, teammate_review, quiz, team_formation, signup, drop_topic   &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DeadlineRight Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model represents the permission level for a specific action at a given deadline.&lt;br /&gt;
It defines three states: &lt;br /&gt;
* &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt; : Action is allowed without penalty&lt;br /&gt;
* &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt; : Action is allowed but marked as late&lt;br /&gt;
* &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt; : Action is not permitted&lt;br /&gt;
&lt;br /&gt;
These values are referenced by &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; through fields such as &amp;lt;code&amp;gt;submission_allowed_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review_allowed_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz_allowed_id&amp;lt;/code&amp;gt;, and others.&lt;br /&gt;
This allows the system to determine user permissions by combining:&lt;br /&gt;
&lt;br /&gt;
# The current deadline’s allowed state&lt;br /&gt;
# The participant’s role-based ability (submit, review, take quiz)&lt;br /&gt;
&lt;br /&gt;
This combination logic is implemented in &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
The &amp;lt;code&amp;gt;deadline_rights&amp;lt;/code&amp;gt; table stores the canonical permission states used across the entire due-date system.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_rights&amp;lt;/code&amp;gt; Table Structure                                                         &lt;br /&gt;
|-&lt;br /&gt;
! Column                                                                                               &lt;br /&gt;
! Type                                                                                                 &lt;br /&gt;
! Description                                                                                          &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| id                                                                                                     &lt;br /&gt;
| BIGINT (Primary Key)                                                                                  &lt;br /&gt;
| Unique identifier for each permission state. Referenced by &amp;lt;code&amp;gt;due_dates.*_allowed_id&amp;lt;/code&amp;gt; fields. &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| name                                                                                                   &lt;br /&gt;
| VARCHAR(255)                                                                                           &lt;br /&gt;
| Symbolic permission name (&amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;). Must be unique.        &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| description                                                                                            &lt;br /&gt;
| TEXT                                                                                                   &lt;br /&gt;
| Human-readable explanation of what the permission state means.                                         &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| created_at                                                                                             &lt;br /&gt;
| TIMESTAMP                                                                                              &lt;br /&gt;
| Timestamp when the entry was created.                                                                  &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| updated_at                                                                                             &lt;br /&gt;
| TIMESTAMP                                                                                              &lt;br /&gt;
| Timestamp when the entry was last updated.                                                             &lt;br /&gt;
|}  &lt;br /&gt;
&lt;br /&gt;
==== DeadlineRight Model ====&lt;br /&gt;
Instead of embedding permission logic inside &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;, the &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model defines the meaning of each permission state.&lt;br /&gt;
This removes magic strings (&amp;lt;code&amp;gt;&amp;quot;OK&amp;quot;&amp;lt;/code&amp;gt;) and numeric comparisons and replaces them with expressive model-level semantics.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Semantic Meaning Provided by &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt;                    &lt;br /&gt;
|-&lt;br /&gt;
! Concept                                                                    &lt;br /&gt;
! Meaning                                                                    &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| allows_action?                                                               &lt;br /&gt;
| True for &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;; false for &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;.    &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| denies_action?                                                               &lt;br /&gt;
| True only for &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;.                                               &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| allows_with_penalty?                                                         &lt;br /&gt;
| True only for &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;.                                             &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| allows_without_penalty?                                                      &lt;br /&gt;
| True only for &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.                                               &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| permission_level                                                             &lt;br /&gt;
| Numeric ordering: No = 0, Late = 1, OK = 2. Used for comparison and sorting. &lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
These behaviors allow other parts of the system—particularly &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;—to make consistent, readable decisions.&lt;br /&gt;
&lt;br /&gt;
=== DueDate Model Refactoring ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model currently mixes persistence logic, deadline calculation, and permission checking, violating SRP.  &lt;br /&gt;
Most of its logic is implemented as class methods, which makes testing and extension difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Current design&lt;br /&gt;
def self.copy(old_assignment_id, new_assignment_id)&lt;br /&gt;
  duedates = where(parent_id: old_assignment_id)&lt;br /&gt;
  duedates.each do |orig_due_date|&lt;br /&gt;
    new_due_date = orig_due_date.dup&lt;br /&gt;
    new_due_date.parent_id = new_assignment_id&lt;br /&gt;
    new_due_date.save&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Improved Design ====&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model is simpler, more modular, and focused on representing a single deadline entry.&lt;br /&gt;
Behavior previously embedded inside &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; has been extracted into appropriate modules:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Responsibility Distribution After Refactoring                        &lt;br /&gt;
|-&lt;br /&gt;
! Concern                                                              &lt;br /&gt;
! Where It Lives Now                                                   &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Permission checking                                                    &lt;br /&gt;
| &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module                                 &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Deadline-related actions (next deadline, stage name, deadline copying) &lt;br /&gt;
| &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module (included in parent models)         &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Deadline type semantics                                                &lt;br /&gt;
| &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model                                        &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Core deadline data (due_at, round, parent)                             &lt;br /&gt;
| &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model                                             &lt;br /&gt;
|}   &lt;br /&gt;
&lt;br /&gt;
===== Instance-Based Behavior Instead of Class Methods =====&lt;br /&gt;
&lt;br /&gt;
Previously, many behaviors were class methods (e.g., copying deadlines).&lt;br /&gt;
These methods did not reflect object-level behavior and often duplicated logic.&lt;br /&gt;
&lt;br /&gt;
The redesign moves these operations to:&lt;br /&gt;
&lt;br /&gt;
* instance methods for actions on a specific &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;&lt;br /&gt;
* modules (DueDateActions) for higher-level behaviors on assignments or topics&lt;br /&gt;
&lt;br /&gt;
This separation:&lt;br /&gt;
&lt;br /&gt;
* improves testability&lt;br /&gt;
* aligns with Rails conventions&lt;br /&gt;
* avoids state duplication&lt;br /&gt;
&lt;br /&gt;
===== Clearer Query Methods =====&lt;br /&gt;
&lt;br /&gt;
The model now exposes simple, intuitive instance-level questions:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Examples of Simplified State Queries    &lt;br /&gt;
|-&lt;br /&gt;
! Method                                  &lt;br /&gt;
! Meaning                                 &lt;br /&gt;
|-                                         &lt;br /&gt;
| &amp;lt;code&amp;gt;upcoming?&amp;lt;/code&amp;gt;                    &lt;br /&gt;
| Whether the deadline occurs in the future &lt;br /&gt;
|-                                         &lt;br /&gt;
| &amp;lt;code&amp;gt;overdue?&amp;lt;/code&amp;gt;                     &lt;br /&gt;
| Whether the deadline has already passed   &lt;br /&gt;
|-                                         &lt;br /&gt;
| &amp;lt;code&amp;gt;deadline_type_name&amp;lt;/code&amp;gt;           &lt;br /&gt;
| Human-readable deadline type              &lt;br /&gt;
|}                                         &lt;br /&gt;
&lt;br /&gt;
These small, composable predicates replace multiple ad-hoc comparisons scattered across the codebase.&lt;br /&gt;
&lt;br /&gt;
=== DueDatePermissions Mixin ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Currently, permission checks depend only on user roles and ignore the actual deadline.  &lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Role-based permissions (participant_helpers.rb)&lt;br /&gt;
def participant_permissions(authorization)&lt;br /&gt;
  case authorization&lt;br /&gt;
  when 'reader'   then { can_submit: false, can_review: true, can_take_quiz: true }&lt;br /&gt;
  when 'reviewer' then { can_submit: false, can_review: true, can_take_quiz: false }&lt;br /&gt;
  when 'submitter' then { can_submit: true, can_review: false, can_take_quiz: false }&lt;br /&gt;
  else { can_submit: true, can_review: true, can_take_quiz: true }&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And deadline-based checks exist separately in the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def submission_allowed(topic_id = nil)&lt;br /&gt;
  check_condition('submission_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
def can_review(topic_id = nil)&lt;br /&gt;
  check_condition('review_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These two layers (role-based and time-based) never interact, leading to inconsistencies (e.g., a user with &amp;lt;code&amp;gt;can_submit=true&amp;lt;/code&amp;gt; after deadline).&lt;br /&gt;
&lt;br /&gt;
==== Proposed Changes ====&lt;br /&gt;
&lt;br /&gt;
The new &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module integrates role-based and deadline-based checks.&lt;br /&gt;
Instead of embedding checks directly into &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;, all permission logic now flows through &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Meaning of Permission Methods                                             &lt;br /&gt;
! Method                                                                                  &lt;br /&gt;
! What It Determines                                                                                        &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| can_submit?                                                                                                 &lt;br /&gt;
| Submission is allowed only if (1) the submission deadline is OK/Late and (2) the participant’s role permits submitting. &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| can_review?                                                                                                 &lt;br /&gt;
| Review is allowed only if the review deadline is OK/Late and the participant has review privileges.         &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| can_take_quiz?                                                                                              &lt;br /&gt;
| Quiz access is controlled by both quiz deadlines and participant quiz permissions.                          &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| can_review_teammate?                                                                                        &lt;br /&gt;
| Teammate review uses both the teammate-review deadline and participant review rights.                       &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| activity_permissible?                                                                                       &lt;br /&gt;
| Unified checker for any action, mapped through a dynamically computed &amp;lt;code&amp;gt;*_allowed_id&amp;lt;/code&amp;gt;.            &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| permission_status_for(action)                                                                               &lt;br /&gt;
| Returns &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt; for display and UI logic.                    &lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
These methods handle all the interactions between role state, deadline state, and the user’s participant record.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Integration with the DueDate Model ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model includes the &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module so that each deadline object inherently knows:&lt;br /&gt;
&lt;br /&gt;
* whether an action is allowed at that time&lt;br /&gt;
* whether lateness is permitted&lt;br /&gt;
* what the current permission status is&lt;br /&gt;
* how to evaluate participant-specific permissions&lt;br /&gt;
&lt;br /&gt;
=== DueDateActions Mixin ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; mixin provides a unified interface for models that own due dates, such as &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt;.&lt;br /&gt;
This module centralizes the logic for querying upcoming deadlines and determining whether specific activities are currently allowed.&lt;br /&gt;
&lt;br /&gt;
This solves the previous issue where each model re-implemented its own partial or duplicated deadline logic.&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The mixin provides:&lt;br /&gt;
&lt;br /&gt;
* A generic mechanism to determine whether an action is currently permissible&lt;br /&gt;
* Convenience methods for commonly used actions (submission, review, quiz, etc.)&lt;br /&gt;
* Unified logic for resolving topic-specific vs assignment-level deadlines&lt;br /&gt;
* A consistent method for retrieving the “next” deadline&lt;br /&gt;
* Utility methods for copying due dates between models&lt;br /&gt;
&lt;br /&gt;
This leads to cleaner parent models and avoids repeated logic scattered across the codebase.&lt;br /&gt;
&lt;br /&gt;
==== Proposed Changes ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Summary of Methods in &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! Method&lt;br /&gt;
! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;activity_permissible?(activity)&amp;lt;/code&amp;gt;&lt;br /&gt;
| Generic checker that evaluates whether a given activity is permitted at the current time, based on the resolved due date.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;submission_permissible?&amp;lt;/code&amp;gt;&lt;br /&gt;
| rowspan=&amp;quot;6&amp;quot; | Convenience wrappers around &amp;lt;code&amp;gt;activity_permissible?&amp;lt;/code&amp;gt; that provide activity-specific entry points while delegating the actual permission logic to the generic method.  &lt;br /&gt;
Mainly used to keep calling code and views readable, while ensuring all checks still go through the same underlying mechanism.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;review_permissible?&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;teammate_review_permissible?&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;quiz_permissible?&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;team_formation_permissible?&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;drop_topic_permissible?&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;next_due_date(topic_id)&amp;lt;/code&amp;gt;&lt;br /&gt;
| Resolves the next applicable deadline, preferring topic-specific deadlines when they exist and falling back to assignment-level deadlines otherwise.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;current_stage&amp;lt;/code&amp;gt;&lt;br /&gt;
| Returns a human-readable stage name (e.g., “review”, “submission”, or “finished”) based on the next due date. Useful for UI display.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;has_topic_specific_deadlines?&amp;lt;/code&amp;gt;&lt;br /&gt;
| Determines whether the assignment uses staggered or topic-specific deadlines.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;copy_due_dates_to(new_parent)&amp;lt;/code&amp;gt;&lt;br /&gt;
| Utility for cloning an entire set of due dates to another parent object (e.g., duplicating an assignment).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== How Deadline Resolution Works ====&lt;br /&gt;
&lt;br /&gt;
When checking permissions or determining the current stage, the mixin:&lt;br /&gt;
&lt;br /&gt;
# Checks for topic-specific deadlines if a topic ID is provided&lt;br /&gt;
# Selects the earliest upcoming deadline (topic-level or assignment-level)&lt;br /&gt;
# Delegates permission checking to that deadline’s logic&lt;br /&gt;
&lt;br /&gt;
This creates a consistent and predictable flow:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Deadline Resolution Pipeline                                                           &lt;br /&gt;
|-&lt;br /&gt;
! Step                                                                                   &lt;br /&gt;
! Description                                                                            &lt;br /&gt;
|-                                                                                        &lt;br /&gt;
| 1. Topic-specific?                                                                       &lt;br /&gt;
| If the assignment uses staggered/topic deadlines, topic-level entries are checked first. &lt;br /&gt;
|-                                                                                        &lt;br /&gt;
| 2. Assignment-level fallback                                                             &lt;br /&gt;
| If no applicable topic deadline exists, the nearest assignment-level deadline is used.   &lt;br /&gt;
|-                                                                                        &lt;br /&gt;
| 3. Permission evaluation                                                                 &lt;br /&gt;
| &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; decides whether the action is allowed (OK / Late / No).  &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Implementation Summary ==&lt;br /&gt;
&lt;br /&gt;
=== Models Created/Modified ===&lt;br /&gt;
&lt;br /&gt;
* '''DeadlineType''' - Canonical deadline type definitions with semantic helper methods&lt;br /&gt;
* '''DeadlineRight''' - Permission state model (OK/Late/No) with comparison methods&lt;br /&gt;
* '''DueDate''' - Refactored with instance methods, scopes, and permission checking&lt;br /&gt;
* '''TopicDueDate''' - STI subclass for topic-specific deadlines&lt;br /&gt;
* '''Assignment''' - Includes DueDateActions mixin&lt;br /&gt;
* '''SignUpTopic''' - Includes DueDateActions mixin&lt;br /&gt;
&lt;br /&gt;
=== Modules Created ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions''' - Permission checking combining deadline and role constraints&lt;br /&gt;
* '''DueDateActions''' - Deadline-related operations for parent models (Assignment, SignUpTopic)&lt;br /&gt;
&lt;br /&gt;
=== Key Improvements ===&lt;br /&gt;
&lt;br /&gt;
# '''Separation of Concerns''': Permission logic separated into dedicated modules&lt;br /&gt;
# '''Instance Methods''': Replaced class methods with testable instance methods&lt;br /&gt;
# '''Polymorphic Design''': Single DueDate model serves both Assignment and SignUpTopic&lt;br /&gt;
# '''Unified Permissions''': Role-based and time-based checks combined in one interface&lt;br /&gt;
# '''Data Integrity''': Removed duplicate deadline types, enforced foreign key constraints&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
The test suite includes comprehensive RSpec tests for the ''DueDate'' model and its associated models (''DeadlineType'', ''DeadlineRight'').&lt;br /&gt;
These tests verify model validations, scopes, instance methods, class methods, and callbacks with extensive coverage of edge cases and error conditions.&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
The test suite follows RSpec best practices with:&lt;br /&gt;
* '''Nested contexts''' for different scenarios (e.g., &amp;quot;when parent is missing&amp;quot;, &amp;quot;when round is 0&amp;quot;)&lt;br /&gt;
* '''Multiple assertions per context''' to verify behavior from different angles&lt;br /&gt;
* '''Edge case coverage''' including empty collections, nil values, and boundary conditions&lt;br /&gt;
* '''Error case testing''' for invalid data and non-existent records&lt;br /&gt;
* '''Clear descriptive names''' using &amp;quot;context 'when...'&amp;quot; and &amp;quot;it 'does something'&amp;quot; patterns&lt;br /&gt;
&lt;br /&gt;
=== Current Coverage ===&lt;br /&gt;
&lt;br /&gt;
==== Associations ====&lt;br /&gt;
* Tests verify &amp;lt;code&amp;gt;belongs_to :parent&amp;lt;/code&amp;gt; (polymorphic) and &amp;lt;code&amp;gt;belongs_to :deadline_type&amp;lt;/code&amp;gt; relationships&lt;br /&gt;
&lt;br /&gt;
==== Validations ====&lt;br /&gt;
* '''Required field validation''': Tests for parent, due_at, and deadline_type_id presence&lt;br /&gt;
* '''Round validation''': Tests for positive values, zero rejection, negative rejection, and nil handling&lt;br /&gt;
* '''Error messages''': Verifies specific error messages are added to appropriate fields&lt;br /&gt;
* '''Valid record creation''': Confirms records persist when all requirements are met&lt;br /&gt;
&lt;br /&gt;
==== Scopes ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;''': Returns future due dates ordered by due_at, excludes past dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;''': Returns past due dates ordered by due_at, excludes future dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;''': Filters by round number, handles non-existent rounds&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt;''': Filters by deadline type name, handles non-existent types&lt;br /&gt;
&lt;br /&gt;
==== Instance Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#overdue?&amp;lt;/code&amp;gt;''': Tests past dates (true), future dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#upcoming?&amp;lt;/code&amp;gt;''': Tests future dates (true), past dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#set&amp;lt;/code&amp;gt;''': Verifies updating deadline_type_id, parent_id, and round with persistence and error handling&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#copy&amp;lt;/code&amp;gt;''': Tests duplication to new assignment, attribute preservation, error handling for non-existent targets&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#deadline_type_name&amp;lt;/code&amp;gt;''': Returns associated deadline type name, handles nil cases&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#last_deadline?&amp;lt;/code&amp;gt;''': Tests detection of final deadline, handles multiple deadlines and single deadline scenarios&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#&amp;lt;=&amp;gt;&amp;lt;/code&amp;gt;''': Tests comparison by due_at time, handles non-DueDate objects&lt;br /&gt;
&lt;br /&gt;
==== Class Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.sort_due_dates&amp;lt;/code&amp;gt;''': Sorts by due_at ascending, handles empty arrays and single elements&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.any_future_due_dates?&amp;lt;/code&amp;gt;''': Detects future dates in collections, handles empty arrays and mixed date collections&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.copy&amp;lt;/code&amp;gt;''': Copies all due dates between assignments, preserves attributes, handles empty sources and non-existent records&lt;br /&gt;
&lt;br /&gt;
==== Callbacks ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;before_save :set_default_round&amp;lt;/code&amp;gt;''': Sets round to 1 when not specified, preserves explicit values, handles nil&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
Tests follow RSpec best practices with nested contexts for different scenarios:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
describe 'validations' do&lt;br /&gt;
  context 'when parent is missing' do&lt;br /&gt;
    it 'is invalid' do&lt;br /&gt;
      expect(due_date).to be_invalid&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'has error on parent field' do&lt;br /&gt;
      due_date.valid?&lt;br /&gt;
      expect(due_date.errors[:parent]).to include('must exist')&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when round validation' do&lt;br /&gt;
    context 'with round value of 0' do&lt;br /&gt;
      it 'is invalid' do&lt;br /&gt;
        expect(due_date).to be_invalid&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'with nil round' do&lt;br /&gt;
      it 'sets default round to 1' do&lt;br /&gt;
        expect(due_date.round).to eq(1)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
=== Demo Link ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add deadline notification system using the new permission framework&lt;br /&gt;
* Implement deadline templates for common assignment patterns&lt;br /&gt;
* Add API endpoints for mobile app integration&lt;br /&gt;
* Create admin dashboard for monitoring deadline compliance across courses&lt;br /&gt;
* Add automated deadline extension workflows based on configurable rules&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=File:Due-date-topic-instructor-2.png&amp;diff=167206</id>
		<title>File:Due-date-topic-instructor-2.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=File:Due-date-topic-instructor-2.png&amp;diff=167206"/>
		<updated>2025-12-02T05:19:55Z</updated>

		<summary type="html">&lt;p&gt;Skim253: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=File:Due-date-topic-instructor.png&amp;diff=167205</id>
		<title>File:Due-date-topic-instructor.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=File:Due-date-topic-instructor.png&amp;diff=167205"/>
		<updated>2025-12-02T05:19:46Z</updated>

		<summary type="html">&lt;p&gt;Skim253: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167204</id>
		<title>CSC/ECE 517 Fall 2025 - E2566. Finish DueDates</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167204"/>
		<updated>2025-12-02T05:07:42Z</updated>

		<summary type="html">&lt;p&gt;Skim253: /* Current Behavior */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
This project aims to complete the implementation of the DueDate system in Expertiza. The current implementation consists mostly of class methods and lacks proper definition of different deadline types and permission checking functionality. This redesign will create a more robust, readable, and maintainable due date system.&lt;br /&gt;
&lt;br /&gt;
=== Existing Components ===&lt;br /&gt;
* &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model with polymorphic parent association (Assignment/SignUpTopic)&lt;br /&gt;
* &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; subclasses&lt;br /&gt;
* Basic CRUD operations (within &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AssignmentForm&amp;lt;/code&amp;gt;) and sorting functionality (&amp;lt;code&amp;gt;deadline_sort&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Database schema with &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt; field&lt;br /&gt;
&lt;br /&gt;
=== Current Behavior ===&lt;br /&gt;
&lt;br /&gt;
====Instructor's View====&lt;br /&gt;
[[File:Duedates.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* When editing an assignment, due dates can be modified under the Due Dates tab.&lt;br /&gt;
* Each row contains the following columns: &amp;lt;code&amp;gt;Date &amp;amp; Time&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Use Date Updater&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Submission Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Teammate Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Meta-Review Allowed&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Reminder&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:Assignments-CurrentStage.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* In the Assignments table, the ''DueDate'' of each assignment is displayed as Current Stage and Stage Deadline.&lt;br /&gt;
&lt;br /&gt;
====Student's View====&lt;br /&gt;
[[File:Student-assignments.png|600px]]&lt;br /&gt;
* Students can view the due date information under &amp;lt;code&amp;gt;Current State&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Stage Deadline&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
* The actions available to students are determined by the current DueDate status.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-not-over.png|600px]]&lt;br /&gt;
* For example, during the submission period, students can access the &amp;lt;code&amp;gt;Submit a hyperlink&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Submit a file&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-over.png|600px]]&lt;br /&gt;
* Once the due date has passed, students cannot submit either a hyperlink or a file.&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Currently, there are some glaring issues with how due_dates was created in the first place, including redundancy and lack of modularity.&lt;br /&gt;
&lt;br /&gt;
Modeling &amp;amp; Structural Issues&lt;br /&gt;
# No dedicated &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to define different kinds of deadlines. The existing &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; class only serves as an association for &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; models, and is never referenced in the current &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; implementation.&lt;br /&gt;
# Duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table, which may lead to potential bugs.&lt;br /&gt;
# Incomplete deadline type definitions - &amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt; need to be added.&lt;br /&gt;
# No clear separation of concerns - data persistence, business logic, and permission logic are mixed in a single model.&lt;br /&gt;
&lt;br /&gt;
Permission &amp;amp; Behavior Issues&lt;br /&gt;
# Overuse of class methods making the code difficult to maintain.&lt;br /&gt;
# Missing permission checking logic for user actions.&lt;br /&gt;
&lt;br /&gt;
=== Tasks Identified ===&lt;br /&gt;
&lt;br /&gt;
Modeling &amp;amp; Structural Issues&lt;br /&gt;
# Refactor the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model to separate persistence, business logic, and permission logic into distinct layers.&lt;br /&gt;
# Implement reusable mixins (e.g., &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;) to handle permission evaluation and deadline-related operations.&lt;br /&gt;
# Introduce instance-level methods (e.g., &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;) to replace class-level utilities and improve testability.&lt;br /&gt;
&lt;br /&gt;
Permission &amp;amp; Behavior Issues&lt;br /&gt;
# Integrate role-based and time-based permission checks within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; to ensure consistent access control.&lt;br /&gt;
# Redesign the &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.&lt;br /&gt;
# Add missing deadline types (&amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt;) and remove duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries for data integrity.&lt;br /&gt;
&lt;br /&gt;
== Proposed Solution ==&lt;br /&gt;
The proposed solution restructures the due date system by separating concerns across dedicated models and mixin modules.&lt;br /&gt;
Deadline definitions (&amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt;), core deadline data (&amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;), and behavioral logic (&amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;) are isolated so each component has a single clear responsibility.&lt;br /&gt;
This modular design improves readability, reduces coupling, and makes deadline-related behavior easier to extend and maintain.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Duedate-diagram.png|600px]]&lt;br /&gt;
&lt;br /&gt;
This diagram visualizes the redesigned architecture: blue/red/green indicate Rails models, orange indicates modules. &lt;br /&gt;
Parent models call DueDateActions, which manages DueDate, while DueDatePermissions checks user rights using reference models. &lt;br /&gt;
&lt;br /&gt;
=== DeadlineType Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Although the current codebase includes a &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model, it only serves as a passive lookup table for associations with &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt;.  &lt;br /&gt;
In practice, most parts of the system still rely on hard-coded numeric IDs or manual lookups such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Example of current usage&lt;br /&gt;
deadline_type_id = DeadlineType.find_by(name: 'review').id&lt;br /&gt;
if due_date.deadline_type_id == deadline_type_id&lt;br /&gt;
  # Perform review-related logic&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of using the model as an active domain object, &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; directly compares IDs or string literals.  &lt;br /&gt;
This leads to several structural issues:&lt;br /&gt;
&lt;br /&gt;
* Inconsistent references (some use IDs, others strings)&lt;br /&gt;
* Tight coupling between &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
* Lack of helper semantics (e.g., &amp;lt;code&amp;gt;is_review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;is_submission?&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Data duplication and integrity risks (two &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; rows)&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; will serve as a source of truth, defining all valid deadline types and providing meaningful interfaces.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table defines all canonical deadline categories used by the system.&lt;br /&gt;
Each row represents one distinct deadline type, and every &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; record must reference one of these entries through &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Below is a structural description of the table:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; Table Structure                                                    &lt;br /&gt;
|- &lt;br /&gt;
! Column                                                                                         &lt;br /&gt;
! Type                                                                                           &lt;br /&gt;
! Description                                                                                    &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| id                                                                                               &lt;br /&gt;
| BIGINT (Primary Key)                                                                             &lt;br /&gt;
| Unique identifier for each deadline type; referenced by &amp;lt;code&amp;gt;due_dates.deadline_type_id&amp;lt;/code&amp;gt;. &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| name                                                                                             &lt;br /&gt;
| VARCHAR(255)                                                                                     &lt;br /&gt;
| Symbolic name (e.g., &amp;lt;code&amp;gt;submission&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review&amp;lt;/code&amp;gt;); must be unique.              &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| description                                                                                      &lt;br /&gt;
| TEXT                                                                                             &lt;br /&gt;
| Human-readable explanation of the deadline category.                                             &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| created_at                                                                                       &lt;br /&gt;
| TIMESTAMP                                                                                        &lt;br /&gt;
| Time when the row was created.                                                                   &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| updated_at                                                                                       &lt;br /&gt;
| TIMESTAMP                                                                                        &lt;br /&gt;
| Time when the row was last updated.                                                              &lt;br /&gt;
|}                                                                                                &lt;br /&gt;
&lt;br /&gt;
This schema ensures that:&lt;br /&gt;
&lt;br /&gt;
* Each deadline type has a unique name and description.&lt;br /&gt;
* No duplicate entries can exist (e.g., the prior duplicated &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; was cleaned).&lt;br /&gt;
* All &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; rows reference this master table through &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
* The &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; column acts as the authoritative key used throughout permissions and due-date logic.&lt;br /&gt;
&lt;br /&gt;
Migrated constraints also ensure that &amp;lt;code&amp;gt;due_dates.deadline_type_id&amp;lt;/code&amp;gt; is consistently stored as &amp;lt;code&amp;gt;BIGINT&amp;lt;/code&amp;gt;, allowing a long-term stable keyspace.&lt;br /&gt;
&lt;br /&gt;
==== Deadline Type Definitions ====&lt;br /&gt;
&lt;br /&gt;
Because each row in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; is referenced by &amp;lt;code&amp;gt;DueDate.deadline_type_id&amp;lt;/code&amp;gt;, the following definitions map directly to the schema above.&lt;br /&gt;
&lt;br /&gt;
Each ID corresponds exactly to the &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; field in the table and serves as the system-wide canonical reference for that deadline category.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Canonical Deadline Type Entries                                       &lt;br /&gt;
|-&lt;br /&gt;
! ID                                                                    &lt;br /&gt;
! Name                                                                  &lt;br /&gt;
! Description                                                           &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 1                                                                       &lt;br /&gt;
| submission                                                              &lt;br /&gt;
| Student submission deadlines                                            &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 2                                                                       &lt;br /&gt;
| review                                                                  &lt;br /&gt;
| Peer review deadlines                                                   &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 3                                                                       &lt;br /&gt;
| teammate_review                                                         &lt;br /&gt;
| Team member evaluation deadlines                                        &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 5                                                                       &lt;br /&gt;
| metareview                                                              &lt;br /&gt;
| Meta-review deadlines (kept for backward compatibility)                 &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 6                                                                       &lt;br /&gt;
| drop_topic                                                              &lt;br /&gt;
| Topic drop deadlines                                                    &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 7                                                                       &lt;br /&gt;
| signup                                                                  &lt;br /&gt;
| Topic signup deadlines                                                  &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 8                                                                       &lt;br /&gt;
| team_formation                                                          &lt;br /&gt;
| Team formation deadlines (duplicate entries cleaned; ID 8 is canonical) &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 11                                                                      &lt;br /&gt;
| quiz                                                                    &lt;br /&gt;
| Quiz completion deadlines                                               &lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
These definitions are seeded during migration and act as the source of truth for all deadline-related logic within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;.&lt;br /&gt;
By linking each &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; to one of these entries through &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;, the system standardizes behavior and eliminates inconsistent ID or string comparisons from earlier implementations.&lt;br /&gt;
&lt;br /&gt;
==== DeadlineType Model Implementation ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineType &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
  validates :description, presence: true&lt;br /&gt;
&lt;br /&gt;
  has_many :due_dates, foreign_key: :deadline_type_id, dependent: :restrict_with_exception&lt;br /&gt;
&lt;br /&gt;
  # Semantic helper methods for deadline type identification&lt;br /&gt;
  def submission?&lt;br /&gt;
    name == 'submission'&lt;br /&gt;
  end&lt;br /&gt;
  # same code for review, teammate_review, quiz, team_formation, signup, drop_topic   &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DeadlineRight Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model represents the permission level for a specific action at a given deadline.&lt;br /&gt;
It defines three states: &lt;br /&gt;
* &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt; : Action is allowed without penalty&lt;br /&gt;
* &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt; : Action is allowed but marked as late&lt;br /&gt;
* &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt; : Action is not permitted&lt;br /&gt;
&lt;br /&gt;
These values are referenced by &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; through fields such as &amp;lt;code&amp;gt;submission_allowed_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review_allowed_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz_allowed_id&amp;lt;/code&amp;gt;, and others.&lt;br /&gt;
This allows the system to determine user permissions by combining:&lt;br /&gt;
&lt;br /&gt;
# The current deadline’s allowed state&lt;br /&gt;
# The participant’s role-based ability (submit, review, take quiz)&lt;br /&gt;
&lt;br /&gt;
This combination logic is implemented in &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
The &amp;lt;code&amp;gt;deadline_rights&amp;lt;/code&amp;gt; table stores the canonical permission states used across the entire due-date system.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_rights&amp;lt;/code&amp;gt; Table Structure                                                         &lt;br /&gt;
|-&lt;br /&gt;
! Column                                                                                               &lt;br /&gt;
! Type                                                                                                 &lt;br /&gt;
! Description                                                                                          &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| id                                                                                                     &lt;br /&gt;
| BIGINT (Primary Key)                                                                                  &lt;br /&gt;
| Unique identifier for each permission state. Referenced by &amp;lt;code&amp;gt;due_dates.*_allowed_id&amp;lt;/code&amp;gt; fields. &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| name                                                                                                   &lt;br /&gt;
| VARCHAR(255)                                                                                           &lt;br /&gt;
| Symbolic permission name (&amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;). Must be unique.        &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| description                                                                                            &lt;br /&gt;
| TEXT                                                                                                   &lt;br /&gt;
| Human-readable explanation of what the permission state means.                                         &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| created_at                                                                                             &lt;br /&gt;
| TIMESTAMP                                                                                              &lt;br /&gt;
| Timestamp when the entry was created.                                                                  &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| updated_at                                                                                             &lt;br /&gt;
| TIMESTAMP                                                                                              &lt;br /&gt;
| Timestamp when the entry was last updated.                                                             &lt;br /&gt;
|}  &lt;br /&gt;
&lt;br /&gt;
==== DeadlineRight Model ====&lt;br /&gt;
Instead of embedding permission logic inside &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;, the &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model defines the meaning of each permission state.&lt;br /&gt;
This removes magic strings (&amp;lt;code&amp;gt;&amp;quot;OK&amp;quot;&amp;lt;/code&amp;gt;) and numeric comparisons and replaces them with expressive model-level semantics.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Semantic Meaning Provided by &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt;                    &lt;br /&gt;
|-&lt;br /&gt;
! Concept                                                                    &lt;br /&gt;
! Meaning                                                                    &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| allows_action?                                                               &lt;br /&gt;
| True for &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;; false for &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;.    &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| denies_action?                                                               &lt;br /&gt;
| True only for &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;.                                               &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| allows_with_penalty?                                                         &lt;br /&gt;
| True only for &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;.                                             &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| allows_without_penalty?                                                      &lt;br /&gt;
| True only for &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.                                               &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| permission_level                                                             &lt;br /&gt;
| Numeric ordering: No = 0, Late = 1, OK = 2. Used for comparison and sorting. &lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
These behaviors allow other parts of the system—particularly &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;—to make consistent, readable decisions.&lt;br /&gt;
&lt;br /&gt;
=== DueDate Model Refactoring ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model currently mixes persistence logic, deadline calculation, and permission checking, violating SRP.  &lt;br /&gt;
Most of its logic is implemented as class methods, which makes testing and extension difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Current design&lt;br /&gt;
def self.copy(old_assignment_id, new_assignment_id)&lt;br /&gt;
  duedates = where(parent_id: old_assignment_id)&lt;br /&gt;
  duedates.each do |orig_due_date|&lt;br /&gt;
    new_due_date = orig_due_date.dup&lt;br /&gt;
    new_due_date.parent_id = new_assignment_id&lt;br /&gt;
    new_due_date.save&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Improved Design ====&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model is simpler, more modular, and focused on representing a single deadline entry.&lt;br /&gt;
Behavior previously embedded inside &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; has been extracted into appropriate modules:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Responsibility Distribution After Refactoring                        &lt;br /&gt;
|-&lt;br /&gt;
! Concern                                                              &lt;br /&gt;
! Where It Lives Now                                                   &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Permission checking                                                    &lt;br /&gt;
| &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module                                 &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Deadline-related actions (next deadline, stage name, deadline copying) &lt;br /&gt;
| &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module (included in parent models)         &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Deadline type semantics                                                &lt;br /&gt;
| &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model                                        &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Core deadline data (due_at, round, parent)                             &lt;br /&gt;
| &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model                                             &lt;br /&gt;
|}   &lt;br /&gt;
&lt;br /&gt;
===== Instance-Based Behavior Instead of Class Methods =====&lt;br /&gt;
&lt;br /&gt;
Previously, many behaviors were class methods (e.g., copying deadlines).&lt;br /&gt;
These methods did not reflect object-level behavior and often duplicated logic.&lt;br /&gt;
&lt;br /&gt;
The redesign moves these operations to:&lt;br /&gt;
&lt;br /&gt;
* instance methods for actions on a specific &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;&lt;br /&gt;
* modules (DueDateActions) for higher-level behaviors on assignments or topics&lt;br /&gt;
&lt;br /&gt;
This separation:&lt;br /&gt;
&lt;br /&gt;
* improves testability&lt;br /&gt;
* aligns with Rails conventions&lt;br /&gt;
* avoids state duplication&lt;br /&gt;
&lt;br /&gt;
===== Clearer Query Methods =====&lt;br /&gt;
&lt;br /&gt;
The model now exposes simple, intuitive instance-level questions:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Examples of Simplified State Queries    &lt;br /&gt;
|-&lt;br /&gt;
! Method                                  &lt;br /&gt;
! Meaning                                 &lt;br /&gt;
|-                                         &lt;br /&gt;
| &amp;lt;code&amp;gt;upcoming?&amp;lt;/code&amp;gt;                    &lt;br /&gt;
| Whether the deadline occurs in the future &lt;br /&gt;
|-                                         &lt;br /&gt;
| &amp;lt;code&amp;gt;overdue?&amp;lt;/code&amp;gt;                     &lt;br /&gt;
| Whether the deadline has already passed   &lt;br /&gt;
|-                                         &lt;br /&gt;
| &amp;lt;code&amp;gt;deadline_type_name&amp;lt;/code&amp;gt;           &lt;br /&gt;
| Human-readable deadline type              &lt;br /&gt;
|}                                         &lt;br /&gt;
&lt;br /&gt;
These small, composable predicates replace multiple ad-hoc comparisons scattered across the codebase.&lt;br /&gt;
&lt;br /&gt;
=== DueDatePermissions Mixin ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Currently, permission checks depend only on user roles and ignore the actual deadline.  &lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Role-based permissions (participant_helpers.rb)&lt;br /&gt;
def participant_permissions(authorization)&lt;br /&gt;
  case authorization&lt;br /&gt;
  when 'reader'   then { can_submit: false, can_review: true, can_take_quiz: true }&lt;br /&gt;
  when 'reviewer' then { can_submit: false, can_review: true, can_take_quiz: false }&lt;br /&gt;
  when 'submitter' then { can_submit: true, can_review: false, can_take_quiz: false }&lt;br /&gt;
  else { can_submit: true, can_review: true, can_take_quiz: true }&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And deadline-based checks exist separately in the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def submission_allowed(topic_id = nil)&lt;br /&gt;
  check_condition('submission_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
def can_review(topic_id = nil)&lt;br /&gt;
  check_condition('review_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These two layers (role-based and time-based) never interact, leading to inconsistencies (e.g., a user with &amp;lt;code&amp;gt;can_submit=true&amp;lt;/code&amp;gt; after deadline).&lt;br /&gt;
&lt;br /&gt;
==== Proposed Changes ====&lt;br /&gt;
&lt;br /&gt;
The new &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module integrates role-based and deadline-based checks.&lt;br /&gt;
Instead of embedding checks directly into &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;, all permission logic now flows through &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Meaning of Permission Methods                                             &lt;br /&gt;
! Method                                                                                  &lt;br /&gt;
! What It Determines                                                                                        &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| can_submit?                                                                                                 &lt;br /&gt;
| Submission is allowed only if (1) the submission deadline is OK/Late and (2) the participant’s role permits submitting. &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| can_review?                                                                                                 &lt;br /&gt;
| Review is allowed only if the review deadline is OK/Late and the participant has review privileges.         &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| can_take_quiz?                                                                                              &lt;br /&gt;
| Quiz access is controlled by both quiz deadlines and participant quiz permissions.                          &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| can_review_teammate?                                                                                        &lt;br /&gt;
| Teammate review uses both the teammate-review deadline and participant review rights.                       &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| activity_permissible?                                                                                       &lt;br /&gt;
| Unified checker for any action, mapped through a dynamically computed &amp;lt;code&amp;gt;*_allowed_id&amp;lt;/code&amp;gt;.            &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| permission_status_for(action)                                                                               &lt;br /&gt;
| Returns &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt; for display and UI logic.                    &lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
These methods handle all the interactions between role state, deadline state, and the user’s participant record.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Integration with the DueDate Model ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model includes the &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module so that each deadline object inherently knows:&lt;br /&gt;
&lt;br /&gt;
* whether an action is allowed at that time&lt;br /&gt;
* whether lateness is permitted&lt;br /&gt;
* what the current permission status is&lt;br /&gt;
* how to evaluate participant-specific permissions&lt;br /&gt;
&lt;br /&gt;
=== DueDateActions Mixin ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; mixin provides a unified interface for models that own due dates, such as &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt;.&lt;br /&gt;
This module centralizes the logic for querying upcoming deadlines and determining whether specific activities are currently allowed.&lt;br /&gt;
&lt;br /&gt;
This solves the previous issue where each model re-implemented its own partial or duplicated deadline logic.&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The mixin provides:&lt;br /&gt;
&lt;br /&gt;
* A generic mechanism to determine whether an action is currently permissible&lt;br /&gt;
* Convenience methods for commonly used actions (submission, review, quiz, etc.)&lt;br /&gt;
* Unified logic for resolving topic-specific vs assignment-level deadlines&lt;br /&gt;
* A consistent method for retrieving the “next” deadline&lt;br /&gt;
* Utility methods for copying due dates between models&lt;br /&gt;
&lt;br /&gt;
This leads to cleaner parent models and avoids repeated logic scattered across the codebase.&lt;br /&gt;
&lt;br /&gt;
==== Proposed Changes ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Summary of Methods in &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! Method&lt;br /&gt;
! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;activity_permissible?(activity)&amp;lt;/code&amp;gt;&lt;br /&gt;
| Generic checker that evaluates whether a given activity is permitted at the current time, based on the resolved due date.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;submission_permissible?&amp;lt;/code&amp;gt;&lt;br /&gt;
| rowspan=&amp;quot;6&amp;quot; | Convenience wrappers around &amp;lt;code&amp;gt;activity_permissible?&amp;lt;/code&amp;gt; that provide activity-specific entry points while delegating the actual permission logic to the generic method.  &lt;br /&gt;
Mainly used to keep calling code and views readable, while ensuring all checks still go through the same underlying mechanism.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;review_permissible?&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;teammate_review_permissible?&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;quiz_permissible?&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;team_formation_permissible?&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;drop_topic_permissible?&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;next_due_date(topic_id)&amp;lt;/code&amp;gt;&lt;br /&gt;
| Resolves the next applicable deadline, preferring topic-specific deadlines when they exist and falling back to assignment-level deadlines otherwise.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;current_stage&amp;lt;/code&amp;gt;&lt;br /&gt;
| Returns a human-readable stage name (e.g., “review”, “submission”, or “finished”) based on the next due date. Useful for UI display.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;has_topic_specific_deadlines?&amp;lt;/code&amp;gt;&lt;br /&gt;
| Determines whether the assignment uses staggered or topic-specific deadlines.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;copy_due_dates_to(new_parent)&amp;lt;/code&amp;gt;&lt;br /&gt;
| Utility for cloning an entire set of due dates to another parent object (e.g., duplicating an assignment).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== How Deadline Resolution Works ====&lt;br /&gt;
&lt;br /&gt;
When checking permissions or determining the current stage, the mixin:&lt;br /&gt;
&lt;br /&gt;
# Checks for topic-specific deadlines if a topic ID is provided&lt;br /&gt;
# Selects the earliest upcoming deadline (topic-level or assignment-level)&lt;br /&gt;
# Delegates permission checking to that deadline’s logic&lt;br /&gt;
&lt;br /&gt;
This creates a consistent and predictable flow:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Deadline Resolution Pipeline                                                           &lt;br /&gt;
|-&lt;br /&gt;
! Step                                                                                   &lt;br /&gt;
! Description                                                                            &lt;br /&gt;
|-                                                                                        &lt;br /&gt;
| 1. Topic-specific?                                                                       &lt;br /&gt;
| If the assignment uses staggered/topic deadlines, topic-level entries are checked first. &lt;br /&gt;
|-                                                                                        &lt;br /&gt;
| 2. Assignment-level fallback                                                             &lt;br /&gt;
| If no applicable topic deadline exists, the nearest assignment-level deadline is used.   &lt;br /&gt;
|-                                                                                        &lt;br /&gt;
| 3. Permission evaluation                                                                 &lt;br /&gt;
| &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; decides whether the action is allowed (OK / Late / No).  &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Implementation Summary ==&lt;br /&gt;
&lt;br /&gt;
=== Models Created/Modified ===&lt;br /&gt;
&lt;br /&gt;
* '''DeadlineType''' - Canonical deadline type definitions with semantic helper methods&lt;br /&gt;
* '''DeadlineRight''' - Permission state model (OK/Late/No) with comparison methods&lt;br /&gt;
* '''DueDate''' - Refactored with instance methods, scopes, and permission checking&lt;br /&gt;
* '''TopicDueDate''' - STI subclass for topic-specific deadlines&lt;br /&gt;
* '''Assignment''' - Includes DueDateActions mixin&lt;br /&gt;
* '''SignUpTopic''' - Includes DueDateActions mixin&lt;br /&gt;
&lt;br /&gt;
=== Modules Created ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions''' - Permission checking combining deadline and role constraints&lt;br /&gt;
* '''DueDateActions''' - Deadline-related operations for parent models (Assignment, SignUpTopic)&lt;br /&gt;
&lt;br /&gt;
=== Key Improvements ===&lt;br /&gt;
&lt;br /&gt;
# '''Separation of Concerns''': Permission logic separated into dedicated modules&lt;br /&gt;
# '''Instance Methods''': Replaced class methods with testable instance methods&lt;br /&gt;
# '''Polymorphic Design''': Single DueDate model serves both Assignment and SignUpTopic&lt;br /&gt;
# '''Unified Permissions''': Role-based and time-based checks combined in one interface&lt;br /&gt;
# '''Data Integrity''': Removed duplicate deadline types, enforced foreign key constraints&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
The test suite includes comprehensive RSpec tests for the ''DueDate'' model and its associated models (''DeadlineType'', ''DeadlineRight'').&lt;br /&gt;
These tests verify model validations, scopes, instance methods, class methods, and callbacks with extensive coverage of edge cases and error conditions.&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
The test suite follows RSpec best practices with:&lt;br /&gt;
* '''Nested contexts''' for different scenarios (e.g., &amp;quot;when parent is missing&amp;quot;, &amp;quot;when round is 0&amp;quot;)&lt;br /&gt;
* '''Multiple assertions per context''' to verify behavior from different angles&lt;br /&gt;
* '''Edge case coverage''' including empty collections, nil values, and boundary conditions&lt;br /&gt;
* '''Error case testing''' for invalid data and non-existent records&lt;br /&gt;
* '''Clear descriptive names''' using &amp;quot;context 'when...'&amp;quot; and &amp;quot;it 'does something'&amp;quot; patterns&lt;br /&gt;
&lt;br /&gt;
=== Current Coverage ===&lt;br /&gt;
&lt;br /&gt;
==== Associations ====&lt;br /&gt;
* Tests verify &amp;lt;code&amp;gt;belongs_to :parent&amp;lt;/code&amp;gt; (polymorphic) and &amp;lt;code&amp;gt;belongs_to :deadline_type&amp;lt;/code&amp;gt; relationships&lt;br /&gt;
&lt;br /&gt;
==== Validations ====&lt;br /&gt;
* '''Required field validation''': Tests for parent, due_at, and deadline_type_id presence&lt;br /&gt;
* '''Round validation''': Tests for positive values, zero rejection, negative rejection, and nil handling&lt;br /&gt;
* '''Error messages''': Verifies specific error messages are added to appropriate fields&lt;br /&gt;
* '''Valid record creation''': Confirms records persist when all requirements are met&lt;br /&gt;
&lt;br /&gt;
==== Scopes ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;''': Returns future due dates ordered by due_at, excludes past dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;''': Returns past due dates ordered by due_at, excludes future dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;''': Filters by round number, handles non-existent rounds&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt;''': Filters by deadline type name, handles non-existent types&lt;br /&gt;
&lt;br /&gt;
==== Instance Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#overdue?&amp;lt;/code&amp;gt;''': Tests past dates (true), future dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#upcoming?&amp;lt;/code&amp;gt;''': Tests future dates (true), past dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#set&amp;lt;/code&amp;gt;''': Verifies updating deadline_type_id, parent_id, and round with persistence and error handling&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#copy&amp;lt;/code&amp;gt;''': Tests duplication to new assignment, attribute preservation, error handling for non-existent targets&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#deadline_type_name&amp;lt;/code&amp;gt;''': Returns associated deadline type name, handles nil cases&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#last_deadline?&amp;lt;/code&amp;gt;''': Tests detection of final deadline, handles multiple deadlines and single deadline scenarios&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#&amp;lt;=&amp;gt;&amp;lt;/code&amp;gt;''': Tests comparison by due_at time, handles non-DueDate objects&lt;br /&gt;
&lt;br /&gt;
==== Class Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.sort_due_dates&amp;lt;/code&amp;gt;''': Sorts by due_at ascending, handles empty arrays and single elements&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.any_future_due_dates?&amp;lt;/code&amp;gt;''': Detects future dates in collections, handles empty arrays and mixed date collections&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.copy&amp;lt;/code&amp;gt;''': Copies all due dates between assignments, preserves attributes, handles empty sources and non-existent records&lt;br /&gt;
&lt;br /&gt;
==== Callbacks ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;before_save :set_default_round&amp;lt;/code&amp;gt;''': Sets round to 1 when not specified, preserves explicit values, handles nil&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
Tests follow RSpec best practices with nested contexts for different scenarios:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
describe 'validations' do&lt;br /&gt;
  context 'when parent is missing' do&lt;br /&gt;
    it 'is invalid' do&lt;br /&gt;
      expect(due_date).to be_invalid&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'has error on parent field' do&lt;br /&gt;
      due_date.valid?&lt;br /&gt;
      expect(due_date.errors[:parent]).to include('must exist')&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when round validation' do&lt;br /&gt;
    context 'with round value of 0' do&lt;br /&gt;
      it 'is invalid' do&lt;br /&gt;
        expect(due_date).to be_invalid&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'with nil round' do&lt;br /&gt;
      it 'sets default round to 1' do&lt;br /&gt;
        expect(due_date.round).to eq(1)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
=== Demo Link ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add deadline notification system using the new permission framework&lt;br /&gt;
* Implement deadline templates for common assignment patterns&lt;br /&gt;
* Add API endpoints for mobile app integration&lt;br /&gt;
* Create admin dashboard for monitoring deadline compliance across courses&lt;br /&gt;
* Add automated deadline extension workflows based on configurable rules&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167203</id>
		<title>CSC/ECE 517 Fall 2025 - E2566. Finish DueDates</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167203"/>
		<updated>2025-12-02T05:07:02Z</updated>

		<summary type="html">&lt;p&gt;Skim253: /* DueDateActions Mixin */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
This project aims to complete the implementation of the DueDate system in Expertiza. The current implementation consists mostly of class methods and lacks proper definition of different deadline types and permission checking functionality. This redesign will create a more robust, readable, and maintainable due date system.&lt;br /&gt;
&lt;br /&gt;
=== Existing Components ===&lt;br /&gt;
* &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model with polymorphic parent association (Assignment/SignUpTopic)&lt;br /&gt;
* &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; subclasses&lt;br /&gt;
* Basic CRUD operations (within &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AssignmentForm&amp;lt;/code&amp;gt;) and sorting functionality (&amp;lt;code&amp;gt;deadline_sort&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Database schema with &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt; field&lt;br /&gt;
&lt;br /&gt;
=== Current Behavior ===&lt;br /&gt;
&lt;br /&gt;
====Admin's View====&lt;br /&gt;
[[File:Duedates.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* When editing an assignment, due dates can be modified under the Due Dates tab.&lt;br /&gt;
* Each row contains the following columns: &amp;lt;code&amp;gt;Date &amp;amp; Time&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Use Date Updater&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Submission Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Teammate Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Meta-Review Allowed&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Reminder&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:Assignments-CurrentStage.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* In the Assignments table, the ''DueDate'' of each assignment is displayed as Current Stage and Stage Deadline.&lt;br /&gt;
&lt;br /&gt;
====Student's View====&lt;br /&gt;
[[File:Student-assignments.png|600px]]&lt;br /&gt;
* Students can view the due date information under &amp;lt;code&amp;gt;Current State&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Stage Deadline&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
* The actions available to students are determined by the current DueDate status.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-not-over.png|600px]]&lt;br /&gt;
* For example, during the submission period, students can access the &amp;lt;code&amp;gt;Submit a hyperlink&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Submit a file&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-over.png|600px]]&lt;br /&gt;
* Once the due date has passed, students cannot submit either a hyperlink or a file.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Currently, there are some glaring issues with how due_dates was created in the first place, including redundancy and lack of modularity.&lt;br /&gt;
&lt;br /&gt;
Modeling &amp;amp; Structural Issues&lt;br /&gt;
# No dedicated &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to define different kinds of deadlines. The existing &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; class only serves as an association for &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; models, and is never referenced in the current &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; implementation.&lt;br /&gt;
# Duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table, which may lead to potential bugs.&lt;br /&gt;
# Incomplete deadline type definitions - &amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt; need to be added.&lt;br /&gt;
# No clear separation of concerns - data persistence, business logic, and permission logic are mixed in a single model.&lt;br /&gt;
&lt;br /&gt;
Permission &amp;amp; Behavior Issues&lt;br /&gt;
# Overuse of class methods making the code difficult to maintain.&lt;br /&gt;
# Missing permission checking logic for user actions.&lt;br /&gt;
&lt;br /&gt;
=== Tasks Identified ===&lt;br /&gt;
&lt;br /&gt;
Modeling &amp;amp; Structural Issues&lt;br /&gt;
# Refactor the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model to separate persistence, business logic, and permission logic into distinct layers.&lt;br /&gt;
# Implement reusable mixins (e.g., &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;) to handle permission evaluation and deadline-related operations.&lt;br /&gt;
# Introduce instance-level methods (e.g., &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;) to replace class-level utilities and improve testability.&lt;br /&gt;
&lt;br /&gt;
Permission &amp;amp; Behavior Issues&lt;br /&gt;
# Integrate role-based and time-based permission checks within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; to ensure consistent access control.&lt;br /&gt;
# Redesign the &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.&lt;br /&gt;
# Add missing deadline types (&amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt;) and remove duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries for data integrity.&lt;br /&gt;
&lt;br /&gt;
== Proposed Solution ==&lt;br /&gt;
The proposed solution restructures the due date system by separating concerns across dedicated models and mixin modules.&lt;br /&gt;
Deadline definitions (&amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt;), core deadline data (&amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;), and behavioral logic (&amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;) are isolated so each component has a single clear responsibility.&lt;br /&gt;
This modular design improves readability, reduces coupling, and makes deadline-related behavior easier to extend and maintain.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Duedate-diagram.png|600px]]&lt;br /&gt;
&lt;br /&gt;
This diagram visualizes the redesigned architecture: blue/red/green indicate Rails models, orange indicates modules. &lt;br /&gt;
Parent models call DueDateActions, which manages DueDate, while DueDatePermissions checks user rights using reference models. &lt;br /&gt;
&lt;br /&gt;
=== DeadlineType Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Although the current codebase includes a &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model, it only serves as a passive lookup table for associations with &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt;.  &lt;br /&gt;
In practice, most parts of the system still rely on hard-coded numeric IDs or manual lookups such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Example of current usage&lt;br /&gt;
deadline_type_id = DeadlineType.find_by(name: 'review').id&lt;br /&gt;
if due_date.deadline_type_id == deadline_type_id&lt;br /&gt;
  # Perform review-related logic&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of using the model as an active domain object, &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; directly compares IDs or string literals.  &lt;br /&gt;
This leads to several structural issues:&lt;br /&gt;
&lt;br /&gt;
* Inconsistent references (some use IDs, others strings)&lt;br /&gt;
* Tight coupling between &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
* Lack of helper semantics (e.g., &amp;lt;code&amp;gt;is_review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;is_submission?&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Data duplication and integrity risks (two &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; rows)&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; will serve as a source of truth, defining all valid deadline types and providing meaningful interfaces.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table defines all canonical deadline categories used by the system.&lt;br /&gt;
Each row represents one distinct deadline type, and every &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; record must reference one of these entries through &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Below is a structural description of the table:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; Table Structure                                                    &lt;br /&gt;
|- &lt;br /&gt;
! Column                                                                                         &lt;br /&gt;
! Type                                                                                           &lt;br /&gt;
! Description                                                                                    &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| id                                                                                               &lt;br /&gt;
| BIGINT (Primary Key)                                                                             &lt;br /&gt;
| Unique identifier for each deadline type; referenced by &amp;lt;code&amp;gt;due_dates.deadline_type_id&amp;lt;/code&amp;gt;. &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| name                                                                                             &lt;br /&gt;
| VARCHAR(255)                                                                                     &lt;br /&gt;
| Symbolic name (e.g., &amp;lt;code&amp;gt;submission&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review&amp;lt;/code&amp;gt;); must be unique.              &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| description                                                                                      &lt;br /&gt;
| TEXT                                                                                             &lt;br /&gt;
| Human-readable explanation of the deadline category.                                             &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| created_at                                                                                       &lt;br /&gt;
| TIMESTAMP                                                                                        &lt;br /&gt;
| Time when the row was created.                                                                   &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| updated_at                                                                                       &lt;br /&gt;
| TIMESTAMP                                                                                        &lt;br /&gt;
| Time when the row was last updated.                                                              &lt;br /&gt;
|}                                                                                                &lt;br /&gt;
&lt;br /&gt;
This schema ensures that:&lt;br /&gt;
&lt;br /&gt;
* Each deadline type has a unique name and description.&lt;br /&gt;
* No duplicate entries can exist (e.g., the prior duplicated &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; was cleaned).&lt;br /&gt;
* All &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; rows reference this master table through &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
* The &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; column acts as the authoritative key used throughout permissions and due-date logic.&lt;br /&gt;
&lt;br /&gt;
Migrated constraints also ensure that &amp;lt;code&amp;gt;due_dates.deadline_type_id&amp;lt;/code&amp;gt; is consistently stored as &amp;lt;code&amp;gt;BIGINT&amp;lt;/code&amp;gt;, allowing a long-term stable keyspace.&lt;br /&gt;
&lt;br /&gt;
==== Deadline Type Definitions ====&lt;br /&gt;
&lt;br /&gt;
Because each row in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; is referenced by &amp;lt;code&amp;gt;DueDate.deadline_type_id&amp;lt;/code&amp;gt;, the following definitions map directly to the schema above.&lt;br /&gt;
&lt;br /&gt;
Each ID corresponds exactly to the &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; field in the table and serves as the system-wide canonical reference for that deadline category.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Canonical Deadline Type Entries                                       &lt;br /&gt;
|-&lt;br /&gt;
! ID                                                                    &lt;br /&gt;
! Name                                                                  &lt;br /&gt;
! Description                                                           &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 1                                                                       &lt;br /&gt;
| submission                                                              &lt;br /&gt;
| Student submission deadlines                                            &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 2                                                                       &lt;br /&gt;
| review                                                                  &lt;br /&gt;
| Peer review deadlines                                                   &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 3                                                                       &lt;br /&gt;
| teammate_review                                                         &lt;br /&gt;
| Team member evaluation deadlines                                        &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 5                                                                       &lt;br /&gt;
| metareview                                                              &lt;br /&gt;
| Meta-review deadlines (kept for backward compatibility)                 &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 6                                                                       &lt;br /&gt;
| drop_topic                                                              &lt;br /&gt;
| Topic drop deadlines                                                    &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 7                                                                       &lt;br /&gt;
| signup                                                                  &lt;br /&gt;
| Topic signup deadlines                                                  &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 8                                                                       &lt;br /&gt;
| team_formation                                                          &lt;br /&gt;
| Team formation deadlines (duplicate entries cleaned; ID 8 is canonical) &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 11                                                                      &lt;br /&gt;
| quiz                                                                    &lt;br /&gt;
| Quiz completion deadlines                                               &lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
These definitions are seeded during migration and act as the source of truth for all deadline-related logic within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;.&lt;br /&gt;
By linking each &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; to one of these entries through &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;, the system standardizes behavior and eliminates inconsistent ID or string comparisons from earlier implementations.&lt;br /&gt;
&lt;br /&gt;
==== DeadlineType Model Implementation ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineType &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
  validates :description, presence: true&lt;br /&gt;
&lt;br /&gt;
  has_many :due_dates, foreign_key: :deadline_type_id, dependent: :restrict_with_exception&lt;br /&gt;
&lt;br /&gt;
  # Semantic helper methods for deadline type identification&lt;br /&gt;
  def submission?&lt;br /&gt;
    name == 'submission'&lt;br /&gt;
  end&lt;br /&gt;
  # same code for review, teammate_review, quiz, team_formation, signup, drop_topic   &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DeadlineRight Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model represents the permission level for a specific action at a given deadline.&lt;br /&gt;
It defines three states: &lt;br /&gt;
* &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt; : Action is allowed without penalty&lt;br /&gt;
* &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt; : Action is allowed but marked as late&lt;br /&gt;
* &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt; : Action is not permitted&lt;br /&gt;
&lt;br /&gt;
These values are referenced by &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; through fields such as &amp;lt;code&amp;gt;submission_allowed_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review_allowed_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz_allowed_id&amp;lt;/code&amp;gt;, and others.&lt;br /&gt;
This allows the system to determine user permissions by combining:&lt;br /&gt;
&lt;br /&gt;
# The current deadline’s allowed state&lt;br /&gt;
# The participant’s role-based ability (submit, review, take quiz)&lt;br /&gt;
&lt;br /&gt;
This combination logic is implemented in &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
The &amp;lt;code&amp;gt;deadline_rights&amp;lt;/code&amp;gt; table stores the canonical permission states used across the entire due-date system.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_rights&amp;lt;/code&amp;gt; Table Structure                                                         &lt;br /&gt;
|-&lt;br /&gt;
! Column                                                                                               &lt;br /&gt;
! Type                                                                                                 &lt;br /&gt;
! Description                                                                                          &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| id                                                                                                     &lt;br /&gt;
| BIGINT (Primary Key)                                                                                  &lt;br /&gt;
| Unique identifier for each permission state. Referenced by &amp;lt;code&amp;gt;due_dates.*_allowed_id&amp;lt;/code&amp;gt; fields. &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| name                                                                                                   &lt;br /&gt;
| VARCHAR(255)                                                                                           &lt;br /&gt;
| Symbolic permission name (&amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;). Must be unique.        &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| description                                                                                            &lt;br /&gt;
| TEXT                                                                                                   &lt;br /&gt;
| Human-readable explanation of what the permission state means.                                         &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| created_at                                                                                             &lt;br /&gt;
| TIMESTAMP                                                                                              &lt;br /&gt;
| Timestamp when the entry was created.                                                                  &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| updated_at                                                                                             &lt;br /&gt;
| TIMESTAMP                                                                                              &lt;br /&gt;
| Timestamp when the entry was last updated.                                                             &lt;br /&gt;
|}  &lt;br /&gt;
&lt;br /&gt;
==== DeadlineRight Model ====&lt;br /&gt;
Instead of embedding permission logic inside &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;, the &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model defines the meaning of each permission state.&lt;br /&gt;
This removes magic strings (&amp;lt;code&amp;gt;&amp;quot;OK&amp;quot;&amp;lt;/code&amp;gt;) and numeric comparisons and replaces them with expressive model-level semantics.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Semantic Meaning Provided by &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt;                    &lt;br /&gt;
|-&lt;br /&gt;
! Concept                                                                    &lt;br /&gt;
! Meaning                                                                    &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| allows_action?                                                               &lt;br /&gt;
| True for &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;; false for &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;.    &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| denies_action?                                                               &lt;br /&gt;
| True only for &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;.                                               &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| allows_with_penalty?                                                         &lt;br /&gt;
| True only for &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;.                                             &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| allows_without_penalty?                                                      &lt;br /&gt;
| True only for &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.                                               &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| permission_level                                                             &lt;br /&gt;
| Numeric ordering: No = 0, Late = 1, OK = 2. Used for comparison and sorting. &lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
These behaviors allow other parts of the system—particularly &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;—to make consistent, readable decisions.&lt;br /&gt;
&lt;br /&gt;
=== DueDate Model Refactoring ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model currently mixes persistence logic, deadline calculation, and permission checking, violating SRP.  &lt;br /&gt;
Most of its logic is implemented as class methods, which makes testing and extension difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Current design&lt;br /&gt;
def self.copy(old_assignment_id, new_assignment_id)&lt;br /&gt;
  duedates = where(parent_id: old_assignment_id)&lt;br /&gt;
  duedates.each do |orig_due_date|&lt;br /&gt;
    new_due_date = orig_due_date.dup&lt;br /&gt;
    new_due_date.parent_id = new_assignment_id&lt;br /&gt;
    new_due_date.save&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Improved Design ====&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model is simpler, more modular, and focused on representing a single deadline entry.&lt;br /&gt;
Behavior previously embedded inside &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; has been extracted into appropriate modules:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Responsibility Distribution After Refactoring                        &lt;br /&gt;
|-&lt;br /&gt;
! Concern                                                              &lt;br /&gt;
! Where It Lives Now                                                   &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Permission checking                                                    &lt;br /&gt;
| &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module                                 &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Deadline-related actions (next deadline, stage name, deadline copying) &lt;br /&gt;
| &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module (included in parent models)         &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Deadline type semantics                                                &lt;br /&gt;
| &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model                                        &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Core deadline data (due_at, round, parent)                             &lt;br /&gt;
| &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model                                             &lt;br /&gt;
|}   &lt;br /&gt;
&lt;br /&gt;
===== Instance-Based Behavior Instead of Class Methods =====&lt;br /&gt;
&lt;br /&gt;
Previously, many behaviors were class methods (e.g., copying deadlines).&lt;br /&gt;
These methods did not reflect object-level behavior and often duplicated logic.&lt;br /&gt;
&lt;br /&gt;
The redesign moves these operations to:&lt;br /&gt;
&lt;br /&gt;
* instance methods for actions on a specific &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;&lt;br /&gt;
* modules (DueDateActions) for higher-level behaviors on assignments or topics&lt;br /&gt;
&lt;br /&gt;
This separation:&lt;br /&gt;
&lt;br /&gt;
* improves testability&lt;br /&gt;
* aligns with Rails conventions&lt;br /&gt;
* avoids state duplication&lt;br /&gt;
&lt;br /&gt;
===== Clearer Query Methods =====&lt;br /&gt;
&lt;br /&gt;
The model now exposes simple, intuitive instance-level questions:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Examples of Simplified State Queries    &lt;br /&gt;
|-&lt;br /&gt;
! Method                                  &lt;br /&gt;
! Meaning                                 &lt;br /&gt;
|-                                         &lt;br /&gt;
| &amp;lt;code&amp;gt;upcoming?&amp;lt;/code&amp;gt;                    &lt;br /&gt;
| Whether the deadline occurs in the future &lt;br /&gt;
|-                                         &lt;br /&gt;
| &amp;lt;code&amp;gt;overdue?&amp;lt;/code&amp;gt;                     &lt;br /&gt;
| Whether the deadline has already passed   &lt;br /&gt;
|-                                         &lt;br /&gt;
| &amp;lt;code&amp;gt;deadline_type_name&amp;lt;/code&amp;gt;           &lt;br /&gt;
| Human-readable deadline type              &lt;br /&gt;
|}                                         &lt;br /&gt;
&lt;br /&gt;
These small, composable predicates replace multiple ad-hoc comparisons scattered across the codebase.&lt;br /&gt;
&lt;br /&gt;
=== DueDatePermissions Mixin ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Currently, permission checks depend only on user roles and ignore the actual deadline.  &lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Role-based permissions (participant_helpers.rb)&lt;br /&gt;
def participant_permissions(authorization)&lt;br /&gt;
  case authorization&lt;br /&gt;
  when 'reader'   then { can_submit: false, can_review: true, can_take_quiz: true }&lt;br /&gt;
  when 'reviewer' then { can_submit: false, can_review: true, can_take_quiz: false }&lt;br /&gt;
  when 'submitter' then { can_submit: true, can_review: false, can_take_quiz: false }&lt;br /&gt;
  else { can_submit: true, can_review: true, can_take_quiz: true }&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And deadline-based checks exist separately in the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def submission_allowed(topic_id = nil)&lt;br /&gt;
  check_condition('submission_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
def can_review(topic_id = nil)&lt;br /&gt;
  check_condition('review_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These two layers (role-based and time-based) never interact, leading to inconsistencies (e.g., a user with &amp;lt;code&amp;gt;can_submit=true&amp;lt;/code&amp;gt; after deadline).&lt;br /&gt;
&lt;br /&gt;
==== Proposed Changes ====&lt;br /&gt;
&lt;br /&gt;
The new &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module integrates role-based and deadline-based checks.&lt;br /&gt;
Instead of embedding checks directly into &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;, all permission logic now flows through &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Meaning of Permission Methods                                             &lt;br /&gt;
! Method                                                                                  &lt;br /&gt;
! What It Determines                                                                                        &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| can_submit?                                                                                                 &lt;br /&gt;
| Submission is allowed only if (1) the submission deadline is OK/Late and (2) the participant’s role permits submitting. &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| can_review?                                                                                                 &lt;br /&gt;
| Review is allowed only if the review deadline is OK/Late and the participant has review privileges.         &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| can_take_quiz?                                                                                              &lt;br /&gt;
| Quiz access is controlled by both quiz deadlines and participant quiz permissions.                          &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| can_review_teammate?                                                                                        &lt;br /&gt;
| Teammate review uses both the teammate-review deadline and participant review rights.                       &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| activity_permissible?                                                                                       &lt;br /&gt;
| Unified checker for any action, mapped through a dynamically computed &amp;lt;code&amp;gt;*_allowed_id&amp;lt;/code&amp;gt;.            &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| permission_status_for(action)                                                                               &lt;br /&gt;
| Returns &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt; for display and UI logic.                    &lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
These methods handle all the interactions between role state, deadline state, and the user’s participant record.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Integration with the DueDate Model ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model includes the &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module so that each deadline object inherently knows:&lt;br /&gt;
&lt;br /&gt;
* whether an action is allowed at that time&lt;br /&gt;
* whether lateness is permitted&lt;br /&gt;
* what the current permission status is&lt;br /&gt;
* how to evaluate participant-specific permissions&lt;br /&gt;
&lt;br /&gt;
=== DueDateActions Mixin ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; mixin provides a unified interface for models that own due dates, such as &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt;.&lt;br /&gt;
This module centralizes the logic for querying upcoming deadlines and determining whether specific activities are currently allowed.&lt;br /&gt;
&lt;br /&gt;
This solves the previous issue where each model re-implemented its own partial or duplicated deadline logic.&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The mixin provides:&lt;br /&gt;
&lt;br /&gt;
* A generic mechanism to determine whether an action is currently permissible&lt;br /&gt;
* Convenience methods for commonly used actions (submission, review, quiz, etc.)&lt;br /&gt;
* Unified logic for resolving topic-specific vs assignment-level deadlines&lt;br /&gt;
* A consistent method for retrieving the “next” deadline&lt;br /&gt;
* Utility methods for copying due dates between models&lt;br /&gt;
&lt;br /&gt;
This leads to cleaner parent models and avoids repeated logic scattered across the codebase.&lt;br /&gt;
&lt;br /&gt;
==== Proposed Changes ====&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Summary of Methods in &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
! Method&lt;br /&gt;
! Purpose&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;activity_permissible?(activity)&amp;lt;/code&amp;gt;&lt;br /&gt;
| Generic checker that evaluates whether a given activity is permitted at the current time, based on the resolved due date.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;submission_permissible?&amp;lt;/code&amp;gt;&lt;br /&gt;
| rowspan=&amp;quot;6&amp;quot; | Convenience wrappers around &amp;lt;code&amp;gt;activity_permissible?&amp;lt;/code&amp;gt; that provide activity-specific entry points while delegating the actual permission logic to the generic method.  &lt;br /&gt;
Mainly used to keep calling code and views readable, while ensuring all checks still go through the same underlying mechanism.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;review_permissible?&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;teammate_review_permissible?&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;quiz_permissible?&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;team_formation_permissible?&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;drop_topic_permissible?&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;next_due_date(topic_id)&amp;lt;/code&amp;gt;&lt;br /&gt;
| Resolves the next applicable deadline, preferring topic-specific deadlines when they exist and falling back to assignment-level deadlines otherwise.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;current_stage&amp;lt;/code&amp;gt;&lt;br /&gt;
| Returns a human-readable stage name (e.g., “review”, “submission”, or “finished”) based on the next due date. Useful for UI display.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;has_topic_specific_deadlines?&amp;lt;/code&amp;gt;&lt;br /&gt;
| Determines whether the assignment uses staggered or topic-specific deadlines.&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;copy_due_dates_to(new_parent)&amp;lt;/code&amp;gt;&lt;br /&gt;
| Utility for cloning an entire set of due dates to another parent object (e.g., duplicating an assignment).&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== How Deadline Resolution Works ====&lt;br /&gt;
&lt;br /&gt;
When checking permissions or determining the current stage, the mixin:&lt;br /&gt;
&lt;br /&gt;
# Checks for topic-specific deadlines if a topic ID is provided&lt;br /&gt;
# Selects the earliest upcoming deadline (topic-level or assignment-level)&lt;br /&gt;
# Delegates permission checking to that deadline’s logic&lt;br /&gt;
&lt;br /&gt;
This creates a consistent and predictable flow:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Deadline Resolution Pipeline                                                           &lt;br /&gt;
|-&lt;br /&gt;
! Step                                                                                   &lt;br /&gt;
! Description                                                                            &lt;br /&gt;
|-                                                                                        &lt;br /&gt;
| 1. Topic-specific?                                                                       &lt;br /&gt;
| If the assignment uses staggered/topic deadlines, topic-level entries are checked first. &lt;br /&gt;
|-                                                                                        &lt;br /&gt;
| 2. Assignment-level fallback                                                             &lt;br /&gt;
| If no applicable topic deadline exists, the nearest assignment-level deadline is used.   &lt;br /&gt;
|-                                                                                        &lt;br /&gt;
| 3. Permission evaluation                                                                 &lt;br /&gt;
| &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; decides whether the action is allowed (OK / Late / No).  &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Implementation Summary ==&lt;br /&gt;
&lt;br /&gt;
=== Models Created/Modified ===&lt;br /&gt;
&lt;br /&gt;
* '''DeadlineType''' - Canonical deadline type definitions with semantic helper methods&lt;br /&gt;
* '''DeadlineRight''' - Permission state model (OK/Late/No) with comparison methods&lt;br /&gt;
* '''DueDate''' - Refactored with instance methods, scopes, and permission checking&lt;br /&gt;
* '''TopicDueDate''' - STI subclass for topic-specific deadlines&lt;br /&gt;
* '''Assignment''' - Includes DueDateActions mixin&lt;br /&gt;
* '''SignUpTopic''' - Includes DueDateActions mixin&lt;br /&gt;
&lt;br /&gt;
=== Modules Created ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions''' - Permission checking combining deadline and role constraints&lt;br /&gt;
* '''DueDateActions''' - Deadline-related operations for parent models (Assignment, SignUpTopic)&lt;br /&gt;
&lt;br /&gt;
=== Key Improvements ===&lt;br /&gt;
&lt;br /&gt;
# '''Separation of Concerns''': Permission logic separated into dedicated modules&lt;br /&gt;
# '''Instance Methods''': Replaced class methods with testable instance methods&lt;br /&gt;
# '''Polymorphic Design''': Single DueDate model serves both Assignment and SignUpTopic&lt;br /&gt;
# '''Unified Permissions''': Role-based and time-based checks combined in one interface&lt;br /&gt;
# '''Data Integrity''': Removed duplicate deadline types, enforced foreign key constraints&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
The test suite includes comprehensive RSpec tests for the ''DueDate'' model and its associated models (''DeadlineType'', ''DeadlineRight'').&lt;br /&gt;
These tests verify model validations, scopes, instance methods, class methods, and callbacks with extensive coverage of edge cases and error conditions.&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
The test suite follows RSpec best practices with:&lt;br /&gt;
* '''Nested contexts''' for different scenarios (e.g., &amp;quot;when parent is missing&amp;quot;, &amp;quot;when round is 0&amp;quot;)&lt;br /&gt;
* '''Multiple assertions per context''' to verify behavior from different angles&lt;br /&gt;
* '''Edge case coverage''' including empty collections, nil values, and boundary conditions&lt;br /&gt;
* '''Error case testing''' for invalid data and non-existent records&lt;br /&gt;
* '''Clear descriptive names''' using &amp;quot;context 'when...'&amp;quot; and &amp;quot;it 'does something'&amp;quot; patterns&lt;br /&gt;
&lt;br /&gt;
=== Current Coverage ===&lt;br /&gt;
&lt;br /&gt;
==== Associations ====&lt;br /&gt;
* Tests verify &amp;lt;code&amp;gt;belongs_to :parent&amp;lt;/code&amp;gt; (polymorphic) and &amp;lt;code&amp;gt;belongs_to :deadline_type&amp;lt;/code&amp;gt; relationships&lt;br /&gt;
&lt;br /&gt;
==== Validations ====&lt;br /&gt;
* '''Required field validation''': Tests for parent, due_at, and deadline_type_id presence&lt;br /&gt;
* '''Round validation''': Tests for positive values, zero rejection, negative rejection, and nil handling&lt;br /&gt;
* '''Error messages''': Verifies specific error messages are added to appropriate fields&lt;br /&gt;
* '''Valid record creation''': Confirms records persist when all requirements are met&lt;br /&gt;
&lt;br /&gt;
==== Scopes ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;''': Returns future due dates ordered by due_at, excludes past dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;''': Returns past due dates ordered by due_at, excludes future dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;''': Filters by round number, handles non-existent rounds&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt;''': Filters by deadline type name, handles non-existent types&lt;br /&gt;
&lt;br /&gt;
==== Instance Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#overdue?&amp;lt;/code&amp;gt;''': Tests past dates (true), future dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#upcoming?&amp;lt;/code&amp;gt;''': Tests future dates (true), past dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#set&amp;lt;/code&amp;gt;''': Verifies updating deadline_type_id, parent_id, and round with persistence and error handling&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#copy&amp;lt;/code&amp;gt;''': Tests duplication to new assignment, attribute preservation, error handling for non-existent targets&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#deadline_type_name&amp;lt;/code&amp;gt;''': Returns associated deadline type name, handles nil cases&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#last_deadline?&amp;lt;/code&amp;gt;''': Tests detection of final deadline, handles multiple deadlines and single deadline scenarios&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#&amp;lt;=&amp;gt;&amp;lt;/code&amp;gt;''': Tests comparison by due_at time, handles non-DueDate objects&lt;br /&gt;
&lt;br /&gt;
==== Class Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.sort_due_dates&amp;lt;/code&amp;gt;''': Sorts by due_at ascending, handles empty arrays and single elements&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.any_future_due_dates?&amp;lt;/code&amp;gt;''': Detects future dates in collections, handles empty arrays and mixed date collections&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.copy&amp;lt;/code&amp;gt;''': Copies all due dates between assignments, preserves attributes, handles empty sources and non-existent records&lt;br /&gt;
&lt;br /&gt;
==== Callbacks ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;before_save :set_default_round&amp;lt;/code&amp;gt;''': Sets round to 1 when not specified, preserves explicit values, handles nil&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
Tests follow RSpec best practices with nested contexts for different scenarios:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
describe 'validations' do&lt;br /&gt;
  context 'when parent is missing' do&lt;br /&gt;
    it 'is invalid' do&lt;br /&gt;
      expect(due_date).to be_invalid&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'has error on parent field' do&lt;br /&gt;
      due_date.valid?&lt;br /&gt;
      expect(due_date.errors[:parent]).to include('must exist')&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when round validation' do&lt;br /&gt;
    context 'with round value of 0' do&lt;br /&gt;
      it 'is invalid' do&lt;br /&gt;
        expect(due_date).to be_invalid&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'with nil round' do&lt;br /&gt;
      it 'sets default round to 1' do&lt;br /&gt;
        expect(due_date.round).to eq(1)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
=== Demo Link ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add deadline notification system using the new permission framework&lt;br /&gt;
* Implement deadline templates for common assignment patterns&lt;br /&gt;
* Add API endpoints for mobile app integration&lt;br /&gt;
* Create admin dashboard for monitoring deadline compliance across courses&lt;br /&gt;
* Add automated deadline extension workflows based on configurable rules&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167202</id>
		<title>CSC/ECE 517 Fall 2025 - E2566. Finish DueDates</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167202"/>
		<updated>2025-12-02T04:56:07Z</updated>

		<summary type="html">&lt;p&gt;Skim253: /* DueDateActions Mixin */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
This project aims to complete the implementation of the DueDate system in Expertiza. The current implementation consists mostly of class methods and lacks proper definition of different deadline types and permission checking functionality. This redesign will create a more robust, readable, and maintainable due date system.&lt;br /&gt;
&lt;br /&gt;
=== Existing Components ===&lt;br /&gt;
* &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model with polymorphic parent association (Assignment/SignUpTopic)&lt;br /&gt;
* &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; subclasses&lt;br /&gt;
* Basic CRUD operations (within &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AssignmentForm&amp;lt;/code&amp;gt;) and sorting functionality (&amp;lt;code&amp;gt;deadline_sort&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Database schema with &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt; field&lt;br /&gt;
&lt;br /&gt;
=== Current Behavior ===&lt;br /&gt;
&lt;br /&gt;
====Admin's View====&lt;br /&gt;
[[File:Duedates.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* When editing an assignment, due dates can be modified under the Due Dates tab.&lt;br /&gt;
* Each row contains the following columns: &amp;lt;code&amp;gt;Date &amp;amp; Time&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Use Date Updater&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Submission Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Teammate Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Meta-Review Allowed&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Reminder&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:Assignments-CurrentStage.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* In the Assignments table, the ''DueDate'' of each assignment is displayed as Current Stage and Stage Deadline.&lt;br /&gt;
&lt;br /&gt;
====Student's View====&lt;br /&gt;
[[File:Student-assignments.png|600px]]&lt;br /&gt;
* Students can view the due date information under &amp;lt;code&amp;gt;Current State&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Stage Deadline&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
* The actions available to students are determined by the current DueDate status.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-not-over.png|600px]]&lt;br /&gt;
* For example, during the submission period, students can access the &amp;lt;code&amp;gt;Submit a hyperlink&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Submit a file&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-over.png|600px]]&lt;br /&gt;
* Once the due date has passed, students cannot submit either a hyperlink or a file.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Currently, there are some glaring issues with how due_dates was created in the first place, including redundancy and lack of modularity.&lt;br /&gt;
&lt;br /&gt;
Modeling &amp;amp; Structural Issues&lt;br /&gt;
# No dedicated &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to define different kinds of deadlines. The existing &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; class only serves as an association for &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; models, and is never referenced in the current &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; implementation.&lt;br /&gt;
# Duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table, which may lead to potential bugs.&lt;br /&gt;
# Incomplete deadline type definitions - &amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt; need to be added.&lt;br /&gt;
# No clear separation of concerns - data persistence, business logic, and permission logic are mixed in a single model.&lt;br /&gt;
&lt;br /&gt;
Permission &amp;amp; Behavior Issues&lt;br /&gt;
# Overuse of class methods making the code difficult to maintain.&lt;br /&gt;
# Missing permission checking logic for user actions.&lt;br /&gt;
&lt;br /&gt;
=== Tasks Identified ===&lt;br /&gt;
&lt;br /&gt;
Modeling &amp;amp; Structural Issues&lt;br /&gt;
# Refactor the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model to separate persistence, business logic, and permission logic into distinct layers.&lt;br /&gt;
# Implement reusable mixins (e.g., &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;) to handle permission evaluation and deadline-related operations.&lt;br /&gt;
# Introduce instance-level methods (e.g., &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;) to replace class-level utilities and improve testability.&lt;br /&gt;
&lt;br /&gt;
Permission &amp;amp; Behavior Issues&lt;br /&gt;
# Integrate role-based and time-based permission checks within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; to ensure consistent access control.&lt;br /&gt;
# Redesign the &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.&lt;br /&gt;
# Add missing deadline types (&amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt;) and remove duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries for data integrity.&lt;br /&gt;
&lt;br /&gt;
== Proposed Solution ==&lt;br /&gt;
The proposed solution restructures the due date system by separating concerns across dedicated models and mixin modules.&lt;br /&gt;
Deadline definitions (&amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt;), core deadline data (&amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;), and behavioral logic (&amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;) are isolated so each component has a single clear responsibility.&lt;br /&gt;
This modular design improves readability, reduces coupling, and makes deadline-related behavior easier to extend and maintain.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Duedate-diagram.png|600px]]&lt;br /&gt;
&lt;br /&gt;
This diagram visualizes the redesigned architecture: blue/red/green indicate Rails models, orange indicates modules. &lt;br /&gt;
Parent models call DueDateActions, which manages DueDate, while DueDatePermissions checks user rights using reference models. &lt;br /&gt;
&lt;br /&gt;
=== DeadlineType Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Although the current codebase includes a &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model, it only serves as a passive lookup table for associations with &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt;.  &lt;br /&gt;
In practice, most parts of the system still rely on hard-coded numeric IDs or manual lookups such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Example of current usage&lt;br /&gt;
deadline_type_id = DeadlineType.find_by(name: 'review').id&lt;br /&gt;
if due_date.deadline_type_id == deadline_type_id&lt;br /&gt;
  # Perform review-related logic&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of using the model as an active domain object, &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; directly compares IDs or string literals.  &lt;br /&gt;
This leads to several structural issues:&lt;br /&gt;
&lt;br /&gt;
* Inconsistent references (some use IDs, others strings)&lt;br /&gt;
* Tight coupling between &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
* Lack of helper semantics (e.g., &amp;lt;code&amp;gt;is_review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;is_submission?&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Data duplication and integrity risks (two &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; rows)&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; will serve as a source of truth, defining all valid deadline types and providing meaningful interfaces.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table defines all canonical deadline categories used by the system.&lt;br /&gt;
Each row represents one distinct deadline type, and every &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; record must reference one of these entries through &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Below is a structural description of the table:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; Table Structure                                                    &lt;br /&gt;
|- &lt;br /&gt;
! Column                                                                                         &lt;br /&gt;
! Type                                                                                           &lt;br /&gt;
! Description                                                                                    &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| id                                                                                               &lt;br /&gt;
| BIGINT (Primary Key)                                                                             &lt;br /&gt;
| Unique identifier for each deadline type; referenced by &amp;lt;code&amp;gt;due_dates.deadline_type_id&amp;lt;/code&amp;gt;. &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| name                                                                                             &lt;br /&gt;
| VARCHAR(255)                                                                                     &lt;br /&gt;
| Symbolic name (e.g., &amp;lt;code&amp;gt;submission&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review&amp;lt;/code&amp;gt;); must be unique.              &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| description                                                                                      &lt;br /&gt;
| TEXT                                                                                             &lt;br /&gt;
| Human-readable explanation of the deadline category.                                             &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| created_at                                                                                       &lt;br /&gt;
| TIMESTAMP                                                                                        &lt;br /&gt;
| Time when the row was created.                                                                   &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| updated_at                                                                                       &lt;br /&gt;
| TIMESTAMP                                                                                        &lt;br /&gt;
| Time when the row was last updated.                                                              &lt;br /&gt;
|}                                                                                                &lt;br /&gt;
&lt;br /&gt;
This schema ensures that:&lt;br /&gt;
&lt;br /&gt;
* Each deadline type has a unique name and description.&lt;br /&gt;
* No duplicate entries can exist (e.g., the prior duplicated &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; was cleaned).&lt;br /&gt;
* All &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; rows reference this master table through &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
* The &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; column acts as the authoritative key used throughout permissions and due-date logic.&lt;br /&gt;
&lt;br /&gt;
Migrated constraints also ensure that &amp;lt;code&amp;gt;due_dates.deadline_type_id&amp;lt;/code&amp;gt; is consistently stored as &amp;lt;code&amp;gt;BIGINT&amp;lt;/code&amp;gt;, allowing a long-term stable keyspace.&lt;br /&gt;
&lt;br /&gt;
==== Deadline Type Definitions ====&lt;br /&gt;
&lt;br /&gt;
Because each row in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; is referenced by &amp;lt;code&amp;gt;DueDate.deadline_type_id&amp;lt;/code&amp;gt;, the following definitions map directly to the schema above.&lt;br /&gt;
&lt;br /&gt;
Each ID corresponds exactly to the &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; field in the table and serves as the system-wide canonical reference for that deadline category.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Canonical Deadline Type Entries                                       &lt;br /&gt;
|-&lt;br /&gt;
! ID                                                                    &lt;br /&gt;
! Name                                                                  &lt;br /&gt;
! Description                                                           &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 1                                                                       &lt;br /&gt;
| submission                                                              &lt;br /&gt;
| Student submission deadlines                                            &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 2                                                                       &lt;br /&gt;
| review                                                                  &lt;br /&gt;
| Peer review deadlines                                                   &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 3                                                                       &lt;br /&gt;
| teammate_review                                                         &lt;br /&gt;
| Team member evaluation deadlines                                        &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 5                                                                       &lt;br /&gt;
| metareview                                                              &lt;br /&gt;
| Meta-review deadlines (kept for backward compatibility)                 &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 6                                                                       &lt;br /&gt;
| drop_topic                                                              &lt;br /&gt;
| Topic drop deadlines                                                    &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 7                                                                       &lt;br /&gt;
| signup                                                                  &lt;br /&gt;
| Topic signup deadlines                                                  &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 8                                                                       &lt;br /&gt;
| team_formation                                                          &lt;br /&gt;
| Team formation deadlines (duplicate entries cleaned; ID 8 is canonical) &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 11                                                                      &lt;br /&gt;
| quiz                                                                    &lt;br /&gt;
| Quiz completion deadlines                                               &lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
These definitions are seeded during migration and act as the source of truth for all deadline-related logic within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;.&lt;br /&gt;
By linking each &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; to one of these entries through &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;, the system standardizes behavior and eliminates inconsistent ID or string comparisons from earlier implementations.&lt;br /&gt;
&lt;br /&gt;
==== DeadlineType Model Implementation ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineType &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
  validates :description, presence: true&lt;br /&gt;
&lt;br /&gt;
  has_many :due_dates, foreign_key: :deadline_type_id, dependent: :restrict_with_exception&lt;br /&gt;
&lt;br /&gt;
  # Semantic helper methods for deadline type identification&lt;br /&gt;
  def submission?&lt;br /&gt;
    name == 'submission'&lt;br /&gt;
  end&lt;br /&gt;
  # same code for review, teammate_review, quiz, team_formation, signup, drop_topic   &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DeadlineRight Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model represents the permission level for a specific action at a given deadline.&lt;br /&gt;
It defines three states: &lt;br /&gt;
* &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt; : Action is allowed without penalty&lt;br /&gt;
* &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt; : Action is allowed but marked as late&lt;br /&gt;
* &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt; : Action is not permitted&lt;br /&gt;
&lt;br /&gt;
These values are referenced by &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; through fields such as &amp;lt;code&amp;gt;submission_allowed_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review_allowed_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz_allowed_id&amp;lt;/code&amp;gt;, and others.&lt;br /&gt;
This allows the system to determine user permissions by combining:&lt;br /&gt;
&lt;br /&gt;
# The current deadline’s allowed state&lt;br /&gt;
# The participant’s role-based ability (submit, review, take quiz)&lt;br /&gt;
&lt;br /&gt;
This combination logic is implemented in &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
The &amp;lt;code&amp;gt;deadline_rights&amp;lt;/code&amp;gt; table stores the canonical permission states used across the entire due-date system.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_rights&amp;lt;/code&amp;gt; Table Structure                                                         &lt;br /&gt;
|-&lt;br /&gt;
! Column                                                                                               &lt;br /&gt;
! Type                                                                                                 &lt;br /&gt;
! Description                                                                                          &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| id                                                                                                     &lt;br /&gt;
| BIGINT (Primary Key)                                                                                  &lt;br /&gt;
| Unique identifier for each permission state. Referenced by &amp;lt;code&amp;gt;due_dates.*_allowed_id&amp;lt;/code&amp;gt; fields. &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| name                                                                                                   &lt;br /&gt;
| VARCHAR(255)                                                                                           &lt;br /&gt;
| Symbolic permission name (&amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;). Must be unique.        &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| description                                                                                            &lt;br /&gt;
| TEXT                                                                                                   &lt;br /&gt;
| Human-readable explanation of what the permission state means.                                         &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| created_at                                                                                             &lt;br /&gt;
| TIMESTAMP                                                                                              &lt;br /&gt;
| Timestamp when the entry was created.                                                                  &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| updated_at                                                                                             &lt;br /&gt;
| TIMESTAMP                                                                                              &lt;br /&gt;
| Timestamp when the entry was last updated.                                                             &lt;br /&gt;
|}  &lt;br /&gt;
&lt;br /&gt;
==== DeadlineRight Model ====&lt;br /&gt;
Instead of embedding permission logic inside &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;, the &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model defines the meaning of each permission state.&lt;br /&gt;
This removes magic strings (&amp;lt;code&amp;gt;&amp;quot;OK&amp;quot;&amp;lt;/code&amp;gt;) and numeric comparisons and replaces them with expressive model-level semantics.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Semantic Meaning Provided by &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt;                    &lt;br /&gt;
|-&lt;br /&gt;
! Concept                                                                    &lt;br /&gt;
! Meaning                                                                    &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| allows_action?                                                               &lt;br /&gt;
| True for &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;; false for &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;.    &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| denies_action?                                                               &lt;br /&gt;
| True only for &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;.                                               &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| allows_with_penalty?                                                         &lt;br /&gt;
| True only for &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;.                                             &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| allows_without_penalty?                                                      &lt;br /&gt;
| True only for &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.                                               &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| permission_level                                                             &lt;br /&gt;
| Numeric ordering: No = 0, Late = 1, OK = 2. Used for comparison and sorting. &lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
These behaviors allow other parts of the system—particularly &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;—to make consistent, readable decisions.&lt;br /&gt;
&lt;br /&gt;
=== DueDate Model Refactoring ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model currently mixes persistence logic, deadline calculation, and permission checking, violating SRP.  &lt;br /&gt;
Most of its logic is implemented as class methods, which makes testing and extension difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Current design&lt;br /&gt;
def self.copy(old_assignment_id, new_assignment_id)&lt;br /&gt;
  duedates = where(parent_id: old_assignment_id)&lt;br /&gt;
  duedates.each do |orig_due_date|&lt;br /&gt;
    new_due_date = orig_due_date.dup&lt;br /&gt;
    new_due_date.parent_id = new_assignment_id&lt;br /&gt;
    new_due_date.save&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Improved Design ====&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model is simpler, more modular, and focused on representing a single deadline entry.&lt;br /&gt;
Behavior previously embedded inside &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; has been extracted into appropriate modules:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Responsibility Distribution After Refactoring                        &lt;br /&gt;
|-&lt;br /&gt;
! Concern                                                              &lt;br /&gt;
! Where It Lives Now                                                   &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Permission checking                                                    &lt;br /&gt;
| &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module                                 &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Deadline-related actions (next deadline, stage name, deadline copying) &lt;br /&gt;
| &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module (included in parent models)         &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Deadline type semantics                                                &lt;br /&gt;
| &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model                                        &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Core deadline data (due_at, round, parent)                             &lt;br /&gt;
| &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model                                             &lt;br /&gt;
|}   &lt;br /&gt;
&lt;br /&gt;
===== Instance-Based Behavior Instead of Class Methods =====&lt;br /&gt;
&lt;br /&gt;
Previously, many behaviors were class methods (e.g., copying deadlines).&lt;br /&gt;
These methods did not reflect object-level behavior and often duplicated logic.&lt;br /&gt;
&lt;br /&gt;
The redesign moves these operations to:&lt;br /&gt;
&lt;br /&gt;
* instance methods for actions on a specific &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;&lt;br /&gt;
* modules (DueDateActions) for higher-level behaviors on assignments or topics&lt;br /&gt;
&lt;br /&gt;
This separation:&lt;br /&gt;
&lt;br /&gt;
* improves testability&lt;br /&gt;
* aligns with Rails conventions&lt;br /&gt;
* avoids state duplication&lt;br /&gt;
&lt;br /&gt;
===== Clearer Query Methods =====&lt;br /&gt;
&lt;br /&gt;
The model now exposes simple, intuitive instance-level questions:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Examples of Simplified State Queries    &lt;br /&gt;
|-&lt;br /&gt;
! Method                                  &lt;br /&gt;
! Meaning                                 &lt;br /&gt;
|-                                         &lt;br /&gt;
| &amp;lt;code&amp;gt;upcoming?&amp;lt;/code&amp;gt;                    &lt;br /&gt;
| Whether the deadline occurs in the future &lt;br /&gt;
|-                                         &lt;br /&gt;
| &amp;lt;code&amp;gt;overdue?&amp;lt;/code&amp;gt;                     &lt;br /&gt;
| Whether the deadline has already passed   &lt;br /&gt;
|-                                         &lt;br /&gt;
| &amp;lt;code&amp;gt;deadline_type_name&amp;lt;/code&amp;gt;           &lt;br /&gt;
| Human-readable deadline type              &lt;br /&gt;
|}                                         &lt;br /&gt;
&lt;br /&gt;
These small, composable predicates replace multiple ad-hoc comparisons scattered across the codebase.&lt;br /&gt;
&lt;br /&gt;
=== DueDatePermissions Mixin ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Currently, permission checks depend only on user roles and ignore the actual deadline.  &lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Role-based permissions (participant_helpers.rb)&lt;br /&gt;
def participant_permissions(authorization)&lt;br /&gt;
  case authorization&lt;br /&gt;
  when 'reader'   then { can_submit: false, can_review: true, can_take_quiz: true }&lt;br /&gt;
  when 'reviewer' then { can_submit: false, can_review: true, can_take_quiz: false }&lt;br /&gt;
  when 'submitter' then { can_submit: true, can_review: false, can_take_quiz: false }&lt;br /&gt;
  else { can_submit: true, can_review: true, can_take_quiz: true }&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And deadline-based checks exist separately in the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def submission_allowed(topic_id = nil)&lt;br /&gt;
  check_condition('submission_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
def can_review(topic_id = nil)&lt;br /&gt;
  check_condition('review_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These two layers (role-based and time-based) never interact, leading to inconsistencies (e.g., a user with &amp;lt;code&amp;gt;can_submit=true&amp;lt;/code&amp;gt; after deadline).&lt;br /&gt;
&lt;br /&gt;
==== Proposed Changes ====&lt;br /&gt;
&lt;br /&gt;
The new &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module integrates role-based and deadline-based checks.&lt;br /&gt;
Instead of embedding checks directly into &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;, all permission logic now flows through &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Meaning of Permission Methods                                             &lt;br /&gt;
! Method                                                                                  &lt;br /&gt;
! What It Determines                                                                                        &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| can_submit?                                                                                                 &lt;br /&gt;
| Submission is allowed only if (1) the submission deadline is OK/Late and (2) the participant’s role permits submitting. &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| can_review?                                                                                                 &lt;br /&gt;
| Review is allowed only if the review deadline is OK/Late and the participant has review privileges.         &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| can_take_quiz?                                                                                              &lt;br /&gt;
| Quiz access is controlled by both quiz deadlines and participant quiz permissions.                          &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| can_review_teammate?                                                                                        &lt;br /&gt;
| Teammate review uses both the teammate-review deadline and participant review rights.                       &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| activity_permissible?                                                                                       &lt;br /&gt;
| Unified checker for any action, mapped through a dynamically computed &amp;lt;code&amp;gt;*_allowed_id&amp;lt;/code&amp;gt;.            &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| permission_status_for(action)                                                                               &lt;br /&gt;
| Returns &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt; for display and UI logic.                    &lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
These methods handle all the interactions between role state, deadline state, and the user’s participant record.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Integration with the DueDate Model ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model includes the &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module so that each deadline object inherently knows:&lt;br /&gt;
&lt;br /&gt;
* whether an action is allowed at that time&lt;br /&gt;
* whether lateness is permitted&lt;br /&gt;
* what the current permission status is&lt;br /&gt;
* how to evaluate participant-specific permissions&lt;br /&gt;
&lt;br /&gt;
=== DueDateActions Mixin ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; mixin provides a unified interface for models that own due dates, such as &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt;.&lt;br /&gt;
This module centralizes the logic for querying upcoming deadlines and determining whether specific activities are currently allowed.&lt;br /&gt;
&lt;br /&gt;
This solves the previous issue where each model re-implemented its own partial or duplicated deadline logic.&lt;br /&gt;
&lt;br /&gt;
== Implementation Summary ==&lt;br /&gt;
&lt;br /&gt;
=== Models Created/Modified ===&lt;br /&gt;
&lt;br /&gt;
* '''DeadlineType''' - Canonical deadline type definitions with semantic helper methods&lt;br /&gt;
* '''DeadlineRight''' - Permission state model (OK/Late/No) with comparison methods&lt;br /&gt;
* '''DueDate''' - Refactored with instance methods, scopes, and permission checking&lt;br /&gt;
* '''TopicDueDate''' - STI subclass for topic-specific deadlines&lt;br /&gt;
* '''Assignment''' - Includes DueDateActions mixin&lt;br /&gt;
* '''SignUpTopic''' - Includes DueDateActions mixin&lt;br /&gt;
&lt;br /&gt;
=== Modules Created ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions''' - Permission checking combining deadline and role constraints&lt;br /&gt;
* '''DueDateActions''' - Deadline-related operations for parent models (Assignment, SignUpTopic)&lt;br /&gt;
&lt;br /&gt;
=== Key Improvements ===&lt;br /&gt;
&lt;br /&gt;
# '''Separation of Concerns''': Permission logic separated into dedicated modules&lt;br /&gt;
# '''Instance Methods''': Replaced class methods with testable instance methods&lt;br /&gt;
# '''Polymorphic Design''': Single DueDate model serves both Assignment and SignUpTopic&lt;br /&gt;
# '''Unified Permissions''': Role-based and time-based checks combined in one interface&lt;br /&gt;
# '''Data Integrity''': Removed duplicate deadline types, enforced foreign key constraints&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
The test suite includes comprehensive RSpec tests for the ''DueDate'' model and its associated models (''DeadlineType'', ''DeadlineRight'').&lt;br /&gt;
These tests verify model validations, scopes, instance methods, class methods, and callbacks with extensive coverage of edge cases and error conditions.&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
The test suite follows RSpec best practices with:&lt;br /&gt;
* '''Nested contexts''' for different scenarios (e.g., &amp;quot;when parent is missing&amp;quot;, &amp;quot;when round is 0&amp;quot;)&lt;br /&gt;
* '''Multiple assertions per context''' to verify behavior from different angles&lt;br /&gt;
* '''Edge case coverage''' including empty collections, nil values, and boundary conditions&lt;br /&gt;
* '''Error case testing''' for invalid data and non-existent records&lt;br /&gt;
* '''Clear descriptive names''' using &amp;quot;context 'when...'&amp;quot; and &amp;quot;it 'does something'&amp;quot; patterns&lt;br /&gt;
&lt;br /&gt;
=== Current Coverage ===&lt;br /&gt;
&lt;br /&gt;
==== Associations ====&lt;br /&gt;
* Tests verify &amp;lt;code&amp;gt;belongs_to :parent&amp;lt;/code&amp;gt; (polymorphic) and &amp;lt;code&amp;gt;belongs_to :deadline_type&amp;lt;/code&amp;gt; relationships&lt;br /&gt;
&lt;br /&gt;
==== Validations ====&lt;br /&gt;
* '''Required field validation''': Tests for parent, due_at, and deadline_type_id presence&lt;br /&gt;
* '''Round validation''': Tests for positive values, zero rejection, negative rejection, and nil handling&lt;br /&gt;
* '''Error messages''': Verifies specific error messages are added to appropriate fields&lt;br /&gt;
* '''Valid record creation''': Confirms records persist when all requirements are met&lt;br /&gt;
&lt;br /&gt;
==== Scopes ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;''': Returns future due dates ordered by due_at, excludes past dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;''': Returns past due dates ordered by due_at, excludes future dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;''': Filters by round number, handles non-existent rounds&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt;''': Filters by deadline type name, handles non-existent types&lt;br /&gt;
&lt;br /&gt;
==== Instance Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#overdue?&amp;lt;/code&amp;gt;''': Tests past dates (true), future dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#upcoming?&amp;lt;/code&amp;gt;''': Tests future dates (true), past dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#set&amp;lt;/code&amp;gt;''': Verifies updating deadline_type_id, parent_id, and round with persistence and error handling&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#copy&amp;lt;/code&amp;gt;''': Tests duplication to new assignment, attribute preservation, error handling for non-existent targets&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#deadline_type_name&amp;lt;/code&amp;gt;''': Returns associated deadline type name, handles nil cases&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#last_deadline?&amp;lt;/code&amp;gt;''': Tests detection of final deadline, handles multiple deadlines and single deadline scenarios&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#&amp;lt;=&amp;gt;&amp;lt;/code&amp;gt;''': Tests comparison by due_at time, handles non-DueDate objects&lt;br /&gt;
&lt;br /&gt;
==== Class Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.sort_due_dates&amp;lt;/code&amp;gt;''': Sorts by due_at ascending, handles empty arrays and single elements&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.any_future_due_dates?&amp;lt;/code&amp;gt;''': Detects future dates in collections, handles empty arrays and mixed date collections&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.copy&amp;lt;/code&amp;gt;''': Copies all due dates between assignments, preserves attributes, handles empty sources and non-existent records&lt;br /&gt;
&lt;br /&gt;
==== Callbacks ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;before_save :set_default_round&amp;lt;/code&amp;gt;''': Sets round to 1 when not specified, preserves explicit values, handles nil&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
Tests follow RSpec best practices with nested contexts for different scenarios:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
describe 'validations' do&lt;br /&gt;
  context 'when parent is missing' do&lt;br /&gt;
    it 'is invalid' do&lt;br /&gt;
      expect(due_date).to be_invalid&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'has error on parent field' do&lt;br /&gt;
      due_date.valid?&lt;br /&gt;
      expect(due_date.errors[:parent]).to include('must exist')&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when round validation' do&lt;br /&gt;
    context 'with round value of 0' do&lt;br /&gt;
      it 'is invalid' do&lt;br /&gt;
        expect(due_date).to be_invalid&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'with nil round' do&lt;br /&gt;
      it 'sets default round to 1' do&lt;br /&gt;
        expect(due_date.round).to eq(1)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
=== Demo Link ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add deadline notification system using the new permission framework&lt;br /&gt;
* Implement deadline templates for common assignment patterns&lt;br /&gt;
* Add API endpoints for mobile app integration&lt;br /&gt;
* Create admin dashboard for monitoring deadline compliance across courses&lt;br /&gt;
* Add automated deadline extension workflows based on configurable rules&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167201</id>
		<title>CSC/ECE 517 Fall 2025 - E2566. Finish DueDates</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167201"/>
		<updated>2025-12-02T04:54:49Z</updated>

		<summary type="html">&lt;p&gt;Skim253: /* DueDatePermission Mixin */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
This project aims to complete the implementation of the DueDate system in Expertiza. The current implementation consists mostly of class methods and lacks proper definition of different deadline types and permission checking functionality. This redesign will create a more robust, readable, and maintainable due date system.&lt;br /&gt;
&lt;br /&gt;
=== Existing Components ===&lt;br /&gt;
* &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model with polymorphic parent association (Assignment/SignUpTopic)&lt;br /&gt;
* &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; subclasses&lt;br /&gt;
* Basic CRUD operations (within &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AssignmentForm&amp;lt;/code&amp;gt;) and sorting functionality (&amp;lt;code&amp;gt;deadline_sort&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Database schema with &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt; field&lt;br /&gt;
&lt;br /&gt;
=== Current Behavior ===&lt;br /&gt;
&lt;br /&gt;
====Admin's View====&lt;br /&gt;
[[File:Duedates.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* When editing an assignment, due dates can be modified under the Due Dates tab.&lt;br /&gt;
* Each row contains the following columns: &amp;lt;code&amp;gt;Date &amp;amp; Time&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Use Date Updater&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Submission Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Teammate Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Meta-Review Allowed&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Reminder&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:Assignments-CurrentStage.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* In the Assignments table, the ''DueDate'' of each assignment is displayed as Current Stage and Stage Deadline.&lt;br /&gt;
&lt;br /&gt;
====Student's View====&lt;br /&gt;
[[File:Student-assignments.png|600px]]&lt;br /&gt;
* Students can view the due date information under &amp;lt;code&amp;gt;Current State&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Stage Deadline&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
* The actions available to students are determined by the current DueDate status.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-not-over.png|600px]]&lt;br /&gt;
* For example, during the submission period, students can access the &amp;lt;code&amp;gt;Submit a hyperlink&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Submit a file&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-over.png|600px]]&lt;br /&gt;
* Once the due date has passed, students cannot submit either a hyperlink or a file.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Currently, there are some glaring issues with how due_dates was created in the first place, including redundancy and lack of modularity.&lt;br /&gt;
&lt;br /&gt;
Modeling &amp;amp; Structural Issues&lt;br /&gt;
# No dedicated &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to define different kinds of deadlines. The existing &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; class only serves as an association for &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; models, and is never referenced in the current &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; implementation.&lt;br /&gt;
# Duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table, which may lead to potential bugs.&lt;br /&gt;
# Incomplete deadline type definitions - &amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt; need to be added.&lt;br /&gt;
# No clear separation of concerns - data persistence, business logic, and permission logic are mixed in a single model.&lt;br /&gt;
&lt;br /&gt;
Permission &amp;amp; Behavior Issues&lt;br /&gt;
# Overuse of class methods making the code difficult to maintain.&lt;br /&gt;
# Missing permission checking logic for user actions.&lt;br /&gt;
&lt;br /&gt;
=== Tasks Identified ===&lt;br /&gt;
&lt;br /&gt;
Modeling &amp;amp; Structural Issues&lt;br /&gt;
# Refactor the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model to separate persistence, business logic, and permission logic into distinct layers.&lt;br /&gt;
# Implement reusable mixins (e.g., &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;) to handle permission evaluation and deadline-related operations.&lt;br /&gt;
# Introduce instance-level methods (e.g., &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;) to replace class-level utilities and improve testability.&lt;br /&gt;
&lt;br /&gt;
Permission &amp;amp; Behavior Issues&lt;br /&gt;
# Integrate role-based and time-based permission checks within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; to ensure consistent access control.&lt;br /&gt;
# Redesign the &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.&lt;br /&gt;
# Add missing deadline types (&amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt;) and remove duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries for data integrity.&lt;br /&gt;
&lt;br /&gt;
== Proposed Solution ==&lt;br /&gt;
The proposed solution restructures the due date system by separating concerns across dedicated models and mixin modules.&lt;br /&gt;
Deadline definitions (&amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt;), core deadline data (&amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;), and behavioral logic (&amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;) are isolated so each component has a single clear responsibility.&lt;br /&gt;
This modular design improves readability, reduces coupling, and makes deadline-related behavior easier to extend and maintain.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Duedate-diagram.png|600px]]&lt;br /&gt;
&lt;br /&gt;
This diagram visualizes the redesigned architecture: blue/red/green indicate Rails models, orange indicates modules. &lt;br /&gt;
Parent models call DueDateActions, which manages DueDate, while DueDatePermissions checks user rights using reference models. &lt;br /&gt;
&lt;br /&gt;
=== DeadlineType Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Although the current codebase includes a &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model, it only serves as a passive lookup table for associations with &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt;.  &lt;br /&gt;
In practice, most parts of the system still rely on hard-coded numeric IDs or manual lookups such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Example of current usage&lt;br /&gt;
deadline_type_id = DeadlineType.find_by(name: 'review').id&lt;br /&gt;
if due_date.deadline_type_id == deadline_type_id&lt;br /&gt;
  # Perform review-related logic&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of using the model as an active domain object, &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; directly compares IDs or string literals.  &lt;br /&gt;
This leads to several structural issues:&lt;br /&gt;
&lt;br /&gt;
* Inconsistent references (some use IDs, others strings)&lt;br /&gt;
* Tight coupling between &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
* Lack of helper semantics (e.g., &amp;lt;code&amp;gt;is_review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;is_submission?&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Data duplication and integrity risks (two &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; rows)&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; will serve as a source of truth, defining all valid deadline types and providing meaningful interfaces.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table defines all canonical deadline categories used by the system.&lt;br /&gt;
Each row represents one distinct deadline type, and every &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; record must reference one of these entries through &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Below is a structural description of the table:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; Table Structure                                                    &lt;br /&gt;
|- &lt;br /&gt;
! Column                                                                                         &lt;br /&gt;
! Type                                                                                           &lt;br /&gt;
! Description                                                                                    &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| id                                                                                               &lt;br /&gt;
| BIGINT (Primary Key)                                                                             &lt;br /&gt;
| Unique identifier for each deadline type; referenced by &amp;lt;code&amp;gt;due_dates.deadline_type_id&amp;lt;/code&amp;gt;. &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| name                                                                                             &lt;br /&gt;
| VARCHAR(255)                                                                                     &lt;br /&gt;
| Symbolic name (e.g., &amp;lt;code&amp;gt;submission&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review&amp;lt;/code&amp;gt;); must be unique.              &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| description                                                                                      &lt;br /&gt;
| TEXT                                                                                             &lt;br /&gt;
| Human-readable explanation of the deadline category.                                             &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| created_at                                                                                       &lt;br /&gt;
| TIMESTAMP                                                                                        &lt;br /&gt;
| Time when the row was created.                                                                   &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| updated_at                                                                                       &lt;br /&gt;
| TIMESTAMP                                                                                        &lt;br /&gt;
| Time when the row was last updated.                                                              &lt;br /&gt;
|}                                                                                                &lt;br /&gt;
&lt;br /&gt;
This schema ensures that:&lt;br /&gt;
&lt;br /&gt;
* Each deadline type has a unique name and description.&lt;br /&gt;
* No duplicate entries can exist (e.g., the prior duplicated &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; was cleaned).&lt;br /&gt;
* All &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; rows reference this master table through &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
* The &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; column acts as the authoritative key used throughout permissions and due-date logic.&lt;br /&gt;
&lt;br /&gt;
Migrated constraints also ensure that &amp;lt;code&amp;gt;due_dates.deadline_type_id&amp;lt;/code&amp;gt; is consistently stored as &amp;lt;code&amp;gt;BIGINT&amp;lt;/code&amp;gt;, allowing a long-term stable keyspace.&lt;br /&gt;
&lt;br /&gt;
==== Deadline Type Definitions ====&lt;br /&gt;
&lt;br /&gt;
Because each row in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; is referenced by &amp;lt;code&amp;gt;DueDate.deadline_type_id&amp;lt;/code&amp;gt;, the following definitions map directly to the schema above.&lt;br /&gt;
&lt;br /&gt;
Each ID corresponds exactly to the &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; field in the table and serves as the system-wide canonical reference for that deadline category.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Canonical Deadline Type Entries                                       &lt;br /&gt;
|-&lt;br /&gt;
! ID                                                                    &lt;br /&gt;
! Name                                                                  &lt;br /&gt;
! Description                                                           &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 1                                                                       &lt;br /&gt;
| submission                                                              &lt;br /&gt;
| Student submission deadlines                                            &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 2                                                                       &lt;br /&gt;
| review                                                                  &lt;br /&gt;
| Peer review deadlines                                                   &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 3                                                                       &lt;br /&gt;
| teammate_review                                                         &lt;br /&gt;
| Team member evaluation deadlines                                        &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 5                                                                       &lt;br /&gt;
| metareview                                                              &lt;br /&gt;
| Meta-review deadlines (kept for backward compatibility)                 &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 6                                                                       &lt;br /&gt;
| drop_topic                                                              &lt;br /&gt;
| Topic drop deadlines                                                    &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 7                                                                       &lt;br /&gt;
| signup                                                                  &lt;br /&gt;
| Topic signup deadlines                                                  &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 8                                                                       &lt;br /&gt;
| team_formation                                                          &lt;br /&gt;
| Team formation deadlines (duplicate entries cleaned; ID 8 is canonical) &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 11                                                                      &lt;br /&gt;
| quiz                                                                    &lt;br /&gt;
| Quiz completion deadlines                                               &lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
These definitions are seeded during migration and act as the source of truth for all deadline-related logic within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;.&lt;br /&gt;
By linking each &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; to one of these entries through &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;, the system standardizes behavior and eliminates inconsistent ID or string comparisons from earlier implementations.&lt;br /&gt;
&lt;br /&gt;
==== DeadlineType Model Implementation ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineType &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
  validates :description, presence: true&lt;br /&gt;
&lt;br /&gt;
  has_many :due_dates, foreign_key: :deadline_type_id, dependent: :restrict_with_exception&lt;br /&gt;
&lt;br /&gt;
  # Semantic helper methods for deadline type identification&lt;br /&gt;
  def submission?&lt;br /&gt;
    name == 'submission'&lt;br /&gt;
  end&lt;br /&gt;
  # same code for review, teammate_review, quiz, team_formation, signup, drop_topic   &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DeadlineRight Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model represents the permission level for a specific action at a given deadline.&lt;br /&gt;
It defines three states: &lt;br /&gt;
* &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt; : Action is allowed without penalty&lt;br /&gt;
* &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt; : Action is allowed but marked as late&lt;br /&gt;
* &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt; : Action is not permitted&lt;br /&gt;
&lt;br /&gt;
These values are referenced by &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; through fields such as &amp;lt;code&amp;gt;submission_allowed_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review_allowed_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz_allowed_id&amp;lt;/code&amp;gt;, and others.&lt;br /&gt;
This allows the system to determine user permissions by combining:&lt;br /&gt;
&lt;br /&gt;
# The current deadline’s allowed state&lt;br /&gt;
# The participant’s role-based ability (submit, review, take quiz)&lt;br /&gt;
&lt;br /&gt;
This combination logic is implemented in &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
The &amp;lt;code&amp;gt;deadline_rights&amp;lt;/code&amp;gt; table stores the canonical permission states used across the entire due-date system.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_rights&amp;lt;/code&amp;gt; Table Structure                                                         &lt;br /&gt;
|-&lt;br /&gt;
! Column                                                                                               &lt;br /&gt;
! Type                                                                                                 &lt;br /&gt;
! Description                                                                                          &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| id                                                                                                     &lt;br /&gt;
| BIGINT (Primary Key)                                                                                  &lt;br /&gt;
| Unique identifier for each permission state. Referenced by &amp;lt;code&amp;gt;due_dates.*_allowed_id&amp;lt;/code&amp;gt; fields. &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| name                                                                                                   &lt;br /&gt;
| VARCHAR(255)                                                                                           &lt;br /&gt;
| Symbolic permission name (&amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;). Must be unique.        &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| description                                                                                            &lt;br /&gt;
| TEXT                                                                                                   &lt;br /&gt;
| Human-readable explanation of what the permission state means.                                         &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| created_at                                                                                             &lt;br /&gt;
| TIMESTAMP                                                                                              &lt;br /&gt;
| Timestamp when the entry was created.                                                                  &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| updated_at                                                                                             &lt;br /&gt;
| TIMESTAMP                                                                                              &lt;br /&gt;
| Timestamp when the entry was last updated.                                                             &lt;br /&gt;
|}  &lt;br /&gt;
&lt;br /&gt;
==== DeadlineRight Model ====&lt;br /&gt;
Instead of embedding permission logic inside &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;, the &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model defines the meaning of each permission state.&lt;br /&gt;
This removes magic strings (&amp;lt;code&amp;gt;&amp;quot;OK&amp;quot;&amp;lt;/code&amp;gt;) and numeric comparisons and replaces them with expressive model-level semantics.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Semantic Meaning Provided by &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt;                    &lt;br /&gt;
|-&lt;br /&gt;
! Concept                                                                    &lt;br /&gt;
! Meaning                                                                    &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| allows_action?                                                               &lt;br /&gt;
| True for &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;; false for &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;.    &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| denies_action?                                                               &lt;br /&gt;
| True only for &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;.                                               &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| allows_with_penalty?                                                         &lt;br /&gt;
| True only for &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;.                                             &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| allows_without_penalty?                                                      &lt;br /&gt;
| True only for &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.                                               &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| permission_level                                                             &lt;br /&gt;
| Numeric ordering: No = 0, Late = 1, OK = 2. Used for comparison and sorting. &lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
These behaviors allow other parts of the system—particularly &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;—to make consistent, readable decisions.&lt;br /&gt;
&lt;br /&gt;
=== DueDate Model Refactoring ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model currently mixes persistence logic, deadline calculation, and permission checking, violating SRP.  &lt;br /&gt;
Most of its logic is implemented as class methods, which makes testing and extension difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Current design&lt;br /&gt;
def self.copy(old_assignment_id, new_assignment_id)&lt;br /&gt;
  duedates = where(parent_id: old_assignment_id)&lt;br /&gt;
  duedates.each do |orig_due_date|&lt;br /&gt;
    new_due_date = orig_due_date.dup&lt;br /&gt;
    new_due_date.parent_id = new_assignment_id&lt;br /&gt;
    new_due_date.save&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Improved Design ====&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model is simpler, more modular, and focused on representing a single deadline entry.&lt;br /&gt;
Behavior previously embedded inside &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; has been extracted into appropriate modules:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Responsibility Distribution After Refactoring                        &lt;br /&gt;
|-&lt;br /&gt;
! Concern                                                              &lt;br /&gt;
! Where It Lives Now                                                   &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Permission checking                                                    &lt;br /&gt;
| &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module                                 &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Deadline-related actions (next deadline, stage name, deadline copying) &lt;br /&gt;
| &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module (included in parent models)         &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Deadline type semantics                                                &lt;br /&gt;
| &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model                                        &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Core deadline data (due_at, round, parent)                             &lt;br /&gt;
| &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model                                             &lt;br /&gt;
|}   &lt;br /&gt;
&lt;br /&gt;
===== Instance-Based Behavior Instead of Class Methods =====&lt;br /&gt;
&lt;br /&gt;
Previously, many behaviors were class methods (e.g., copying deadlines).&lt;br /&gt;
These methods did not reflect object-level behavior and often duplicated logic.&lt;br /&gt;
&lt;br /&gt;
The redesign moves these operations to:&lt;br /&gt;
&lt;br /&gt;
* instance methods for actions on a specific &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;&lt;br /&gt;
* modules (DueDateActions) for higher-level behaviors on assignments or topics&lt;br /&gt;
&lt;br /&gt;
This separation:&lt;br /&gt;
&lt;br /&gt;
* improves testability&lt;br /&gt;
* aligns with Rails conventions&lt;br /&gt;
* avoids state duplication&lt;br /&gt;
&lt;br /&gt;
===== Clearer Query Methods =====&lt;br /&gt;
&lt;br /&gt;
The model now exposes simple, intuitive instance-level questions:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Examples of Simplified State Queries    &lt;br /&gt;
|-&lt;br /&gt;
! Method                                  &lt;br /&gt;
! Meaning                                 &lt;br /&gt;
|-                                         &lt;br /&gt;
| &amp;lt;code&amp;gt;upcoming?&amp;lt;/code&amp;gt;                    &lt;br /&gt;
| Whether the deadline occurs in the future &lt;br /&gt;
|-                                         &lt;br /&gt;
| &amp;lt;code&amp;gt;overdue?&amp;lt;/code&amp;gt;                     &lt;br /&gt;
| Whether the deadline has already passed   &lt;br /&gt;
|-                                         &lt;br /&gt;
| &amp;lt;code&amp;gt;deadline_type_name&amp;lt;/code&amp;gt;           &lt;br /&gt;
| Human-readable deadline type              &lt;br /&gt;
|}                                         &lt;br /&gt;
&lt;br /&gt;
These small, composable predicates replace multiple ad-hoc comparisons scattered across the codebase.&lt;br /&gt;
&lt;br /&gt;
=== DueDatePermissions Mixin ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Currently, permission checks depend only on user roles and ignore the actual deadline.  &lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Role-based permissions (participant_helpers.rb)&lt;br /&gt;
def participant_permissions(authorization)&lt;br /&gt;
  case authorization&lt;br /&gt;
  when 'reader'   then { can_submit: false, can_review: true, can_take_quiz: true }&lt;br /&gt;
  when 'reviewer' then { can_submit: false, can_review: true, can_take_quiz: false }&lt;br /&gt;
  when 'submitter' then { can_submit: true, can_review: false, can_take_quiz: false }&lt;br /&gt;
  else { can_submit: true, can_review: true, can_take_quiz: true }&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And deadline-based checks exist separately in the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def submission_allowed(topic_id = nil)&lt;br /&gt;
  check_condition('submission_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
def can_review(topic_id = nil)&lt;br /&gt;
  check_condition('review_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These two layers (role-based and time-based) never interact, leading to inconsistencies (e.g., a user with &amp;lt;code&amp;gt;can_submit=true&amp;lt;/code&amp;gt; after deadline).&lt;br /&gt;
&lt;br /&gt;
==== Proposed Changes ====&lt;br /&gt;
&lt;br /&gt;
The new &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module integrates role-based and deadline-based checks.&lt;br /&gt;
Instead of embedding checks directly into &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;, all permission logic now flows through &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Meaning of Permission Methods                                             &lt;br /&gt;
! Method                                                                                  &lt;br /&gt;
! What It Determines                                                                                        &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| can_submit?                                                                                                 &lt;br /&gt;
| Submission is allowed only if (1) the submission deadline is OK/Late and (2) the participant’s role permits submitting. &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| can_review?                                                                                                 &lt;br /&gt;
| Review is allowed only if the review deadline is OK/Late and the participant has review privileges.         &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| can_take_quiz?                                                                                              &lt;br /&gt;
| Quiz access is controlled by both quiz deadlines and participant quiz permissions.                          &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| can_review_teammate?                                                                                        &lt;br /&gt;
| Teammate review uses both the teammate-review deadline and participant review rights.                       &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| activity_permissible?                                                                                       &lt;br /&gt;
| Unified checker for any action, mapped through a dynamically computed &amp;lt;code&amp;gt;*_allowed_id&amp;lt;/code&amp;gt;.            &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| permission_status_for(action)                                                                               &lt;br /&gt;
| Returns &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt; for display and UI logic.                    &lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
These methods handle all the interactions between role state, deadline state, and the user’s participant record.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Integration with the DueDate Model ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model includes the &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module so that each deadline object inherently knows:&lt;br /&gt;
&lt;br /&gt;
* whether an action is allowed at that time&lt;br /&gt;
* whether lateness is permitted&lt;br /&gt;
* what the current permission status is&lt;br /&gt;
* how to evaluate participant-specific permissions&lt;br /&gt;
&lt;br /&gt;
=== DueDateActions Mixin ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module provides deadline-related operations for models that have due dates (Assignment, SignUpTopic).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDateActions&lt;br /&gt;
  # Generic activity permission checker that determines if an activity is permissible&lt;br /&gt;
  # based on the current deadline state for this parent object&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    current_deadline = next_due_date()&lt;br /&gt;
    return false unless current_deadline&lt;br /&gt;
&lt;br /&gt;
    current_deadline.activity_permissible?(activity)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Syntactic sugar methods for common activities&lt;br /&gt;
  def submission_permissible?&lt;br /&gt;
    activity_permissible?(:submission)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def review_permissible?&lt;br /&gt;
    activity_permissible?(:review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def teammate_review_permissible?&lt;br /&gt;
    activity_permissible?(:teammate_review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def quiz_permissible?&lt;br /&gt;
    activity_permissible?(:quiz)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def team_formation_permissible?&lt;br /&gt;
    activity_permissible?(:team_formation)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def signup_permissible?&lt;br /&gt;
    activity_permissible?(:signup)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def drop_topic_permissible?&lt;br /&gt;
    activity_permissible?(:drop_topic)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the next due date for this assignment&lt;br /&gt;
  def next_due_date(topic_id = nil)&lt;br /&gt;
    # If a topic is specified and this assignment has topic-specific deadlines,&lt;br /&gt;
    # look for topic due dates first&lt;br /&gt;
    if topic_id &amp;amp;&amp;amp; has_topic_specific_deadlines?&lt;br /&gt;
      topic_deadline = due_dates.where(parent_id: topic_id, parent_type: 'SignUpTopic')&lt;br /&gt;
                               .upcoming&lt;br /&gt;
                               .first&lt;br /&gt;
      return topic_deadline if topic_deadline&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Fall back to assignment-level due dates&lt;br /&gt;
    due_dates.upcoming.first&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the current stage name for display purposes&lt;br /&gt;
  def current_stage&lt;br /&gt;
    deadline = next_due_date()&lt;br /&gt;
    return 'finished' unless deadline&lt;br /&gt;
&lt;br /&gt;
    deadline.deadline_type&amp;amp;.name || 'unknown'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if assignment has topic-specific deadlines&lt;br /&gt;
  def has_topic_specific_deadlines?&lt;br /&gt;
    staggered_deadline || due_dates.where(parent_type: 'SignUpTopic').exists?&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Copy due dates to a new parent object&lt;br /&gt;
  def copy_due_dates_to(new_parent)&lt;br /&gt;
    due_dates.find_each do |due_date|&lt;br /&gt;
      new_due_date = due_date.dup&lt;br /&gt;
      new_due_date.parent = new_parent&lt;br /&gt;
      new_due_date.save!&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This module is included in &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class Assignment &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
class SignUpTopic &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Implementation Summary ==&lt;br /&gt;
&lt;br /&gt;
=== Models Created/Modified ===&lt;br /&gt;
&lt;br /&gt;
* '''DeadlineType''' - Canonical deadline type definitions with semantic helper methods&lt;br /&gt;
* '''DeadlineRight''' - Permission state model (OK/Late/No) with comparison methods&lt;br /&gt;
* '''DueDate''' - Refactored with instance methods, scopes, and permission checking&lt;br /&gt;
* '''TopicDueDate''' - STI subclass for topic-specific deadlines&lt;br /&gt;
* '''Assignment''' - Includes DueDateActions mixin&lt;br /&gt;
* '''SignUpTopic''' - Includes DueDateActions mixin&lt;br /&gt;
&lt;br /&gt;
=== Modules Created ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions''' - Permission checking combining deadline and role constraints&lt;br /&gt;
* '''DueDateActions''' - Deadline-related operations for parent models (Assignment, SignUpTopic)&lt;br /&gt;
&lt;br /&gt;
=== Key Improvements ===&lt;br /&gt;
&lt;br /&gt;
# '''Separation of Concerns''': Permission logic separated into dedicated modules&lt;br /&gt;
# '''Instance Methods''': Replaced class methods with testable instance methods&lt;br /&gt;
# '''Polymorphic Design''': Single DueDate model serves both Assignment and SignUpTopic&lt;br /&gt;
# '''Unified Permissions''': Role-based and time-based checks combined in one interface&lt;br /&gt;
# '''Data Integrity''': Removed duplicate deadline types, enforced foreign key constraints&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
The test suite includes comprehensive RSpec tests for the ''DueDate'' model and its associated models (''DeadlineType'', ''DeadlineRight'').&lt;br /&gt;
These tests verify model validations, scopes, instance methods, class methods, and callbacks with extensive coverage of edge cases and error conditions.&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
The test suite follows RSpec best practices with:&lt;br /&gt;
* '''Nested contexts''' for different scenarios (e.g., &amp;quot;when parent is missing&amp;quot;, &amp;quot;when round is 0&amp;quot;)&lt;br /&gt;
* '''Multiple assertions per context''' to verify behavior from different angles&lt;br /&gt;
* '''Edge case coverage''' including empty collections, nil values, and boundary conditions&lt;br /&gt;
* '''Error case testing''' for invalid data and non-existent records&lt;br /&gt;
* '''Clear descriptive names''' using &amp;quot;context 'when...'&amp;quot; and &amp;quot;it 'does something'&amp;quot; patterns&lt;br /&gt;
&lt;br /&gt;
=== Current Coverage ===&lt;br /&gt;
&lt;br /&gt;
==== Associations ====&lt;br /&gt;
* Tests verify &amp;lt;code&amp;gt;belongs_to :parent&amp;lt;/code&amp;gt; (polymorphic) and &amp;lt;code&amp;gt;belongs_to :deadline_type&amp;lt;/code&amp;gt; relationships&lt;br /&gt;
&lt;br /&gt;
==== Validations ====&lt;br /&gt;
* '''Required field validation''': Tests for parent, due_at, and deadline_type_id presence&lt;br /&gt;
* '''Round validation''': Tests for positive values, zero rejection, negative rejection, and nil handling&lt;br /&gt;
* '''Error messages''': Verifies specific error messages are added to appropriate fields&lt;br /&gt;
* '''Valid record creation''': Confirms records persist when all requirements are met&lt;br /&gt;
&lt;br /&gt;
==== Scopes ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;''': Returns future due dates ordered by due_at, excludes past dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;''': Returns past due dates ordered by due_at, excludes future dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;''': Filters by round number, handles non-existent rounds&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt;''': Filters by deadline type name, handles non-existent types&lt;br /&gt;
&lt;br /&gt;
==== Instance Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#overdue?&amp;lt;/code&amp;gt;''': Tests past dates (true), future dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#upcoming?&amp;lt;/code&amp;gt;''': Tests future dates (true), past dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#set&amp;lt;/code&amp;gt;''': Verifies updating deadline_type_id, parent_id, and round with persistence and error handling&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#copy&amp;lt;/code&amp;gt;''': Tests duplication to new assignment, attribute preservation, error handling for non-existent targets&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#deadline_type_name&amp;lt;/code&amp;gt;''': Returns associated deadline type name, handles nil cases&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#last_deadline?&amp;lt;/code&amp;gt;''': Tests detection of final deadline, handles multiple deadlines and single deadline scenarios&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#&amp;lt;=&amp;gt;&amp;lt;/code&amp;gt;''': Tests comparison by due_at time, handles non-DueDate objects&lt;br /&gt;
&lt;br /&gt;
==== Class Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.sort_due_dates&amp;lt;/code&amp;gt;''': Sorts by due_at ascending, handles empty arrays and single elements&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.any_future_due_dates?&amp;lt;/code&amp;gt;''': Detects future dates in collections, handles empty arrays and mixed date collections&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.copy&amp;lt;/code&amp;gt;''': Copies all due dates between assignments, preserves attributes, handles empty sources and non-existent records&lt;br /&gt;
&lt;br /&gt;
==== Callbacks ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;before_save :set_default_round&amp;lt;/code&amp;gt;''': Sets round to 1 when not specified, preserves explicit values, handles nil&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
Tests follow RSpec best practices with nested contexts for different scenarios:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
describe 'validations' do&lt;br /&gt;
  context 'when parent is missing' do&lt;br /&gt;
    it 'is invalid' do&lt;br /&gt;
      expect(due_date).to be_invalid&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'has error on parent field' do&lt;br /&gt;
      due_date.valid?&lt;br /&gt;
      expect(due_date.errors[:parent]).to include('must exist')&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when round validation' do&lt;br /&gt;
    context 'with round value of 0' do&lt;br /&gt;
      it 'is invalid' do&lt;br /&gt;
        expect(due_date).to be_invalid&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'with nil round' do&lt;br /&gt;
      it 'sets default round to 1' do&lt;br /&gt;
        expect(due_date.round).to eq(1)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
=== Demo Link ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add deadline notification system using the new permission framework&lt;br /&gt;
* Implement deadline templates for common assignment patterns&lt;br /&gt;
* Add API endpoints for mobile app integration&lt;br /&gt;
* Create admin dashboard for monitoring deadline compliance across courses&lt;br /&gt;
* Add automated deadline extension workflows based on configurable rules&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167200</id>
		<title>CSC/ECE 517 Fall 2025 - E2566. Finish DueDates</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167200"/>
		<updated>2025-12-02T04:53:23Z</updated>

		<summary type="html">&lt;p&gt;Skim253: /* Permission-Checking Methods */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
This project aims to complete the implementation of the DueDate system in Expertiza. The current implementation consists mostly of class methods and lacks proper definition of different deadline types and permission checking functionality. This redesign will create a more robust, readable, and maintainable due date system.&lt;br /&gt;
&lt;br /&gt;
=== Existing Components ===&lt;br /&gt;
* &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model with polymorphic parent association (Assignment/SignUpTopic)&lt;br /&gt;
* &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; subclasses&lt;br /&gt;
* Basic CRUD operations (within &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AssignmentForm&amp;lt;/code&amp;gt;) and sorting functionality (&amp;lt;code&amp;gt;deadline_sort&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Database schema with &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt; field&lt;br /&gt;
&lt;br /&gt;
=== Current Behavior ===&lt;br /&gt;
&lt;br /&gt;
====Admin's View====&lt;br /&gt;
[[File:Duedates.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* When editing an assignment, due dates can be modified under the Due Dates tab.&lt;br /&gt;
* Each row contains the following columns: &amp;lt;code&amp;gt;Date &amp;amp; Time&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Use Date Updater&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Submission Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Teammate Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Meta-Review Allowed&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Reminder&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:Assignments-CurrentStage.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* In the Assignments table, the ''DueDate'' of each assignment is displayed as Current Stage and Stage Deadline.&lt;br /&gt;
&lt;br /&gt;
====Student's View====&lt;br /&gt;
[[File:Student-assignments.png|600px]]&lt;br /&gt;
* Students can view the due date information under &amp;lt;code&amp;gt;Current State&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Stage Deadline&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
* The actions available to students are determined by the current DueDate status.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-not-over.png|600px]]&lt;br /&gt;
* For example, during the submission period, students can access the &amp;lt;code&amp;gt;Submit a hyperlink&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Submit a file&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-over.png|600px]]&lt;br /&gt;
* Once the due date has passed, students cannot submit either a hyperlink or a file.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Currently, there are some glaring issues with how due_dates was created in the first place, including redundancy and lack of modularity.&lt;br /&gt;
&lt;br /&gt;
Modeling &amp;amp; Structural Issues&lt;br /&gt;
# No dedicated &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to define different kinds of deadlines. The existing &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; class only serves as an association for &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; models, and is never referenced in the current &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; implementation.&lt;br /&gt;
# Duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table, which may lead to potential bugs.&lt;br /&gt;
# Incomplete deadline type definitions - &amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt; need to be added.&lt;br /&gt;
# No clear separation of concerns - data persistence, business logic, and permission logic are mixed in a single model.&lt;br /&gt;
&lt;br /&gt;
Permission &amp;amp; Behavior Issues&lt;br /&gt;
# Overuse of class methods making the code difficult to maintain.&lt;br /&gt;
# Missing permission checking logic for user actions.&lt;br /&gt;
&lt;br /&gt;
=== Tasks Identified ===&lt;br /&gt;
&lt;br /&gt;
Modeling &amp;amp; Structural Issues&lt;br /&gt;
# Refactor the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model to separate persistence, business logic, and permission logic into distinct layers.&lt;br /&gt;
# Implement reusable mixins (e.g., &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;) to handle permission evaluation and deadline-related operations.&lt;br /&gt;
# Introduce instance-level methods (e.g., &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;) to replace class-level utilities and improve testability.&lt;br /&gt;
&lt;br /&gt;
Permission &amp;amp; Behavior Issues&lt;br /&gt;
# Integrate role-based and time-based permission checks within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; to ensure consistent access control.&lt;br /&gt;
# Redesign the &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.&lt;br /&gt;
# Add missing deadline types (&amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt;) and remove duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries for data integrity.&lt;br /&gt;
&lt;br /&gt;
== Proposed Solution ==&lt;br /&gt;
The proposed solution restructures the due date system by separating concerns across dedicated models and mixin modules.&lt;br /&gt;
Deadline definitions (&amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt;), core deadline data (&amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;), and behavioral logic (&amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;) are isolated so each component has a single clear responsibility.&lt;br /&gt;
This modular design improves readability, reduces coupling, and makes deadline-related behavior easier to extend and maintain.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Duedate-diagram.png|600px]]&lt;br /&gt;
&lt;br /&gt;
This diagram visualizes the redesigned architecture: blue/red/green indicate Rails models, orange indicates modules. &lt;br /&gt;
Parent models call DueDateActions, which manages DueDate, while DueDatePermissions checks user rights using reference models. &lt;br /&gt;
&lt;br /&gt;
=== DeadlineType Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Although the current codebase includes a &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model, it only serves as a passive lookup table for associations with &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt;.  &lt;br /&gt;
In practice, most parts of the system still rely on hard-coded numeric IDs or manual lookups such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Example of current usage&lt;br /&gt;
deadline_type_id = DeadlineType.find_by(name: 'review').id&lt;br /&gt;
if due_date.deadline_type_id == deadline_type_id&lt;br /&gt;
  # Perform review-related logic&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of using the model as an active domain object, &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; directly compares IDs or string literals.  &lt;br /&gt;
This leads to several structural issues:&lt;br /&gt;
&lt;br /&gt;
* Inconsistent references (some use IDs, others strings)&lt;br /&gt;
* Tight coupling between &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
* Lack of helper semantics (e.g., &amp;lt;code&amp;gt;is_review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;is_submission?&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Data duplication and integrity risks (two &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; rows)&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; will serve as a source of truth, defining all valid deadline types and providing meaningful interfaces.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table defines all canonical deadline categories used by the system.&lt;br /&gt;
Each row represents one distinct deadline type, and every &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; record must reference one of these entries through &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Below is a structural description of the table:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; Table Structure                                                    &lt;br /&gt;
|- &lt;br /&gt;
! Column                                                                                         &lt;br /&gt;
! Type                                                                                           &lt;br /&gt;
! Description                                                                                    &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| id                                                                                               &lt;br /&gt;
| BIGINT (Primary Key)                                                                             &lt;br /&gt;
| Unique identifier for each deadline type; referenced by &amp;lt;code&amp;gt;due_dates.deadline_type_id&amp;lt;/code&amp;gt;. &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| name                                                                                             &lt;br /&gt;
| VARCHAR(255)                                                                                     &lt;br /&gt;
| Symbolic name (e.g., &amp;lt;code&amp;gt;submission&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review&amp;lt;/code&amp;gt;); must be unique.              &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| description                                                                                      &lt;br /&gt;
| TEXT                                                                                             &lt;br /&gt;
| Human-readable explanation of the deadline category.                                             &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| created_at                                                                                       &lt;br /&gt;
| TIMESTAMP                                                                                        &lt;br /&gt;
| Time when the row was created.                                                                   &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| updated_at                                                                                       &lt;br /&gt;
| TIMESTAMP                                                                                        &lt;br /&gt;
| Time when the row was last updated.                                                              &lt;br /&gt;
|}                                                                                                &lt;br /&gt;
&lt;br /&gt;
This schema ensures that:&lt;br /&gt;
&lt;br /&gt;
* Each deadline type has a unique name and description.&lt;br /&gt;
* No duplicate entries can exist (e.g., the prior duplicated &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; was cleaned).&lt;br /&gt;
* All &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; rows reference this master table through &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
* The &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; column acts as the authoritative key used throughout permissions and due-date logic.&lt;br /&gt;
&lt;br /&gt;
Migrated constraints also ensure that &amp;lt;code&amp;gt;due_dates.deadline_type_id&amp;lt;/code&amp;gt; is consistently stored as &amp;lt;code&amp;gt;BIGINT&amp;lt;/code&amp;gt;, allowing a long-term stable keyspace.&lt;br /&gt;
&lt;br /&gt;
==== Deadline Type Definitions ====&lt;br /&gt;
&lt;br /&gt;
Because each row in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; is referenced by &amp;lt;code&amp;gt;DueDate.deadline_type_id&amp;lt;/code&amp;gt;, the following definitions map directly to the schema above.&lt;br /&gt;
&lt;br /&gt;
Each ID corresponds exactly to the &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; field in the table and serves as the system-wide canonical reference for that deadline category.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Canonical Deadline Type Entries                                       &lt;br /&gt;
|-&lt;br /&gt;
! ID                                                                    &lt;br /&gt;
! Name                                                                  &lt;br /&gt;
! Description                                                           &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 1                                                                       &lt;br /&gt;
| submission                                                              &lt;br /&gt;
| Student submission deadlines                                            &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 2                                                                       &lt;br /&gt;
| review                                                                  &lt;br /&gt;
| Peer review deadlines                                                   &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 3                                                                       &lt;br /&gt;
| teammate_review                                                         &lt;br /&gt;
| Team member evaluation deadlines                                        &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 5                                                                       &lt;br /&gt;
| metareview                                                              &lt;br /&gt;
| Meta-review deadlines (kept for backward compatibility)                 &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 6                                                                       &lt;br /&gt;
| drop_topic                                                              &lt;br /&gt;
| Topic drop deadlines                                                    &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 7                                                                       &lt;br /&gt;
| signup                                                                  &lt;br /&gt;
| Topic signup deadlines                                                  &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 8                                                                       &lt;br /&gt;
| team_formation                                                          &lt;br /&gt;
| Team formation deadlines (duplicate entries cleaned; ID 8 is canonical) &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 11                                                                      &lt;br /&gt;
| quiz                                                                    &lt;br /&gt;
| Quiz completion deadlines                                               &lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
These definitions are seeded during migration and act as the source of truth for all deadline-related logic within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;.&lt;br /&gt;
By linking each &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; to one of these entries through &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;, the system standardizes behavior and eliminates inconsistent ID or string comparisons from earlier implementations.&lt;br /&gt;
&lt;br /&gt;
==== DeadlineType Model Implementation ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineType &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
  validates :description, presence: true&lt;br /&gt;
&lt;br /&gt;
  has_many :due_dates, foreign_key: :deadline_type_id, dependent: :restrict_with_exception&lt;br /&gt;
&lt;br /&gt;
  # Semantic helper methods for deadline type identification&lt;br /&gt;
  def submission?&lt;br /&gt;
    name == 'submission'&lt;br /&gt;
  end&lt;br /&gt;
  # same code for review, teammate_review, quiz, team_formation, signup, drop_topic   &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DeadlineRight Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model represents the permission level for a specific action at a given deadline.&lt;br /&gt;
It defines three states: &lt;br /&gt;
* &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt; : Action is allowed without penalty&lt;br /&gt;
* &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt; : Action is allowed but marked as late&lt;br /&gt;
* &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt; : Action is not permitted&lt;br /&gt;
&lt;br /&gt;
These values are referenced by &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; through fields such as &amp;lt;code&amp;gt;submission_allowed_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review_allowed_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz_allowed_id&amp;lt;/code&amp;gt;, and others.&lt;br /&gt;
This allows the system to determine user permissions by combining:&lt;br /&gt;
&lt;br /&gt;
# The current deadline’s allowed state&lt;br /&gt;
# The participant’s role-based ability (submit, review, take quiz)&lt;br /&gt;
&lt;br /&gt;
This combination logic is implemented in &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
The &amp;lt;code&amp;gt;deadline_rights&amp;lt;/code&amp;gt; table stores the canonical permission states used across the entire due-date system.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_rights&amp;lt;/code&amp;gt; Table Structure                                                         &lt;br /&gt;
|-&lt;br /&gt;
! Column                                                                                               &lt;br /&gt;
! Type                                                                                                 &lt;br /&gt;
! Description                                                                                          &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| id                                                                                                     &lt;br /&gt;
| BIGINT (Primary Key)                                                                                  &lt;br /&gt;
| Unique identifier for each permission state. Referenced by &amp;lt;code&amp;gt;due_dates.*_allowed_id&amp;lt;/code&amp;gt; fields. &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| name                                                                                                   &lt;br /&gt;
| VARCHAR(255)                                                                                           &lt;br /&gt;
| Symbolic permission name (&amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;). Must be unique.        &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| description                                                                                            &lt;br /&gt;
| TEXT                                                                                                   &lt;br /&gt;
| Human-readable explanation of what the permission state means.                                         &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| created_at                                                                                             &lt;br /&gt;
| TIMESTAMP                                                                                              &lt;br /&gt;
| Timestamp when the entry was created.                                                                  &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| updated_at                                                                                             &lt;br /&gt;
| TIMESTAMP                                                                                              &lt;br /&gt;
| Timestamp when the entry was last updated.                                                             &lt;br /&gt;
|}  &lt;br /&gt;
&lt;br /&gt;
==== DeadlineRight Model ====&lt;br /&gt;
Instead of embedding permission logic inside &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;, the &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model defines the meaning of each permission state.&lt;br /&gt;
This removes magic strings (&amp;lt;code&amp;gt;&amp;quot;OK&amp;quot;&amp;lt;/code&amp;gt;) and numeric comparisons and replaces them with expressive model-level semantics.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Semantic Meaning Provided by &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt;                    &lt;br /&gt;
|-&lt;br /&gt;
! Concept                                                                    &lt;br /&gt;
! Meaning                                                                    &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| allows_action?                                                               &lt;br /&gt;
| True for &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;; false for &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;.    &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| denies_action?                                                               &lt;br /&gt;
| True only for &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;.                                               &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| allows_with_penalty?                                                         &lt;br /&gt;
| True only for &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;.                                             &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| allows_without_penalty?                                                      &lt;br /&gt;
| True only for &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.                                               &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| permission_level                                                             &lt;br /&gt;
| Numeric ordering: No = 0, Late = 1, OK = 2. Used for comparison and sorting. &lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
These behaviors allow other parts of the system—particularly &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;—to make consistent, readable decisions.&lt;br /&gt;
&lt;br /&gt;
=== DueDate Model Refactoring ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model currently mixes persistence logic, deadline calculation, and permission checking, violating SRP.  &lt;br /&gt;
Most of its logic is implemented as class methods, which makes testing and extension difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Current design&lt;br /&gt;
def self.copy(old_assignment_id, new_assignment_id)&lt;br /&gt;
  duedates = where(parent_id: old_assignment_id)&lt;br /&gt;
  duedates.each do |orig_due_date|&lt;br /&gt;
    new_due_date = orig_due_date.dup&lt;br /&gt;
    new_due_date.parent_id = new_assignment_id&lt;br /&gt;
    new_due_date.save&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Improved Design ====&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model is simpler, more modular, and focused on representing a single deadline entry.&lt;br /&gt;
Behavior previously embedded inside &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; has been extracted into appropriate modules:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Responsibility Distribution After Refactoring                        &lt;br /&gt;
|-&lt;br /&gt;
! Concern                                                              &lt;br /&gt;
! Where It Lives Now                                                   &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Permission checking                                                    &lt;br /&gt;
| &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module                                 &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Deadline-related actions (next deadline, stage name, deadline copying) &lt;br /&gt;
| &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module (included in parent models)         &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Deadline type semantics                                                &lt;br /&gt;
| &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model                                        &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Core deadline data (due_at, round, parent)                             &lt;br /&gt;
| &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model                                             &lt;br /&gt;
|}   &lt;br /&gt;
&lt;br /&gt;
===== Instance-Based Behavior Instead of Class Methods =====&lt;br /&gt;
&lt;br /&gt;
Previously, many behaviors were class methods (e.g., copying deadlines).&lt;br /&gt;
These methods did not reflect object-level behavior and often duplicated logic.&lt;br /&gt;
&lt;br /&gt;
The redesign moves these operations to:&lt;br /&gt;
&lt;br /&gt;
* instance methods for actions on a specific &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;&lt;br /&gt;
* modules (DueDateActions) for higher-level behaviors on assignments or topics&lt;br /&gt;
&lt;br /&gt;
This separation:&lt;br /&gt;
&lt;br /&gt;
* improves testability&lt;br /&gt;
* aligns with Rails conventions&lt;br /&gt;
* avoids state duplication&lt;br /&gt;
&lt;br /&gt;
===== Clearer Query Methods =====&lt;br /&gt;
&lt;br /&gt;
The model now exposes simple, intuitive instance-level questions:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Examples of Simplified State Queries    &lt;br /&gt;
|-&lt;br /&gt;
! Method                                  &lt;br /&gt;
! Meaning                                 &lt;br /&gt;
|-                                         &lt;br /&gt;
| &amp;lt;code&amp;gt;upcoming?&amp;lt;/code&amp;gt;                    &lt;br /&gt;
| Whether the deadline occurs in the future &lt;br /&gt;
|-                                         &lt;br /&gt;
| &amp;lt;code&amp;gt;overdue?&amp;lt;/code&amp;gt;                     &lt;br /&gt;
| Whether the deadline has already passed   &lt;br /&gt;
|-                                         &lt;br /&gt;
| &amp;lt;code&amp;gt;deadline_type_name&amp;lt;/code&amp;gt;           &lt;br /&gt;
| Human-readable deadline type              &lt;br /&gt;
|}                                         &lt;br /&gt;
&lt;br /&gt;
These small, composable predicates replace multiple ad-hoc comparisons scattered across the codebase.&lt;br /&gt;
&lt;br /&gt;
=== DueDatePermission Mixin ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Currently, permission checks depend only on user roles and ignore the actual deadline.  &lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Role-based permissions (participant_helpers.rb)&lt;br /&gt;
def participant_permissions(authorization)&lt;br /&gt;
  case authorization&lt;br /&gt;
  when 'reader'   then { can_submit: false, can_review: true, can_take_quiz: true }&lt;br /&gt;
  when 'reviewer' then { can_submit: false, can_review: true, can_take_quiz: false }&lt;br /&gt;
  when 'submitter' then { can_submit: true, can_review: false, can_take_quiz: false }&lt;br /&gt;
  else { can_submit: true, can_review: true, can_take_quiz: true }&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And deadline-based checks exist separately in the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def submission_allowed(topic_id = nil)&lt;br /&gt;
  check_condition('submission_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
def can_review(topic_id = nil)&lt;br /&gt;
  check_condition('review_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These two layers (role-based and time-based) never interact, leading to inconsistencies (e.g., a user with &amp;lt;code&amp;gt;can_submit=true&amp;lt;/code&amp;gt; after deadline).&lt;br /&gt;
&lt;br /&gt;
==== Proposed Changes ====&lt;br /&gt;
&lt;br /&gt;
The new &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module integrates role-based and deadline-based checks.&lt;br /&gt;
Instead of embedding checks directly into &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;, all permission logic now flows through &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Meaning of Permission Methods                                             &lt;br /&gt;
! Method                                                                                  &lt;br /&gt;
! What It Determines                                                                                        &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| can_submit?                                                                                                 &lt;br /&gt;
| Submission is allowed only if (1) the submission deadline is OK/Late and (2) the participant’s role permits submitting. &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| can_review?                                                                                                 &lt;br /&gt;
| Review is allowed only if the review deadline is OK/Late and the participant has review privileges.         &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| can_take_quiz?                                                                                              &lt;br /&gt;
| Quiz access is controlled by both quiz deadlines and participant quiz permissions.                          &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| can_review_teammate?                                                                                        &lt;br /&gt;
| Teammate review uses both the teammate-review deadline and participant review rights.                       &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| activity_permissible?                                                                                       &lt;br /&gt;
| Unified checker for any action, mapped through a dynamically computed &amp;lt;code&amp;gt;*_allowed_id&amp;lt;/code&amp;gt;.            &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| permission_status_for(action)                                                                               &lt;br /&gt;
| Returns &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt; for display and UI logic.                    &lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
These methods handle all the interactions between role state, deadline state, and the user’s participant record.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Integration with the DueDate Model ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model includes the &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module so that each deadline object inherently knows:&lt;br /&gt;
&lt;br /&gt;
* whether an action is allowed at that time&lt;br /&gt;
* whether lateness is permitted&lt;br /&gt;
* what the current permission status is&lt;br /&gt;
* how to evaluate participant-specific permissions&lt;br /&gt;
&lt;br /&gt;
=== DueDateActions Mixin ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module provides deadline-related operations for models that have due dates (Assignment, SignUpTopic).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDateActions&lt;br /&gt;
  # Generic activity permission checker that determines if an activity is permissible&lt;br /&gt;
  # based on the current deadline state for this parent object&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    current_deadline = next_due_date()&lt;br /&gt;
    return false unless current_deadline&lt;br /&gt;
&lt;br /&gt;
    current_deadline.activity_permissible?(activity)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Syntactic sugar methods for common activities&lt;br /&gt;
  def submission_permissible?&lt;br /&gt;
    activity_permissible?(:submission)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def review_permissible?&lt;br /&gt;
    activity_permissible?(:review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def teammate_review_permissible?&lt;br /&gt;
    activity_permissible?(:teammate_review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def quiz_permissible?&lt;br /&gt;
    activity_permissible?(:quiz)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def team_formation_permissible?&lt;br /&gt;
    activity_permissible?(:team_formation)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def signup_permissible?&lt;br /&gt;
    activity_permissible?(:signup)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def drop_topic_permissible?&lt;br /&gt;
    activity_permissible?(:drop_topic)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the next due date for this assignment&lt;br /&gt;
  def next_due_date(topic_id = nil)&lt;br /&gt;
    # If a topic is specified and this assignment has topic-specific deadlines,&lt;br /&gt;
    # look for topic due dates first&lt;br /&gt;
    if topic_id &amp;amp;&amp;amp; has_topic_specific_deadlines?&lt;br /&gt;
      topic_deadline = due_dates.where(parent_id: topic_id, parent_type: 'SignUpTopic')&lt;br /&gt;
                               .upcoming&lt;br /&gt;
                               .first&lt;br /&gt;
      return topic_deadline if topic_deadline&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Fall back to assignment-level due dates&lt;br /&gt;
    due_dates.upcoming.first&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the current stage name for display purposes&lt;br /&gt;
  def current_stage&lt;br /&gt;
    deadline = next_due_date()&lt;br /&gt;
    return 'finished' unless deadline&lt;br /&gt;
&lt;br /&gt;
    deadline.deadline_type&amp;amp;.name || 'unknown'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if assignment has topic-specific deadlines&lt;br /&gt;
  def has_topic_specific_deadlines?&lt;br /&gt;
    staggered_deadline || due_dates.where(parent_type: 'SignUpTopic').exists?&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Copy due dates to a new parent object&lt;br /&gt;
  def copy_due_dates_to(new_parent)&lt;br /&gt;
    due_dates.find_each do |due_date|&lt;br /&gt;
      new_due_date = due_date.dup&lt;br /&gt;
      new_due_date.parent = new_parent&lt;br /&gt;
      new_due_date.save!&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This module is included in &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class Assignment &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
class SignUpTopic &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Implementation Summary ==&lt;br /&gt;
&lt;br /&gt;
=== Models Created/Modified ===&lt;br /&gt;
&lt;br /&gt;
* '''DeadlineType''' - Canonical deadline type definitions with semantic helper methods&lt;br /&gt;
* '''DeadlineRight''' - Permission state model (OK/Late/No) with comparison methods&lt;br /&gt;
* '''DueDate''' - Refactored with instance methods, scopes, and permission checking&lt;br /&gt;
* '''TopicDueDate''' - STI subclass for topic-specific deadlines&lt;br /&gt;
* '''Assignment''' - Includes DueDateActions mixin&lt;br /&gt;
* '''SignUpTopic''' - Includes DueDateActions mixin&lt;br /&gt;
&lt;br /&gt;
=== Modules Created ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions''' - Permission checking combining deadline and role constraints&lt;br /&gt;
* '''DueDateActions''' - Deadline-related operations for parent models (Assignment, SignUpTopic)&lt;br /&gt;
&lt;br /&gt;
=== Key Improvements ===&lt;br /&gt;
&lt;br /&gt;
# '''Separation of Concerns''': Permission logic separated into dedicated modules&lt;br /&gt;
# '''Instance Methods''': Replaced class methods with testable instance methods&lt;br /&gt;
# '''Polymorphic Design''': Single DueDate model serves both Assignment and SignUpTopic&lt;br /&gt;
# '''Unified Permissions''': Role-based and time-based checks combined in one interface&lt;br /&gt;
# '''Data Integrity''': Removed duplicate deadline types, enforced foreign key constraints&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
The test suite includes comprehensive RSpec tests for the ''DueDate'' model and its associated models (''DeadlineType'', ''DeadlineRight'').&lt;br /&gt;
These tests verify model validations, scopes, instance methods, class methods, and callbacks with extensive coverage of edge cases and error conditions.&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
The test suite follows RSpec best practices with:&lt;br /&gt;
* '''Nested contexts''' for different scenarios (e.g., &amp;quot;when parent is missing&amp;quot;, &amp;quot;when round is 0&amp;quot;)&lt;br /&gt;
* '''Multiple assertions per context''' to verify behavior from different angles&lt;br /&gt;
* '''Edge case coverage''' including empty collections, nil values, and boundary conditions&lt;br /&gt;
* '''Error case testing''' for invalid data and non-existent records&lt;br /&gt;
* '''Clear descriptive names''' using &amp;quot;context 'when...'&amp;quot; and &amp;quot;it 'does something'&amp;quot; patterns&lt;br /&gt;
&lt;br /&gt;
=== Current Coverage ===&lt;br /&gt;
&lt;br /&gt;
==== Associations ====&lt;br /&gt;
* Tests verify &amp;lt;code&amp;gt;belongs_to :parent&amp;lt;/code&amp;gt; (polymorphic) and &amp;lt;code&amp;gt;belongs_to :deadline_type&amp;lt;/code&amp;gt; relationships&lt;br /&gt;
&lt;br /&gt;
==== Validations ====&lt;br /&gt;
* '''Required field validation''': Tests for parent, due_at, and deadline_type_id presence&lt;br /&gt;
* '''Round validation''': Tests for positive values, zero rejection, negative rejection, and nil handling&lt;br /&gt;
* '''Error messages''': Verifies specific error messages are added to appropriate fields&lt;br /&gt;
* '''Valid record creation''': Confirms records persist when all requirements are met&lt;br /&gt;
&lt;br /&gt;
==== Scopes ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;''': Returns future due dates ordered by due_at, excludes past dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;''': Returns past due dates ordered by due_at, excludes future dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;''': Filters by round number, handles non-existent rounds&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt;''': Filters by deadline type name, handles non-existent types&lt;br /&gt;
&lt;br /&gt;
==== Instance Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#overdue?&amp;lt;/code&amp;gt;''': Tests past dates (true), future dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#upcoming?&amp;lt;/code&amp;gt;''': Tests future dates (true), past dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#set&amp;lt;/code&amp;gt;''': Verifies updating deadline_type_id, parent_id, and round with persistence and error handling&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#copy&amp;lt;/code&amp;gt;''': Tests duplication to new assignment, attribute preservation, error handling for non-existent targets&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#deadline_type_name&amp;lt;/code&amp;gt;''': Returns associated deadline type name, handles nil cases&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#last_deadline?&amp;lt;/code&amp;gt;''': Tests detection of final deadline, handles multiple deadlines and single deadline scenarios&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#&amp;lt;=&amp;gt;&amp;lt;/code&amp;gt;''': Tests comparison by due_at time, handles non-DueDate objects&lt;br /&gt;
&lt;br /&gt;
==== Class Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.sort_due_dates&amp;lt;/code&amp;gt;''': Sorts by due_at ascending, handles empty arrays and single elements&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.any_future_due_dates?&amp;lt;/code&amp;gt;''': Detects future dates in collections, handles empty arrays and mixed date collections&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.copy&amp;lt;/code&amp;gt;''': Copies all due dates between assignments, preserves attributes, handles empty sources and non-existent records&lt;br /&gt;
&lt;br /&gt;
==== Callbacks ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;before_save :set_default_round&amp;lt;/code&amp;gt;''': Sets round to 1 when not specified, preserves explicit values, handles nil&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
Tests follow RSpec best practices with nested contexts for different scenarios:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
describe 'validations' do&lt;br /&gt;
  context 'when parent is missing' do&lt;br /&gt;
    it 'is invalid' do&lt;br /&gt;
      expect(due_date).to be_invalid&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'has error on parent field' do&lt;br /&gt;
      due_date.valid?&lt;br /&gt;
      expect(due_date.errors[:parent]).to include('must exist')&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when round validation' do&lt;br /&gt;
    context 'with round value of 0' do&lt;br /&gt;
      it 'is invalid' do&lt;br /&gt;
        expect(due_date).to be_invalid&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'with nil round' do&lt;br /&gt;
      it 'sets default round to 1' do&lt;br /&gt;
        expect(due_date.round).to eq(1)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
=== Demo Link ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add deadline notification system using the new permission framework&lt;br /&gt;
* Implement deadline templates for common assignment patterns&lt;br /&gt;
* Add API endpoints for mobile app integration&lt;br /&gt;
* Create admin dashboard for monitoring deadline compliance across courses&lt;br /&gt;
* Add automated deadline extension workflows based on configurable rules&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167199</id>
		<title>CSC/ECE 517 Fall 2025 - E2566. Finish DueDates</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167199"/>
		<updated>2025-12-02T04:50:36Z</updated>

		<summary type="html">&lt;p&gt;Skim253: /* Permission-Checking Methods */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
This project aims to complete the implementation of the DueDate system in Expertiza. The current implementation consists mostly of class methods and lacks proper definition of different deadline types and permission checking functionality. This redesign will create a more robust, readable, and maintainable due date system.&lt;br /&gt;
&lt;br /&gt;
=== Existing Components ===&lt;br /&gt;
* &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model with polymorphic parent association (Assignment/SignUpTopic)&lt;br /&gt;
* &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; subclasses&lt;br /&gt;
* Basic CRUD operations (within &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AssignmentForm&amp;lt;/code&amp;gt;) and sorting functionality (&amp;lt;code&amp;gt;deadline_sort&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Database schema with &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt; field&lt;br /&gt;
&lt;br /&gt;
=== Current Behavior ===&lt;br /&gt;
&lt;br /&gt;
====Admin's View====&lt;br /&gt;
[[File:Duedates.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* When editing an assignment, due dates can be modified under the Due Dates tab.&lt;br /&gt;
* Each row contains the following columns: &amp;lt;code&amp;gt;Date &amp;amp; Time&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Use Date Updater&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Submission Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Teammate Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Meta-Review Allowed&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Reminder&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:Assignments-CurrentStage.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* In the Assignments table, the ''DueDate'' of each assignment is displayed as Current Stage and Stage Deadline.&lt;br /&gt;
&lt;br /&gt;
====Student's View====&lt;br /&gt;
[[File:Student-assignments.png|600px]]&lt;br /&gt;
* Students can view the due date information under &amp;lt;code&amp;gt;Current State&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Stage Deadline&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
* The actions available to students are determined by the current DueDate status.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-not-over.png|600px]]&lt;br /&gt;
* For example, during the submission period, students can access the &amp;lt;code&amp;gt;Submit a hyperlink&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Submit a file&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-over.png|600px]]&lt;br /&gt;
* Once the due date has passed, students cannot submit either a hyperlink or a file.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Currently, there are some glaring issues with how due_dates was created in the first place, including redundancy and lack of modularity.&lt;br /&gt;
&lt;br /&gt;
Modeling &amp;amp; Structural Issues&lt;br /&gt;
# No dedicated &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to define different kinds of deadlines. The existing &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; class only serves as an association for &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; models, and is never referenced in the current &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; implementation.&lt;br /&gt;
# Duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table, which may lead to potential bugs.&lt;br /&gt;
# Incomplete deadline type definitions - &amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt; need to be added.&lt;br /&gt;
# No clear separation of concerns - data persistence, business logic, and permission logic are mixed in a single model.&lt;br /&gt;
&lt;br /&gt;
Permission &amp;amp; Behavior Issues&lt;br /&gt;
# Overuse of class methods making the code difficult to maintain.&lt;br /&gt;
# Missing permission checking logic for user actions.&lt;br /&gt;
&lt;br /&gt;
=== Tasks Identified ===&lt;br /&gt;
&lt;br /&gt;
Modeling &amp;amp; Structural Issues&lt;br /&gt;
# Refactor the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model to separate persistence, business logic, and permission logic into distinct layers.&lt;br /&gt;
# Implement reusable mixins (e.g., &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;) to handle permission evaluation and deadline-related operations.&lt;br /&gt;
# Introduce instance-level methods (e.g., &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;) to replace class-level utilities and improve testability.&lt;br /&gt;
&lt;br /&gt;
Permission &amp;amp; Behavior Issues&lt;br /&gt;
# Integrate role-based and time-based permission checks within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; to ensure consistent access control.&lt;br /&gt;
# Redesign the &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.&lt;br /&gt;
# Add missing deadline types (&amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt;) and remove duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries for data integrity.&lt;br /&gt;
&lt;br /&gt;
== Proposed Solution ==&lt;br /&gt;
The proposed solution restructures the due date system by separating concerns across dedicated models and mixin modules.&lt;br /&gt;
Deadline definitions (&amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt;), core deadline data (&amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;), and behavioral logic (&amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;) are isolated so each component has a single clear responsibility.&lt;br /&gt;
This modular design improves readability, reduces coupling, and makes deadline-related behavior easier to extend and maintain.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Duedate-diagram.png|600px]]&lt;br /&gt;
&lt;br /&gt;
This diagram visualizes the redesigned architecture: blue/red/green indicate Rails models, orange indicates modules. &lt;br /&gt;
Parent models call DueDateActions, which manages DueDate, while DueDatePermissions checks user rights using reference models. &lt;br /&gt;
&lt;br /&gt;
=== DeadlineType Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Although the current codebase includes a &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model, it only serves as a passive lookup table for associations with &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt;.  &lt;br /&gt;
In practice, most parts of the system still rely on hard-coded numeric IDs or manual lookups such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Example of current usage&lt;br /&gt;
deadline_type_id = DeadlineType.find_by(name: 'review').id&lt;br /&gt;
if due_date.deadline_type_id == deadline_type_id&lt;br /&gt;
  # Perform review-related logic&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of using the model as an active domain object, &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; directly compares IDs or string literals.  &lt;br /&gt;
This leads to several structural issues:&lt;br /&gt;
&lt;br /&gt;
* Inconsistent references (some use IDs, others strings)&lt;br /&gt;
* Tight coupling between &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
* Lack of helper semantics (e.g., &amp;lt;code&amp;gt;is_review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;is_submission?&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Data duplication and integrity risks (two &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; rows)&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; will serve as a source of truth, defining all valid deadline types and providing meaningful interfaces.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table defines all canonical deadline categories used by the system.&lt;br /&gt;
Each row represents one distinct deadline type, and every &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; record must reference one of these entries through &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Below is a structural description of the table:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; Table Structure                                                    &lt;br /&gt;
|- &lt;br /&gt;
! Column                                                                                         &lt;br /&gt;
! Type                                                                                           &lt;br /&gt;
! Description                                                                                    &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| id                                                                                               &lt;br /&gt;
| BIGINT (Primary Key)                                                                             &lt;br /&gt;
| Unique identifier for each deadline type; referenced by &amp;lt;code&amp;gt;due_dates.deadline_type_id&amp;lt;/code&amp;gt;. &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| name                                                                                             &lt;br /&gt;
| VARCHAR(255)                                                                                     &lt;br /&gt;
| Symbolic name (e.g., &amp;lt;code&amp;gt;submission&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review&amp;lt;/code&amp;gt;); must be unique.              &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| description                                                                                      &lt;br /&gt;
| TEXT                                                                                             &lt;br /&gt;
| Human-readable explanation of the deadline category.                                             &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| created_at                                                                                       &lt;br /&gt;
| TIMESTAMP                                                                                        &lt;br /&gt;
| Time when the row was created.                                                                   &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| updated_at                                                                                       &lt;br /&gt;
| TIMESTAMP                                                                                        &lt;br /&gt;
| Time when the row was last updated.                                                              &lt;br /&gt;
|}                                                                                                &lt;br /&gt;
&lt;br /&gt;
This schema ensures that:&lt;br /&gt;
&lt;br /&gt;
* Each deadline type has a unique name and description.&lt;br /&gt;
* No duplicate entries can exist (e.g., the prior duplicated &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; was cleaned).&lt;br /&gt;
* All &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; rows reference this master table through &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
* The &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; column acts as the authoritative key used throughout permissions and due-date logic.&lt;br /&gt;
&lt;br /&gt;
Migrated constraints also ensure that &amp;lt;code&amp;gt;due_dates.deadline_type_id&amp;lt;/code&amp;gt; is consistently stored as &amp;lt;code&amp;gt;BIGINT&amp;lt;/code&amp;gt;, allowing a long-term stable keyspace.&lt;br /&gt;
&lt;br /&gt;
==== Deadline Type Definitions ====&lt;br /&gt;
&lt;br /&gt;
Because each row in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; is referenced by &amp;lt;code&amp;gt;DueDate.deadline_type_id&amp;lt;/code&amp;gt;, the following definitions map directly to the schema above.&lt;br /&gt;
&lt;br /&gt;
Each ID corresponds exactly to the &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; field in the table and serves as the system-wide canonical reference for that deadline category.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Canonical Deadline Type Entries                                       &lt;br /&gt;
|-&lt;br /&gt;
! ID                                                                    &lt;br /&gt;
! Name                                                                  &lt;br /&gt;
! Description                                                           &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 1                                                                       &lt;br /&gt;
| submission                                                              &lt;br /&gt;
| Student submission deadlines                                            &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 2                                                                       &lt;br /&gt;
| review                                                                  &lt;br /&gt;
| Peer review deadlines                                                   &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 3                                                                       &lt;br /&gt;
| teammate_review                                                         &lt;br /&gt;
| Team member evaluation deadlines                                        &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 5                                                                       &lt;br /&gt;
| metareview                                                              &lt;br /&gt;
| Meta-review deadlines (kept for backward compatibility)                 &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 6                                                                       &lt;br /&gt;
| drop_topic                                                              &lt;br /&gt;
| Topic drop deadlines                                                    &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 7                                                                       &lt;br /&gt;
| signup                                                                  &lt;br /&gt;
| Topic signup deadlines                                                  &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 8                                                                       &lt;br /&gt;
| team_formation                                                          &lt;br /&gt;
| Team formation deadlines (duplicate entries cleaned; ID 8 is canonical) &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 11                                                                      &lt;br /&gt;
| quiz                                                                    &lt;br /&gt;
| Quiz completion deadlines                                               &lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
These definitions are seeded during migration and act as the source of truth for all deadline-related logic within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;.&lt;br /&gt;
By linking each &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; to one of these entries through &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;, the system standardizes behavior and eliminates inconsistent ID or string comparisons from earlier implementations.&lt;br /&gt;
&lt;br /&gt;
==== DeadlineType Model Implementation ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineType &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
  validates :description, presence: true&lt;br /&gt;
&lt;br /&gt;
  has_many :due_dates, foreign_key: :deadline_type_id, dependent: :restrict_with_exception&lt;br /&gt;
&lt;br /&gt;
  # Semantic helper methods for deadline type identification&lt;br /&gt;
  def submission?&lt;br /&gt;
    name == 'submission'&lt;br /&gt;
  end&lt;br /&gt;
  # same code for review, teammate_review, quiz, team_formation, signup, drop_topic   &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DeadlineRight Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model represents the permission level for a specific action at a given deadline.&lt;br /&gt;
It defines three states: &lt;br /&gt;
* &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt; : Action is allowed without penalty&lt;br /&gt;
* &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt; : Action is allowed but marked as late&lt;br /&gt;
* &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt; : Action is not permitted&lt;br /&gt;
&lt;br /&gt;
These values are referenced by &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; through fields such as &amp;lt;code&amp;gt;submission_allowed_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review_allowed_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz_allowed_id&amp;lt;/code&amp;gt;, and others.&lt;br /&gt;
This allows the system to determine user permissions by combining:&lt;br /&gt;
&lt;br /&gt;
# The current deadline’s allowed state&lt;br /&gt;
# The participant’s role-based ability (submit, review, take quiz)&lt;br /&gt;
&lt;br /&gt;
This combination logic is implemented in &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
The &amp;lt;code&amp;gt;deadline_rights&amp;lt;/code&amp;gt; table stores the canonical permission states used across the entire due-date system.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_rights&amp;lt;/code&amp;gt; Table Structure                                                         &lt;br /&gt;
|-&lt;br /&gt;
! Column                                                                                               &lt;br /&gt;
! Type                                                                                                 &lt;br /&gt;
! Description                                                                                          &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| id                                                                                                     &lt;br /&gt;
| BIGINT (Primary Key)                                                                                  &lt;br /&gt;
| Unique identifier for each permission state. Referenced by &amp;lt;code&amp;gt;due_dates.*_allowed_id&amp;lt;/code&amp;gt; fields. &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| name                                                                                                   &lt;br /&gt;
| VARCHAR(255)                                                                                           &lt;br /&gt;
| Symbolic permission name (&amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;). Must be unique.        &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| description                                                                                            &lt;br /&gt;
| TEXT                                                                                                   &lt;br /&gt;
| Human-readable explanation of what the permission state means.                                         &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| created_at                                                                                             &lt;br /&gt;
| TIMESTAMP                                                                                              &lt;br /&gt;
| Timestamp when the entry was created.                                                                  &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| updated_at                                                                                             &lt;br /&gt;
| TIMESTAMP                                                                                              &lt;br /&gt;
| Timestamp when the entry was last updated.                                                             &lt;br /&gt;
|}  &lt;br /&gt;
&lt;br /&gt;
==== DeadlineRight Model ====&lt;br /&gt;
Instead of embedding permission logic inside &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;, the &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model defines the meaning of each permission state.&lt;br /&gt;
This removes magic strings (&amp;lt;code&amp;gt;&amp;quot;OK&amp;quot;&amp;lt;/code&amp;gt;) and numeric comparisons and replaces them with expressive model-level semantics.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Semantic Meaning Provided by &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt;                    &lt;br /&gt;
|-&lt;br /&gt;
! Concept                                                                    &lt;br /&gt;
! Meaning                                                                    &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| allows_action?                                                               &lt;br /&gt;
| True for &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;; false for &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;.    &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| denies_action?                                                               &lt;br /&gt;
| True only for &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;.                                               &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| allows_with_penalty?                                                         &lt;br /&gt;
| True only for &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;.                                             &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| allows_without_penalty?                                                      &lt;br /&gt;
| True only for &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.                                               &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| permission_level                                                             &lt;br /&gt;
| Numeric ordering: No = 0, Late = 1, OK = 2. Used for comparison and sorting. &lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
These behaviors allow other parts of the system—particularly &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;—to make consistent, readable decisions.&lt;br /&gt;
&lt;br /&gt;
=== DueDate Model Refactoring ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model currently mixes persistence logic, deadline calculation, and permission checking, violating SRP.  &lt;br /&gt;
Most of its logic is implemented as class methods, which makes testing and extension difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Current design&lt;br /&gt;
def self.copy(old_assignment_id, new_assignment_id)&lt;br /&gt;
  duedates = where(parent_id: old_assignment_id)&lt;br /&gt;
  duedates.each do |orig_due_date|&lt;br /&gt;
    new_due_date = orig_due_date.dup&lt;br /&gt;
    new_due_date.parent_id = new_assignment_id&lt;br /&gt;
    new_due_date.save&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Improved Design ====&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model is simpler, more modular, and focused on representing a single deadline entry.&lt;br /&gt;
Behavior previously embedded inside &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; has been extracted into appropriate modules:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Responsibility Distribution After Refactoring                        &lt;br /&gt;
|-&lt;br /&gt;
! Concern                                                              &lt;br /&gt;
! Where It Lives Now                                                   &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Permission checking                                                    &lt;br /&gt;
| &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module                                 &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Deadline-related actions (next deadline, stage name, deadline copying) &lt;br /&gt;
| &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module (included in parent models)         &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Deadline type semantics                                                &lt;br /&gt;
| &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model                                        &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Core deadline data (due_at, round, parent)                             &lt;br /&gt;
| &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model                                             &lt;br /&gt;
|}   &lt;br /&gt;
&lt;br /&gt;
===== Instance-Based Behavior Instead of Class Methods =====&lt;br /&gt;
&lt;br /&gt;
Previously, many behaviors were class methods (e.g., copying deadlines).&lt;br /&gt;
These methods did not reflect object-level behavior and often duplicated logic.&lt;br /&gt;
&lt;br /&gt;
The redesign moves these operations to:&lt;br /&gt;
&lt;br /&gt;
* instance methods for actions on a specific &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;&lt;br /&gt;
* modules (DueDateActions) for higher-level behaviors on assignments or topics&lt;br /&gt;
&lt;br /&gt;
This separation:&lt;br /&gt;
&lt;br /&gt;
* improves testability&lt;br /&gt;
* aligns with Rails conventions&lt;br /&gt;
* avoids state duplication&lt;br /&gt;
&lt;br /&gt;
===== Clearer Query Methods =====&lt;br /&gt;
&lt;br /&gt;
The model now exposes simple, intuitive instance-level questions:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Examples of Simplified State Queries    &lt;br /&gt;
|-&lt;br /&gt;
! Method                                  &lt;br /&gt;
! Meaning                                 &lt;br /&gt;
|-                                         &lt;br /&gt;
| &amp;lt;code&amp;gt;upcoming?&amp;lt;/code&amp;gt;                    &lt;br /&gt;
| Whether the deadline occurs in the future &lt;br /&gt;
|-                                         &lt;br /&gt;
| &amp;lt;code&amp;gt;overdue?&amp;lt;/code&amp;gt;                     &lt;br /&gt;
| Whether the deadline has already passed   &lt;br /&gt;
|-                                         &lt;br /&gt;
| &amp;lt;code&amp;gt;deadline_type_name&amp;lt;/code&amp;gt;           &lt;br /&gt;
| Human-readable deadline type              &lt;br /&gt;
|}                                         &lt;br /&gt;
&lt;br /&gt;
These small, composable predicates replace multiple ad-hoc comparisons scattered across the codebase.&lt;br /&gt;
&lt;br /&gt;
=== Permission-Checking Methods ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Currently, permission checks depend only on user roles and ignore the actual deadline.  &lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Role-based permissions (participant_helpers.rb)&lt;br /&gt;
def participant_permissions(authorization)&lt;br /&gt;
  case authorization&lt;br /&gt;
  when 'reader'   then { can_submit: false, can_review: true, can_take_quiz: true }&lt;br /&gt;
  when 'reviewer' then { can_submit: false, can_review: true, can_take_quiz: false }&lt;br /&gt;
  when 'submitter' then { can_submit: true, can_review: false, can_take_quiz: false }&lt;br /&gt;
  else { can_submit: true, can_review: true, can_take_quiz: true }&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And deadline-based checks exist separately in the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def submission_allowed(topic_id = nil)&lt;br /&gt;
  check_condition('submission_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
def can_review(topic_id = nil)&lt;br /&gt;
  check_condition('review_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These two layers (role-based and time-based) never interact, leading to inconsistencies (e.g., a user with &amp;lt;code&amp;gt;can_submit=true&amp;lt;/code&amp;gt; after deadline).&lt;br /&gt;
&lt;br /&gt;
==== Proposed Changes ====&lt;br /&gt;
&lt;br /&gt;
The new &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module integrates role-based and deadline-based checks.&lt;br /&gt;
Instead of embedding checks directly into &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;, all permission logic now flows through &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Meaning of Permission Methods                                             &lt;br /&gt;
! Method                                                                                  &lt;br /&gt;
! What It Determines                                                                                        &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| can_submit?                                                                                                 &lt;br /&gt;
| Submission is allowed only if (1) the submission deadline is OK/Late and (2) the participant’s role permits submitting. &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| can_review?                                                                                                 &lt;br /&gt;
| Review is allowed only if the review deadline is OK/Late and the participant has review privileges.         &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| can_take_quiz?                                                                                              &lt;br /&gt;
| Quiz access is controlled by both quiz deadlines and participant quiz permissions.                          &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| can_review_teammate?                                                                                        &lt;br /&gt;
| Teammate review uses both the teammate-review deadline and participant review rights.                       &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| activity_permissible?                                                                                       &lt;br /&gt;
| Unified checker for any action, mapped through a dynamically computed &amp;lt;code&amp;gt;*_allowed_id&amp;lt;/code&amp;gt;.            &lt;br /&gt;
|-                                                                                                           &lt;br /&gt;
| permission_status_for(action)                                                                               &lt;br /&gt;
| Returns &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;, or &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt; for display and UI logic.                    &lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
These methods handle all the interactions between role state, deadline state, and the user’s participant record.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==== Integration with the DueDate Model ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model includes the &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module so that each deadline object inherently knows:&lt;br /&gt;
&lt;br /&gt;
* whether an action is allowed at that time&lt;br /&gt;
* whether lateness is permitted&lt;br /&gt;
* what the current permission status is&lt;br /&gt;
* how to evaluate participant-specific permissions&lt;br /&gt;
&lt;br /&gt;
=== DueDateActions Mixin ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module provides deadline-related operations for models that have due dates (Assignment, SignUpTopic).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDateActions&lt;br /&gt;
  # Generic activity permission checker that determines if an activity is permissible&lt;br /&gt;
  # based on the current deadline state for this parent object&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    current_deadline = next_due_date()&lt;br /&gt;
    return false unless current_deadline&lt;br /&gt;
&lt;br /&gt;
    current_deadline.activity_permissible?(activity)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Syntactic sugar methods for common activities&lt;br /&gt;
  def submission_permissible?&lt;br /&gt;
    activity_permissible?(:submission)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def review_permissible?&lt;br /&gt;
    activity_permissible?(:review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def teammate_review_permissible?&lt;br /&gt;
    activity_permissible?(:teammate_review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def quiz_permissible?&lt;br /&gt;
    activity_permissible?(:quiz)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def team_formation_permissible?&lt;br /&gt;
    activity_permissible?(:team_formation)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def signup_permissible?&lt;br /&gt;
    activity_permissible?(:signup)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def drop_topic_permissible?&lt;br /&gt;
    activity_permissible?(:drop_topic)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the next due date for this assignment&lt;br /&gt;
  def next_due_date(topic_id = nil)&lt;br /&gt;
    # If a topic is specified and this assignment has topic-specific deadlines,&lt;br /&gt;
    # look for topic due dates first&lt;br /&gt;
    if topic_id &amp;amp;&amp;amp; has_topic_specific_deadlines?&lt;br /&gt;
      topic_deadline = due_dates.where(parent_id: topic_id, parent_type: 'SignUpTopic')&lt;br /&gt;
                               .upcoming&lt;br /&gt;
                               .first&lt;br /&gt;
      return topic_deadline if topic_deadline&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Fall back to assignment-level due dates&lt;br /&gt;
    due_dates.upcoming.first&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the current stage name for display purposes&lt;br /&gt;
  def current_stage&lt;br /&gt;
    deadline = next_due_date()&lt;br /&gt;
    return 'finished' unless deadline&lt;br /&gt;
&lt;br /&gt;
    deadline.deadline_type&amp;amp;.name || 'unknown'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if assignment has topic-specific deadlines&lt;br /&gt;
  def has_topic_specific_deadlines?&lt;br /&gt;
    staggered_deadline || due_dates.where(parent_type: 'SignUpTopic').exists?&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Copy due dates to a new parent object&lt;br /&gt;
  def copy_due_dates_to(new_parent)&lt;br /&gt;
    due_dates.find_each do |due_date|&lt;br /&gt;
      new_due_date = due_date.dup&lt;br /&gt;
      new_due_date.parent = new_parent&lt;br /&gt;
      new_due_date.save!&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This module is included in &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class Assignment &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
class SignUpTopic &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Implementation Summary ==&lt;br /&gt;
&lt;br /&gt;
=== Models Created/Modified ===&lt;br /&gt;
&lt;br /&gt;
* '''DeadlineType''' - Canonical deadline type definitions with semantic helper methods&lt;br /&gt;
* '''DeadlineRight''' - Permission state model (OK/Late/No) with comparison methods&lt;br /&gt;
* '''DueDate''' - Refactored with instance methods, scopes, and permission checking&lt;br /&gt;
* '''TopicDueDate''' - STI subclass for topic-specific deadlines&lt;br /&gt;
* '''Assignment''' - Includes DueDateActions mixin&lt;br /&gt;
* '''SignUpTopic''' - Includes DueDateActions mixin&lt;br /&gt;
&lt;br /&gt;
=== Modules Created ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions''' - Permission checking combining deadline and role constraints&lt;br /&gt;
* '''DueDateActions''' - Deadline-related operations for parent models (Assignment, SignUpTopic)&lt;br /&gt;
&lt;br /&gt;
=== Key Improvements ===&lt;br /&gt;
&lt;br /&gt;
# '''Separation of Concerns''': Permission logic separated into dedicated modules&lt;br /&gt;
# '''Instance Methods''': Replaced class methods with testable instance methods&lt;br /&gt;
# '''Polymorphic Design''': Single DueDate model serves both Assignment and SignUpTopic&lt;br /&gt;
# '''Unified Permissions''': Role-based and time-based checks combined in one interface&lt;br /&gt;
# '''Data Integrity''': Removed duplicate deadline types, enforced foreign key constraints&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
The test suite includes comprehensive RSpec tests for the ''DueDate'' model and its associated models (''DeadlineType'', ''DeadlineRight'').&lt;br /&gt;
These tests verify model validations, scopes, instance methods, class methods, and callbacks with extensive coverage of edge cases and error conditions.&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
The test suite follows RSpec best practices with:&lt;br /&gt;
* '''Nested contexts''' for different scenarios (e.g., &amp;quot;when parent is missing&amp;quot;, &amp;quot;when round is 0&amp;quot;)&lt;br /&gt;
* '''Multiple assertions per context''' to verify behavior from different angles&lt;br /&gt;
* '''Edge case coverage''' including empty collections, nil values, and boundary conditions&lt;br /&gt;
* '''Error case testing''' for invalid data and non-existent records&lt;br /&gt;
* '''Clear descriptive names''' using &amp;quot;context 'when...'&amp;quot; and &amp;quot;it 'does something'&amp;quot; patterns&lt;br /&gt;
&lt;br /&gt;
=== Current Coverage ===&lt;br /&gt;
&lt;br /&gt;
==== Associations ====&lt;br /&gt;
* Tests verify &amp;lt;code&amp;gt;belongs_to :parent&amp;lt;/code&amp;gt; (polymorphic) and &amp;lt;code&amp;gt;belongs_to :deadline_type&amp;lt;/code&amp;gt; relationships&lt;br /&gt;
&lt;br /&gt;
==== Validations ====&lt;br /&gt;
* '''Required field validation''': Tests for parent, due_at, and deadline_type_id presence&lt;br /&gt;
* '''Round validation''': Tests for positive values, zero rejection, negative rejection, and nil handling&lt;br /&gt;
* '''Error messages''': Verifies specific error messages are added to appropriate fields&lt;br /&gt;
* '''Valid record creation''': Confirms records persist when all requirements are met&lt;br /&gt;
&lt;br /&gt;
==== Scopes ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;''': Returns future due dates ordered by due_at, excludes past dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;''': Returns past due dates ordered by due_at, excludes future dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;''': Filters by round number, handles non-existent rounds&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt;''': Filters by deadline type name, handles non-existent types&lt;br /&gt;
&lt;br /&gt;
==== Instance Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#overdue?&amp;lt;/code&amp;gt;''': Tests past dates (true), future dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#upcoming?&amp;lt;/code&amp;gt;''': Tests future dates (true), past dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#set&amp;lt;/code&amp;gt;''': Verifies updating deadline_type_id, parent_id, and round with persistence and error handling&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#copy&amp;lt;/code&amp;gt;''': Tests duplication to new assignment, attribute preservation, error handling for non-existent targets&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#deadline_type_name&amp;lt;/code&amp;gt;''': Returns associated deadline type name, handles nil cases&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#last_deadline?&amp;lt;/code&amp;gt;''': Tests detection of final deadline, handles multiple deadlines and single deadline scenarios&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#&amp;lt;=&amp;gt;&amp;lt;/code&amp;gt;''': Tests comparison by due_at time, handles non-DueDate objects&lt;br /&gt;
&lt;br /&gt;
==== Class Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.sort_due_dates&amp;lt;/code&amp;gt;''': Sorts by due_at ascending, handles empty arrays and single elements&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.any_future_due_dates?&amp;lt;/code&amp;gt;''': Detects future dates in collections, handles empty arrays and mixed date collections&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.copy&amp;lt;/code&amp;gt;''': Copies all due dates between assignments, preserves attributes, handles empty sources and non-existent records&lt;br /&gt;
&lt;br /&gt;
==== Callbacks ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;before_save :set_default_round&amp;lt;/code&amp;gt;''': Sets round to 1 when not specified, preserves explicit values, handles nil&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
Tests follow RSpec best practices with nested contexts for different scenarios:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
describe 'validations' do&lt;br /&gt;
  context 'when parent is missing' do&lt;br /&gt;
    it 'is invalid' do&lt;br /&gt;
      expect(due_date).to be_invalid&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'has error on parent field' do&lt;br /&gt;
      due_date.valid?&lt;br /&gt;
      expect(due_date.errors[:parent]).to include('must exist')&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when round validation' do&lt;br /&gt;
    context 'with round value of 0' do&lt;br /&gt;
      it 'is invalid' do&lt;br /&gt;
        expect(due_date).to be_invalid&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'with nil round' do&lt;br /&gt;
      it 'sets default round to 1' do&lt;br /&gt;
        expect(due_date.round).to eq(1)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
=== Demo Link ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add deadline notification system using the new permission framework&lt;br /&gt;
* Implement deadline templates for common assignment patterns&lt;br /&gt;
* Add API endpoints for mobile app integration&lt;br /&gt;
* Create admin dashboard for monitoring deadline compliance across courses&lt;br /&gt;
* Add automated deadline extension workflows based on configurable rules&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167198</id>
		<title>CSC/ECE 517 Fall 2025 - E2566. Finish DueDates</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167198"/>
		<updated>2025-12-02T04:40:45Z</updated>

		<summary type="html">&lt;p&gt;Skim253: /* Testing */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
This project aims to complete the implementation of the DueDate system in Expertiza. The current implementation consists mostly of class methods and lacks proper definition of different deadline types and permission checking functionality. This redesign will create a more robust, readable, and maintainable due date system.&lt;br /&gt;
&lt;br /&gt;
=== Existing Components ===&lt;br /&gt;
* &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model with polymorphic parent association (Assignment/SignUpTopic)&lt;br /&gt;
* &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; subclasses&lt;br /&gt;
* Basic CRUD operations (within &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AssignmentForm&amp;lt;/code&amp;gt;) and sorting functionality (&amp;lt;code&amp;gt;deadline_sort&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Database schema with &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt; field&lt;br /&gt;
&lt;br /&gt;
=== Current Behavior ===&lt;br /&gt;
&lt;br /&gt;
====Admin's View====&lt;br /&gt;
[[File:Duedates.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* When editing an assignment, due dates can be modified under the Due Dates tab.&lt;br /&gt;
* Each row contains the following columns: &amp;lt;code&amp;gt;Date &amp;amp; Time&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Use Date Updater&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Submission Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Teammate Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Meta-Review Allowed&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Reminder&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:Assignments-CurrentStage.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* In the Assignments table, the ''DueDate'' of each assignment is displayed as Current Stage and Stage Deadline.&lt;br /&gt;
&lt;br /&gt;
====Student's View====&lt;br /&gt;
[[File:Student-assignments.png|600px]]&lt;br /&gt;
* Students can view the due date information under &amp;lt;code&amp;gt;Current State&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Stage Deadline&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
* The actions available to students are determined by the current DueDate status.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-not-over.png|600px]]&lt;br /&gt;
* For example, during the submission period, students can access the &amp;lt;code&amp;gt;Submit a hyperlink&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Submit a file&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-over.png|600px]]&lt;br /&gt;
* Once the due date has passed, students cannot submit either a hyperlink or a file.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Currently, there are some glaring issues with how due_dates was created in the first place, including redundancy and lack of modularity.&lt;br /&gt;
&lt;br /&gt;
Modeling &amp;amp; Structural Issues&lt;br /&gt;
# No dedicated &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to define different kinds of deadlines. The existing &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; class only serves as an association for &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; models, and is never referenced in the current &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; implementation.&lt;br /&gt;
# Duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table, which may lead to potential bugs.&lt;br /&gt;
# Incomplete deadline type definitions - &amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt; need to be added.&lt;br /&gt;
# No clear separation of concerns - data persistence, business logic, and permission logic are mixed in a single model.&lt;br /&gt;
&lt;br /&gt;
Permission &amp;amp; Behavior Issues&lt;br /&gt;
# Overuse of class methods making the code difficult to maintain.&lt;br /&gt;
# Missing permission checking logic for user actions.&lt;br /&gt;
&lt;br /&gt;
=== Tasks Identified ===&lt;br /&gt;
&lt;br /&gt;
Modeling &amp;amp; Structural Issues&lt;br /&gt;
# Refactor the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model to separate persistence, business logic, and permission logic into distinct layers.&lt;br /&gt;
# Implement reusable mixins (e.g., &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;) to handle permission evaluation and deadline-related operations.&lt;br /&gt;
# Introduce instance-level methods (e.g., &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;) to replace class-level utilities and improve testability.&lt;br /&gt;
&lt;br /&gt;
Permission &amp;amp; Behavior Issues&lt;br /&gt;
# Integrate role-based and time-based permission checks within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; to ensure consistent access control.&lt;br /&gt;
# Redesign the &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.&lt;br /&gt;
# Add missing deadline types (&amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt;) and remove duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries for data integrity.&lt;br /&gt;
&lt;br /&gt;
== Proposed Solution ==&lt;br /&gt;
The proposed solution restructures the due date system by separating concerns across dedicated models and mixin modules.&lt;br /&gt;
Deadline definitions (&amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt;), core deadline data (&amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;), and behavioral logic (&amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;) are isolated so each component has a single clear responsibility.&lt;br /&gt;
This modular design improves readability, reduces coupling, and makes deadline-related behavior easier to extend and maintain.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Duedate-diagram.png|600px]]&lt;br /&gt;
&lt;br /&gt;
This diagram visualizes the redesigned architecture: blue/red/green indicate Rails models, orange indicates modules. &lt;br /&gt;
Parent models call DueDateActions, which manages DueDate, while DueDatePermissions checks user rights using reference models. &lt;br /&gt;
&lt;br /&gt;
=== DeadlineType Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Although the current codebase includes a &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model, it only serves as a passive lookup table for associations with &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt;.  &lt;br /&gt;
In practice, most parts of the system still rely on hard-coded numeric IDs or manual lookups such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Example of current usage&lt;br /&gt;
deadline_type_id = DeadlineType.find_by(name: 'review').id&lt;br /&gt;
if due_date.deadline_type_id == deadline_type_id&lt;br /&gt;
  # Perform review-related logic&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of using the model as an active domain object, &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; directly compares IDs or string literals.  &lt;br /&gt;
This leads to several structural issues:&lt;br /&gt;
&lt;br /&gt;
* Inconsistent references (some use IDs, others strings)&lt;br /&gt;
* Tight coupling between &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
* Lack of helper semantics (e.g., &amp;lt;code&amp;gt;is_review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;is_submission?&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Data duplication and integrity risks (two &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; rows)&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; will serve as a source of truth, defining all valid deadline types and providing meaningful interfaces.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table defines all canonical deadline categories used by the system.&lt;br /&gt;
Each row represents one distinct deadline type, and every &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; record must reference one of these entries through &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Below is a structural description of the table:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; Table Structure                                                    &lt;br /&gt;
|- &lt;br /&gt;
! Column                                                                                         &lt;br /&gt;
! Type                                                                                           &lt;br /&gt;
! Description                                                                                    &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| id                                                                                               &lt;br /&gt;
| BIGINT (Primary Key)                                                                             &lt;br /&gt;
| Unique identifier for each deadline type; referenced by &amp;lt;code&amp;gt;due_dates.deadline_type_id&amp;lt;/code&amp;gt;. &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| name                                                                                             &lt;br /&gt;
| VARCHAR(255)                                                                                     &lt;br /&gt;
| Symbolic name (e.g., &amp;lt;code&amp;gt;submission&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review&amp;lt;/code&amp;gt;); must be unique.              &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| description                                                                                      &lt;br /&gt;
| TEXT                                                                                             &lt;br /&gt;
| Human-readable explanation of the deadline category.                                             &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| created_at                                                                                       &lt;br /&gt;
| TIMESTAMP                                                                                        &lt;br /&gt;
| Time when the row was created.                                                                   &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| updated_at                                                                                       &lt;br /&gt;
| TIMESTAMP                                                                                        &lt;br /&gt;
| Time when the row was last updated.                                                              &lt;br /&gt;
|}                                                                                                &lt;br /&gt;
&lt;br /&gt;
This schema ensures that:&lt;br /&gt;
&lt;br /&gt;
* Each deadline type has a unique name and description.&lt;br /&gt;
* No duplicate entries can exist (e.g., the prior duplicated &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; was cleaned).&lt;br /&gt;
* All &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; rows reference this master table through &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
* The &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; column acts as the authoritative key used throughout permissions and due-date logic.&lt;br /&gt;
&lt;br /&gt;
Migrated constraints also ensure that &amp;lt;code&amp;gt;due_dates.deadline_type_id&amp;lt;/code&amp;gt; is consistently stored as &amp;lt;code&amp;gt;BIGINT&amp;lt;/code&amp;gt;, allowing a long-term stable keyspace.&lt;br /&gt;
&lt;br /&gt;
==== Deadline Type Definitions ====&lt;br /&gt;
&lt;br /&gt;
Because each row in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; is referenced by &amp;lt;code&amp;gt;DueDate.deadline_type_id&amp;lt;/code&amp;gt;, the following definitions map directly to the schema above.&lt;br /&gt;
&lt;br /&gt;
Each ID corresponds exactly to the &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; field in the table and serves as the system-wide canonical reference for that deadline category.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Canonical Deadline Type Entries                                       &lt;br /&gt;
|-&lt;br /&gt;
! ID                                                                    &lt;br /&gt;
! Name                                                                  &lt;br /&gt;
! Description                                                           &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 1                                                                       &lt;br /&gt;
| submission                                                              &lt;br /&gt;
| Student submission deadlines                                            &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 2                                                                       &lt;br /&gt;
| review                                                                  &lt;br /&gt;
| Peer review deadlines                                                   &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 3                                                                       &lt;br /&gt;
| teammate_review                                                         &lt;br /&gt;
| Team member evaluation deadlines                                        &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 5                                                                       &lt;br /&gt;
| metareview                                                              &lt;br /&gt;
| Meta-review deadlines (kept for backward compatibility)                 &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 6                                                                       &lt;br /&gt;
| drop_topic                                                              &lt;br /&gt;
| Topic drop deadlines                                                    &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 7                                                                       &lt;br /&gt;
| signup                                                                  &lt;br /&gt;
| Topic signup deadlines                                                  &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 8                                                                       &lt;br /&gt;
| team_formation                                                          &lt;br /&gt;
| Team formation deadlines (duplicate entries cleaned; ID 8 is canonical) &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 11                                                                      &lt;br /&gt;
| quiz                                                                    &lt;br /&gt;
| Quiz completion deadlines                                               &lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
These definitions are seeded during migration and act as the source of truth for all deadline-related logic within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;.&lt;br /&gt;
By linking each &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; to one of these entries through &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;, the system standardizes behavior and eliminates inconsistent ID or string comparisons from earlier implementations.&lt;br /&gt;
&lt;br /&gt;
==== DeadlineType Model Implementation ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineType &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
  validates :description, presence: true&lt;br /&gt;
&lt;br /&gt;
  has_many :due_dates, foreign_key: :deadline_type_id, dependent: :restrict_with_exception&lt;br /&gt;
&lt;br /&gt;
  # Semantic helper methods for deadline type identification&lt;br /&gt;
  def submission?&lt;br /&gt;
    name == 'submission'&lt;br /&gt;
  end&lt;br /&gt;
  # same code for review, teammate_review, quiz, team_formation, signup, drop_topic   &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DeadlineRight Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model represents the permission level for a specific action at a given deadline.&lt;br /&gt;
It defines three states: &lt;br /&gt;
* &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt; : Action is allowed without penalty&lt;br /&gt;
* &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt; : Action is allowed but marked as late&lt;br /&gt;
* &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt; : Action is not permitted&lt;br /&gt;
&lt;br /&gt;
These values are referenced by &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; through fields such as &amp;lt;code&amp;gt;submission_allowed_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review_allowed_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz_allowed_id&amp;lt;/code&amp;gt;, and others.&lt;br /&gt;
This allows the system to determine user permissions by combining:&lt;br /&gt;
&lt;br /&gt;
# The current deadline’s allowed state&lt;br /&gt;
# The participant’s role-based ability (submit, review, take quiz)&lt;br /&gt;
&lt;br /&gt;
This combination logic is implemented in &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
The &amp;lt;code&amp;gt;deadline_rights&amp;lt;/code&amp;gt; table stores the canonical permission states used across the entire due-date system.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_rights&amp;lt;/code&amp;gt; Table Structure                                                         &lt;br /&gt;
|-&lt;br /&gt;
! Column                                                                                               &lt;br /&gt;
! Type                                                                                                 &lt;br /&gt;
! Description                                                                                          &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| id                                                                                                     &lt;br /&gt;
| BIGINT (Primary Key)                                                                                  &lt;br /&gt;
| Unique identifier for each permission state. Referenced by &amp;lt;code&amp;gt;due_dates.*_allowed_id&amp;lt;/code&amp;gt; fields. &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| name                                                                                                   &lt;br /&gt;
| VARCHAR(255)                                                                                           &lt;br /&gt;
| Symbolic permission name (&amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;). Must be unique.        &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| description                                                                                            &lt;br /&gt;
| TEXT                                                                                                   &lt;br /&gt;
| Human-readable explanation of what the permission state means.                                         &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| created_at                                                                                             &lt;br /&gt;
| TIMESTAMP                                                                                              &lt;br /&gt;
| Timestamp when the entry was created.                                                                  &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| updated_at                                                                                             &lt;br /&gt;
| TIMESTAMP                                                                                              &lt;br /&gt;
| Timestamp when the entry was last updated.                                                             &lt;br /&gt;
|}  &lt;br /&gt;
&lt;br /&gt;
==== DeadlineRight Model ====&lt;br /&gt;
Instead of embedding permission logic inside &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;, the &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model defines the meaning of each permission state.&lt;br /&gt;
This removes magic strings (&amp;lt;code&amp;gt;&amp;quot;OK&amp;quot;&amp;lt;/code&amp;gt;) and numeric comparisons and replaces them with expressive model-level semantics.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Semantic Meaning Provided by &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt;                    &lt;br /&gt;
|-&lt;br /&gt;
! Concept                                                                    &lt;br /&gt;
! Meaning                                                                    &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| allows_action?                                                               &lt;br /&gt;
| True for &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;; false for &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;.    &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| denies_action?                                                               &lt;br /&gt;
| True only for &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;.                                               &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| allows_with_penalty?                                                         &lt;br /&gt;
| True only for &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;.                                             &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| allows_without_penalty?                                                      &lt;br /&gt;
| True only for &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.                                               &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| permission_level                                                             &lt;br /&gt;
| Numeric ordering: No = 0, Late = 1, OK = 2. Used for comparison and sorting. &lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
These behaviors allow other parts of the system—particularly &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;—to make consistent, readable decisions.&lt;br /&gt;
&lt;br /&gt;
=== DueDate Model Refactoring ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model currently mixes persistence logic, deadline calculation, and permission checking, violating SRP.  &lt;br /&gt;
Most of its logic is implemented as class methods, which makes testing and extension difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Current design&lt;br /&gt;
def self.copy(old_assignment_id, new_assignment_id)&lt;br /&gt;
  duedates = where(parent_id: old_assignment_id)&lt;br /&gt;
  duedates.each do |orig_due_date|&lt;br /&gt;
    new_due_date = orig_due_date.dup&lt;br /&gt;
    new_due_date.parent_id = new_assignment_id&lt;br /&gt;
    new_due_date.save&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Improved Design ====&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model is simpler, more modular, and focused on representing a single deadline entry.&lt;br /&gt;
Behavior previously embedded inside &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; has been extracted into appropriate modules:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Responsibility Distribution After Refactoring                        &lt;br /&gt;
|-&lt;br /&gt;
! Concern                                                              &lt;br /&gt;
! Where It Lives Now                                                   &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Permission checking                                                    &lt;br /&gt;
| &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module                                 &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Deadline-related actions (next deadline, stage name, deadline copying) &lt;br /&gt;
| &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module (included in parent models)         &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Deadline type semantics                                                &lt;br /&gt;
| &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model                                        &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Core deadline data (due_at, round, parent)                             &lt;br /&gt;
| &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model                                             &lt;br /&gt;
|}   &lt;br /&gt;
&lt;br /&gt;
===== Instance-Based Behavior Instead of Class Methods =====&lt;br /&gt;
&lt;br /&gt;
Previously, many behaviors were class methods (e.g., copying deadlines).&lt;br /&gt;
These methods did not reflect object-level behavior and often duplicated logic.&lt;br /&gt;
&lt;br /&gt;
The redesign moves these operations to:&lt;br /&gt;
&lt;br /&gt;
* instance methods for actions on a specific &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;&lt;br /&gt;
* modules (DueDateActions) for higher-level behaviors on assignments or topics&lt;br /&gt;
&lt;br /&gt;
This separation:&lt;br /&gt;
&lt;br /&gt;
* improves testability&lt;br /&gt;
* aligns with Rails conventions&lt;br /&gt;
* avoids state duplication&lt;br /&gt;
&lt;br /&gt;
===== Clearer Query Methods =====&lt;br /&gt;
&lt;br /&gt;
The model now exposes simple, intuitive instance-level questions:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Examples of Simplified State Queries    &lt;br /&gt;
|-&lt;br /&gt;
! Method                                  &lt;br /&gt;
! Meaning                                 &lt;br /&gt;
|-                                         &lt;br /&gt;
| &amp;lt;code&amp;gt;upcoming?&amp;lt;/code&amp;gt;                    &lt;br /&gt;
| Whether the deadline occurs in the future &lt;br /&gt;
|-                                         &lt;br /&gt;
| &amp;lt;code&amp;gt;overdue?&amp;lt;/code&amp;gt;                     &lt;br /&gt;
| Whether the deadline has already passed   &lt;br /&gt;
|-                                         &lt;br /&gt;
| &amp;lt;code&amp;gt;deadline_type_name&amp;lt;/code&amp;gt;           &lt;br /&gt;
| Human-readable deadline type              &lt;br /&gt;
|}                                         &lt;br /&gt;
&lt;br /&gt;
These small, composable predicates replace multiple ad-hoc comparisons scattered across the codebase.&lt;br /&gt;
&lt;br /&gt;
=== Permission-Checking Methods ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Currently, permission checks depend only on user roles and ignore the actual deadline.  &lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Role-based permissions (participant_helpers.rb)&lt;br /&gt;
def participant_permissions(authorization)&lt;br /&gt;
  case authorization&lt;br /&gt;
  when 'reader'   then { can_submit: false, can_review: true, can_take_quiz: true }&lt;br /&gt;
  when 'reviewer' then { can_submit: false, can_review: true, can_take_quiz: false }&lt;br /&gt;
  when 'submitter' then { can_submit: true, can_review: false, can_take_quiz: false }&lt;br /&gt;
  else { can_submit: true, can_review: true, can_take_quiz: true }&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And deadline-based checks exist separately in the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def submission_allowed(topic_id = nil)&lt;br /&gt;
  check_condition('submission_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
def can_review(topic_id = nil)&lt;br /&gt;
  check_condition('review_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These two layers (role-based and time-based) never interact, leading to inconsistencies (e.g., a user with &amp;lt;code&amp;gt;can_submit=true&amp;lt;/code&amp;gt; after deadline).&lt;br /&gt;
&lt;br /&gt;
==== Proposed Changes ====&lt;br /&gt;
&lt;br /&gt;
The new &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module integrates role-based and deadline-based checks:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDatePermissions&lt;br /&gt;
  # Permission checking methods that combine deadline-based and role-based logic&lt;br /&gt;
  #&lt;br /&gt;
  # These methods must check both:&lt;br /&gt;
  # 1. Whether the action is permitted by the current deadline (submission_allowed_id, review_allowed_id, etc.)&lt;br /&gt;
  # 2. Whether the participant has the necessary permissions (can_submit, can_review, can_take_quiz fields)&lt;br /&gt;
&lt;br /&gt;
  # Check if submission is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_submit?(participant = nil)&lt;br /&gt;
    return false unless submission_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_submit&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: submission_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if review is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_review?(participant = nil)&lt;br /&gt;
    return false unless review_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_review&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: review_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if taking a quiz is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_take_quiz?(participant = nil)&lt;br /&gt;
    return false unless quiz_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_take_quiz&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: quiz_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if teammate review is allowed&lt;br /&gt;
  def can_review_teammate?(participant = nil)&lt;br /&gt;
    return false unless teammate_review_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_review&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: teammate_review_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Generic permission checker&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    permission_field = &amp;quot;#{activity}_allowed_id&amp;quot;&lt;br /&gt;
    return false unless respond_to?(permission_field)&lt;br /&gt;
&lt;br /&gt;
    allowed_id = public_send(permission_field)&lt;br /&gt;
    return false unless allowed_id&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get permission status for an action (OK, Late, No)&lt;br /&gt;
  def permission_status_for(action)&lt;br /&gt;
    permission_field = &amp;quot;#{action}_allowed_id&amp;quot;&lt;br /&gt;
    return 'No' unless respond_to?(permission_field)&lt;br /&gt;
&lt;br /&gt;
    allowed_id = public_send(permission_field)&lt;br /&gt;
    return 'No' unless allowed_id&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name || 'No'&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This module is included in the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== DueDateActions Mixin ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module provides deadline-related operations for models that have due dates (Assignment, SignUpTopic).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDateActions&lt;br /&gt;
  # Generic activity permission checker that determines if an activity is permissible&lt;br /&gt;
  # based on the current deadline state for this parent object&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    current_deadline = next_due_date()&lt;br /&gt;
    return false unless current_deadline&lt;br /&gt;
&lt;br /&gt;
    current_deadline.activity_permissible?(activity)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Syntactic sugar methods for common activities&lt;br /&gt;
  def submission_permissible?&lt;br /&gt;
    activity_permissible?(:submission)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def review_permissible?&lt;br /&gt;
    activity_permissible?(:review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def teammate_review_permissible?&lt;br /&gt;
    activity_permissible?(:teammate_review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def quiz_permissible?&lt;br /&gt;
    activity_permissible?(:quiz)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def team_formation_permissible?&lt;br /&gt;
    activity_permissible?(:team_formation)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def signup_permissible?&lt;br /&gt;
    activity_permissible?(:signup)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def drop_topic_permissible?&lt;br /&gt;
    activity_permissible?(:drop_topic)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the next due date for this assignment&lt;br /&gt;
  def next_due_date(topic_id = nil)&lt;br /&gt;
    # If a topic is specified and this assignment has topic-specific deadlines,&lt;br /&gt;
    # look for topic due dates first&lt;br /&gt;
    if topic_id &amp;amp;&amp;amp; has_topic_specific_deadlines?&lt;br /&gt;
      topic_deadline = due_dates.where(parent_id: topic_id, parent_type: 'SignUpTopic')&lt;br /&gt;
                               .upcoming&lt;br /&gt;
                               .first&lt;br /&gt;
      return topic_deadline if topic_deadline&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Fall back to assignment-level due dates&lt;br /&gt;
    due_dates.upcoming.first&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the current stage name for display purposes&lt;br /&gt;
  def current_stage&lt;br /&gt;
    deadline = next_due_date()&lt;br /&gt;
    return 'finished' unless deadline&lt;br /&gt;
&lt;br /&gt;
    deadline.deadline_type&amp;amp;.name || 'unknown'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if assignment has topic-specific deadlines&lt;br /&gt;
  def has_topic_specific_deadlines?&lt;br /&gt;
    staggered_deadline || due_dates.where(parent_type: 'SignUpTopic').exists?&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Copy due dates to a new parent object&lt;br /&gt;
  def copy_due_dates_to(new_parent)&lt;br /&gt;
    due_dates.find_each do |due_date|&lt;br /&gt;
      new_due_date = due_date.dup&lt;br /&gt;
      new_due_date.parent = new_parent&lt;br /&gt;
      new_due_date.save!&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This module is included in &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class Assignment &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
class SignUpTopic &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Implementation Summary ==&lt;br /&gt;
&lt;br /&gt;
=== Models Created/Modified ===&lt;br /&gt;
&lt;br /&gt;
* '''DeadlineType''' - Canonical deadline type definitions with semantic helper methods&lt;br /&gt;
* '''DeadlineRight''' - Permission state model (OK/Late/No) with comparison methods&lt;br /&gt;
* '''DueDate''' - Refactored with instance methods, scopes, and permission checking&lt;br /&gt;
* '''TopicDueDate''' - STI subclass for topic-specific deadlines&lt;br /&gt;
* '''Assignment''' - Includes DueDateActions mixin&lt;br /&gt;
* '''SignUpTopic''' - Includes DueDateActions mixin&lt;br /&gt;
&lt;br /&gt;
=== Modules Created ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions''' - Permission checking combining deadline and role constraints&lt;br /&gt;
* '''DueDateActions''' - Deadline-related operations for parent models (Assignment, SignUpTopic)&lt;br /&gt;
&lt;br /&gt;
=== Key Improvements ===&lt;br /&gt;
&lt;br /&gt;
# '''Separation of Concerns''': Permission logic separated into dedicated modules&lt;br /&gt;
# '''Instance Methods''': Replaced class methods with testable instance methods&lt;br /&gt;
# '''Polymorphic Design''': Single DueDate model serves both Assignment and SignUpTopic&lt;br /&gt;
# '''Unified Permissions''': Role-based and time-based checks combined in one interface&lt;br /&gt;
# '''Data Integrity''': Removed duplicate deadline types, enforced foreign key constraints&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
The test suite includes comprehensive RSpec tests for the ''DueDate'' model and its associated models (''DeadlineType'', ''DeadlineRight'').&lt;br /&gt;
These tests verify model validations, scopes, instance methods, class methods, and callbacks with extensive coverage of edge cases and error conditions.&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
The test suite follows RSpec best practices with:&lt;br /&gt;
* '''Nested contexts''' for different scenarios (e.g., &amp;quot;when parent is missing&amp;quot;, &amp;quot;when round is 0&amp;quot;)&lt;br /&gt;
* '''Multiple assertions per context''' to verify behavior from different angles&lt;br /&gt;
* '''Edge case coverage''' including empty collections, nil values, and boundary conditions&lt;br /&gt;
* '''Error case testing''' for invalid data and non-existent records&lt;br /&gt;
* '''Clear descriptive names''' using &amp;quot;context 'when...'&amp;quot; and &amp;quot;it 'does something'&amp;quot; patterns&lt;br /&gt;
&lt;br /&gt;
=== Current Coverage ===&lt;br /&gt;
&lt;br /&gt;
==== Associations ====&lt;br /&gt;
* Tests verify &amp;lt;code&amp;gt;belongs_to :parent&amp;lt;/code&amp;gt; (polymorphic) and &amp;lt;code&amp;gt;belongs_to :deadline_type&amp;lt;/code&amp;gt; relationships&lt;br /&gt;
&lt;br /&gt;
==== Validations ====&lt;br /&gt;
* '''Required field validation''': Tests for parent, due_at, and deadline_type_id presence&lt;br /&gt;
* '''Round validation''': Tests for positive values, zero rejection, negative rejection, and nil handling&lt;br /&gt;
* '''Error messages''': Verifies specific error messages are added to appropriate fields&lt;br /&gt;
* '''Valid record creation''': Confirms records persist when all requirements are met&lt;br /&gt;
&lt;br /&gt;
==== Scopes ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;''': Returns future due dates ordered by due_at, excludes past dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;''': Returns past due dates ordered by due_at, excludes future dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;''': Filters by round number, handles non-existent rounds&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt;''': Filters by deadline type name, handles non-existent types&lt;br /&gt;
&lt;br /&gt;
==== Instance Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#overdue?&amp;lt;/code&amp;gt;''': Tests past dates (true), future dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#upcoming?&amp;lt;/code&amp;gt;''': Tests future dates (true), past dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#set&amp;lt;/code&amp;gt;''': Verifies updating deadline_type_id, parent_id, and round with persistence and error handling&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#copy&amp;lt;/code&amp;gt;''': Tests duplication to new assignment, attribute preservation, error handling for non-existent targets&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#deadline_type_name&amp;lt;/code&amp;gt;''': Returns associated deadline type name, handles nil cases&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#last_deadline?&amp;lt;/code&amp;gt;''': Tests detection of final deadline, handles multiple deadlines and single deadline scenarios&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#&amp;lt;=&amp;gt;&amp;lt;/code&amp;gt;''': Tests comparison by due_at time, handles non-DueDate objects&lt;br /&gt;
&lt;br /&gt;
==== Class Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.sort_due_dates&amp;lt;/code&amp;gt;''': Sorts by due_at ascending, handles empty arrays and single elements&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.any_future_due_dates?&amp;lt;/code&amp;gt;''': Detects future dates in collections, handles empty arrays and mixed date collections&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.copy&amp;lt;/code&amp;gt;''': Copies all due dates between assignments, preserves attributes, handles empty sources and non-existent records&lt;br /&gt;
&lt;br /&gt;
==== Callbacks ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;before_save :set_default_round&amp;lt;/code&amp;gt;''': Sets round to 1 when not specified, preserves explicit values, handles nil&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
Tests follow RSpec best practices with nested contexts for different scenarios:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
describe 'validations' do&lt;br /&gt;
  context 'when parent is missing' do&lt;br /&gt;
    it 'is invalid' do&lt;br /&gt;
      expect(due_date).to be_invalid&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'has error on parent field' do&lt;br /&gt;
      due_date.valid?&lt;br /&gt;
      expect(due_date.errors[:parent]).to include('must exist')&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when round validation' do&lt;br /&gt;
    context 'with round value of 0' do&lt;br /&gt;
      it 'is invalid' do&lt;br /&gt;
        expect(due_date).to be_invalid&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'with nil round' do&lt;br /&gt;
      it 'sets default round to 1' do&lt;br /&gt;
        expect(due_date.round).to eq(1)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
=== Demo Link ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add deadline notification system using the new permission framework&lt;br /&gt;
* Implement deadline templates for common assignment patterns&lt;br /&gt;
* Add API endpoints for mobile app integration&lt;br /&gt;
* Create admin dashboard for monitoring deadline compliance across courses&lt;br /&gt;
* Add automated deadline extension workflows based on configurable rules&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167197</id>
		<title>CSC/ECE 517 Fall 2025 - E2566. Finish DueDates</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167197"/>
		<updated>2025-12-02T04:40:03Z</updated>

		<summary type="html">&lt;p&gt;Skim253: /* Test Examples */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
This project aims to complete the implementation of the DueDate system in Expertiza. The current implementation consists mostly of class methods and lacks proper definition of different deadline types and permission checking functionality. This redesign will create a more robust, readable, and maintainable due date system.&lt;br /&gt;
&lt;br /&gt;
=== Existing Components ===&lt;br /&gt;
* &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model with polymorphic parent association (Assignment/SignUpTopic)&lt;br /&gt;
* &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; subclasses&lt;br /&gt;
* Basic CRUD operations (within &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AssignmentForm&amp;lt;/code&amp;gt;) and sorting functionality (&amp;lt;code&amp;gt;deadline_sort&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Database schema with &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt; field&lt;br /&gt;
&lt;br /&gt;
=== Current Behavior ===&lt;br /&gt;
&lt;br /&gt;
====Admin's View====&lt;br /&gt;
[[File:Duedates.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* When editing an assignment, due dates can be modified under the Due Dates tab.&lt;br /&gt;
* Each row contains the following columns: &amp;lt;code&amp;gt;Date &amp;amp; Time&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Use Date Updater&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Submission Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Teammate Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Meta-Review Allowed&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Reminder&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:Assignments-CurrentStage.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* In the Assignments table, the ''DueDate'' of each assignment is displayed as Current Stage and Stage Deadline.&lt;br /&gt;
&lt;br /&gt;
====Student's View====&lt;br /&gt;
[[File:Student-assignments.png|600px]]&lt;br /&gt;
* Students can view the due date information under &amp;lt;code&amp;gt;Current State&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Stage Deadline&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
* The actions available to students are determined by the current DueDate status.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-not-over.png|600px]]&lt;br /&gt;
* For example, during the submission period, students can access the &amp;lt;code&amp;gt;Submit a hyperlink&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Submit a file&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-over.png|600px]]&lt;br /&gt;
* Once the due date has passed, students cannot submit either a hyperlink or a file.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Currently, there are some glaring issues with how due_dates was created in the first place, including redundancy and lack of modularity.&lt;br /&gt;
&lt;br /&gt;
Modeling &amp;amp; Structural Issues&lt;br /&gt;
# No dedicated &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to define different kinds of deadlines. The existing &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; class only serves as an association for &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; models, and is never referenced in the current &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; implementation.&lt;br /&gt;
# Duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table, which may lead to potential bugs.&lt;br /&gt;
# Incomplete deadline type definitions - &amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt; need to be added.&lt;br /&gt;
# No clear separation of concerns - data persistence, business logic, and permission logic are mixed in a single model.&lt;br /&gt;
&lt;br /&gt;
Permission &amp;amp; Behavior Issues&lt;br /&gt;
# Overuse of class methods making the code difficult to maintain.&lt;br /&gt;
# Missing permission checking logic for user actions.&lt;br /&gt;
&lt;br /&gt;
=== Tasks Identified ===&lt;br /&gt;
&lt;br /&gt;
Modeling &amp;amp; Structural Issues&lt;br /&gt;
# Refactor the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model to separate persistence, business logic, and permission logic into distinct layers.&lt;br /&gt;
# Implement reusable mixins (e.g., &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;) to handle permission evaluation and deadline-related operations.&lt;br /&gt;
# Introduce instance-level methods (e.g., &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;) to replace class-level utilities and improve testability.&lt;br /&gt;
&lt;br /&gt;
Permission &amp;amp; Behavior Issues&lt;br /&gt;
# Integrate role-based and time-based permission checks within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; to ensure consistent access control.&lt;br /&gt;
# Redesign the &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.&lt;br /&gt;
# Add missing deadline types (&amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt;) and remove duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries for data integrity.&lt;br /&gt;
&lt;br /&gt;
== Proposed Solution ==&lt;br /&gt;
The proposed solution restructures the due date system by separating concerns across dedicated models and mixin modules.&lt;br /&gt;
Deadline definitions (&amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt;), core deadline data (&amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;), and behavioral logic (&amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;) are isolated so each component has a single clear responsibility.&lt;br /&gt;
This modular design improves readability, reduces coupling, and makes deadline-related behavior easier to extend and maintain.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Duedate-diagram.png|600px]]&lt;br /&gt;
&lt;br /&gt;
This diagram visualizes the redesigned architecture: blue/red/green indicate Rails models, orange indicates modules. &lt;br /&gt;
Parent models call DueDateActions, which manages DueDate, while DueDatePermissions checks user rights using reference models. &lt;br /&gt;
&lt;br /&gt;
=== DeadlineType Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Although the current codebase includes a &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model, it only serves as a passive lookup table for associations with &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt;.  &lt;br /&gt;
In practice, most parts of the system still rely on hard-coded numeric IDs or manual lookups such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Example of current usage&lt;br /&gt;
deadline_type_id = DeadlineType.find_by(name: 'review').id&lt;br /&gt;
if due_date.deadline_type_id == deadline_type_id&lt;br /&gt;
  # Perform review-related logic&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of using the model as an active domain object, &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; directly compares IDs or string literals.  &lt;br /&gt;
This leads to several structural issues:&lt;br /&gt;
&lt;br /&gt;
* Inconsistent references (some use IDs, others strings)&lt;br /&gt;
* Tight coupling between &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
* Lack of helper semantics (e.g., &amp;lt;code&amp;gt;is_review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;is_submission?&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Data duplication and integrity risks (two &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; rows)&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; will serve as a source of truth, defining all valid deadline types and providing meaningful interfaces.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table defines all canonical deadline categories used by the system.&lt;br /&gt;
Each row represents one distinct deadline type, and every &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; record must reference one of these entries through &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Below is a structural description of the table:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; Table Structure                                                    &lt;br /&gt;
|- &lt;br /&gt;
! Column                                                                                         &lt;br /&gt;
! Type                                                                                           &lt;br /&gt;
! Description                                                                                    &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| id                                                                                               &lt;br /&gt;
| BIGINT (Primary Key)                                                                             &lt;br /&gt;
| Unique identifier for each deadline type; referenced by &amp;lt;code&amp;gt;due_dates.deadline_type_id&amp;lt;/code&amp;gt;. &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| name                                                                                             &lt;br /&gt;
| VARCHAR(255)                                                                                     &lt;br /&gt;
| Symbolic name (e.g., &amp;lt;code&amp;gt;submission&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review&amp;lt;/code&amp;gt;); must be unique.              &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| description                                                                                      &lt;br /&gt;
| TEXT                                                                                             &lt;br /&gt;
| Human-readable explanation of the deadline category.                                             &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| created_at                                                                                       &lt;br /&gt;
| TIMESTAMP                                                                                        &lt;br /&gt;
| Time when the row was created.                                                                   &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| updated_at                                                                                       &lt;br /&gt;
| TIMESTAMP                                                                                        &lt;br /&gt;
| Time when the row was last updated.                                                              &lt;br /&gt;
|}                                                                                                &lt;br /&gt;
&lt;br /&gt;
This schema ensures that:&lt;br /&gt;
&lt;br /&gt;
* Each deadline type has a unique name and description.&lt;br /&gt;
* No duplicate entries can exist (e.g., the prior duplicated &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; was cleaned).&lt;br /&gt;
* All &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; rows reference this master table through &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
* The &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; column acts as the authoritative key used throughout permissions and due-date logic.&lt;br /&gt;
&lt;br /&gt;
Migrated constraints also ensure that &amp;lt;code&amp;gt;due_dates.deadline_type_id&amp;lt;/code&amp;gt; is consistently stored as &amp;lt;code&amp;gt;BIGINT&amp;lt;/code&amp;gt;, allowing a long-term stable keyspace.&lt;br /&gt;
&lt;br /&gt;
==== Deadline Type Definitions ====&lt;br /&gt;
&lt;br /&gt;
Because each row in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; is referenced by &amp;lt;code&amp;gt;DueDate.deadline_type_id&amp;lt;/code&amp;gt;, the following definitions map directly to the schema above.&lt;br /&gt;
&lt;br /&gt;
Each ID corresponds exactly to the &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; field in the table and serves as the system-wide canonical reference for that deadline category.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Canonical Deadline Type Entries                                       &lt;br /&gt;
|-&lt;br /&gt;
! ID                                                                    &lt;br /&gt;
! Name                                                                  &lt;br /&gt;
! Description                                                           &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 1                                                                       &lt;br /&gt;
| submission                                                              &lt;br /&gt;
| Student submission deadlines                                            &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 2                                                                       &lt;br /&gt;
| review                                                                  &lt;br /&gt;
| Peer review deadlines                                                   &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 3                                                                       &lt;br /&gt;
| teammate_review                                                         &lt;br /&gt;
| Team member evaluation deadlines                                        &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 5                                                                       &lt;br /&gt;
| metareview                                                              &lt;br /&gt;
| Meta-review deadlines (kept for backward compatibility)                 &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 6                                                                       &lt;br /&gt;
| drop_topic                                                              &lt;br /&gt;
| Topic drop deadlines                                                    &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 7                                                                       &lt;br /&gt;
| signup                                                                  &lt;br /&gt;
| Topic signup deadlines                                                  &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 8                                                                       &lt;br /&gt;
| team_formation                                                          &lt;br /&gt;
| Team formation deadlines (duplicate entries cleaned; ID 8 is canonical) &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 11                                                                      &lt;br /&gt;
| quiz                                                                    &lt;br /&gt;
| Quiz completion deadlines                                               &lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
These definitions are seeded during migration and act as the source of truth for all deadline-related logic within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;.&lt;br /&gt;
By linking each &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; to one of these entries through &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;, the system standardizes behavior and eliminates inconsistent ID or string comparisons from earlier implementations.&lt;br /&gt;
&lt;br /&gt;
==== DeadlineType Model Implementation ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineType &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
  validates :description, presence: true&lt;br /&gt;
&lt;br /&gt;
  has_many :due_dates, foreign_key: :deadline_type_id, dependent: :restrict_with_exception&lt;br /&gt;
&lt;br /&gt;
  # Semantic helper methods for deadline type identification&lt;br /&gt;
  def submission?&lt;br /&gt;
    name == 'submission'&lt;br /&gt;
  end&lt;br /&gt;
  # same code for review, teammate_review, quiz, team_formation, signup, drop_topic   &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DeadlineRight Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model represents the permission level for a specific action at a given deadline.&lt;br /&gt;
It defines three states: &lt;br /&gt;
* &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt; : Action is allowed without penalty&lt;br /&gt;
* &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt; : Action is allowed but marked as late&lt;br /&gt;
* &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt; : Action is not permitted&lt;br /&gt;
&lt;br /&gt;
These values are referenced by &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; through fields such as &amp;lt;code&amp;gt;submission_allowed_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review_allowed_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz_allowed_id&amp;lt;/code&amp;gt;, and others.&lt;br /&gt;
This allows the system to determine user permissions by combining:&lt;br /&gt;
&lt;br /&gt;
# The current deadline’s allowed state&lt;br /&gt;
# The participant’s role-based ability (submit, review, take quiz)&lt;br /&gt;
&lt;br /&gt;
This combination logic is implemented in &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
The &amp;lt;code&amp;gt;deadline_rights&amp;lt;/code&amp;gt; table stores the canonical permission states used across the entire due-date system.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_rights&amp;lt;/code&amp;gt; Table Structure                                                         &lt;br /&gt;
|-&lt;br /&gt;
! Column                                                                                               &lt;br /&gt;
! Type                                                                                                 &lt;br /&gt;
! Description                                                                                          &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| id                                                                                                     &lt;br /&gt;
| BIGINT (Primary Key)                                                                                  &lt;br /&gt;
| Unique identifier for each permission state. Referenced by &amp;lt;code&amp;gt;due_dates.*_allowed_id&amp;lt;/code&amp;gt; fields. &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| name                                                                                                   &lt;br /&gt;
| VARCHAR(255)                                                                                           &lt;br /&gt;
| Symbolic permission name (&amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;). Must be unique.        &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| description                                                                                            &lt;br /&gt;
| TEXT                                                                                                   &lt;br /&gt;
| Human-readable explanation of what the permission state means.                                         &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| created_at                                                                                             &lt;br /&gt;
| TIMESTAMP                                                                                              &lt;br /&gt;
| Timestamp when the entry was created.                                                                  &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| updated_at                                                                                             &lt;br /&gt;
| TIMESTAMP                                                                                              &lt;br /&gt;
| Timestamp when the entry was last updated.                                                             &lt;br /&gt;
|}  &lt;br /&gt;
&lt;br /&gt;
==== DeadlineRight Model ====&lt;br /&gt;
Instead of embedding permission logic inside &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;, the &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model defines the meaning of each permission state.&lt;br /&gt;
This removes magic strings (&amp;lt;code&amp;gt;&amp;quot;OK&amp;quot;&amp;lt;/code&amp;gt;) and numeric comparisons and replaces them with expressive model-level semantics.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Semantic Meaning Provided by &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt;                    &lt;br /&gt;
|-&lt;br /&gt;
! Concept                                                                    &lt;br /&gt;
! Meaning                                                                    &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| allows_action?                                                               &lt;br /&gt;
| True for &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;; false for &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;.    &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| denies_action?                                                               &lt;br /&gt;
| True only for &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;.                                               &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| allows_with_penalty?                                                         &lt;br /&gt;
| True only for &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;.                                             &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| allows_without_penalty?                                                      &lt;br /&gt;
| True only for &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.                                               &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| permission_level                                                             &lt;br /&gt;
| Numeric ordering: No = 0, Late = 1, OK = 2. Used for comparison and sorting. &lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
These behaviors allow other parts of the system—particularly &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;—to make consistent, readable decisions.&lt;br /&gt;
&lt;br /&gt;
=== DueDate Model Refactoring ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model currently mixes persistence logic, deadline calculation, and permission checking, violating SRP.  &lt;br /&gt;
Most of its logic is implemented as class methods, which makes testing and extension difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Current design&lt;br /&gt;
def self.copy(old_assignment_id, new_assignment_id)&lt;br /&gt;
  duedates = where(parent_id: old_assignment_id)&lt;br /&gt;
  duedates.each do |orig_due_date|&lt;br /&gt;
    new_due_date = orig_due_date.dup&lt;br /&gt;
    new_due_date.parent_id = new_assignment_id&lt;br /&gt;
    new_due_date.save&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Improved Design ====&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model is simpler, more modular, and focused on representing a single deadline entry.&lt;br /&gt;
Behavior previously embedded inside &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; has been extracted into appropriate modules:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Responsibility Distribution After Refactoring                        &lt;br /&gt;
|-&lt;br /&gt;
! Concern                                                              &lt;br /&gt;
! Where It Lives Now                                                   &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Permission checking                                                    &lt;br /&gt;
| &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module                                 &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Deadline-related actions (next deadline, stage name, deadline copying) &lt;br /&gt;
| &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module (included in parent models)         &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Deadline type semantics                                                &lt;br /&gt;
| &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model                                        &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Core deadline data (due_at, round, parent)                             &lt;br /&gt;
| &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model                                             &lt;br /&gt;
|}   &lt;br /&gt;
&lt;br /&gt;
===== Instance-Based Behavior Instead of Class Methods =====&lt;br /&gt;
&lt;br /&gt;
Previously, many behaviors were class methods (e.g., copying deadlines).&lt;br /&gt;
These methods did not reflect object-level behavior and often duplicated logic.&lt;br /&gt;
&lt;br /&gt;
The redesign moves these operations to:&lt;br /&gt;
&lt;br /&gt;
* instance methods for actions on a specific &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;&lt;br /&gt;
* modules (DueDateActions) for higher-level behaviors on assignments or topics&lt;br /&gt;
&lt;br /&gt;
This separation:&lt;br /&gt;
&lt;br /&gt;
* improves testability&lt;br /&gt;
* aligns with Rails conventions&lt;br /&gt;
* avoids state duplication&lt;br /&gt;
&lt;br /&gt;
===== Clearer Query Methods =====&lt;br /&gt;
&lt;br /&gt;
The model now exposes simple, intuitive instance-level questions:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Examples of Simplified State Queries    &lt;br /&gt;
|-&lt;br /&gt;
! Method                                  &lt;br /&gt;
! Meaning                                 &lt;br /&gt;
|-                                         &lt;br /&gt;
| &amp;lt;code&amp;gt;upcoming?&amp;lt;/code&amp;gt;                    &lt;br /&gt;
| Whether the deadline occurs in the future &lt;br /&gt;
|-                                         &lt;br /&gt;
| &amp;lt;code&amp;gt;overdue?&amp;lt;/code&amp;gt;                     &lt;br /&gt;
| Whether the deadline has already passed   &lt;br /&gt;
|-                                         &lt;br /&gt;
| &amp;lt;code&amp;gt;deadline_type_name&amp;lt;/code&amp;gt;           &lt;br /&gt;
| Human-readable deadline type              &lt;br /&gt;
|}                                         &lt;br /&gt;
&lt;br /&gt;
These small, composable predicates replace multiple ad-hoc comparisons scattered across the codebase.&lt;br /&gt;
&lt;br /&gt;
=== Permission-Checking Methods ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Currently, permission checks depend only on user roles and ignore the actual deadline.  &lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Role-based permissions (participant_helpers.rb)&lt;br /&gt;
def participant_permissions(authorization)&lt;br /&gt;
  case authorization&lt;br /&gt;
  when 'reader'   then { can_submit: false, can_review: true, can_take_quiz: true }&lt;br /&gt;
  when 'reviewer' then { can_submit: false, can_review: true, can_take_quiz: false }&lt;br /&gt;
  when 'submitter' then { can_submit: true, can_review: false, can_take_quiz: false }&lt;br /&gt;
  else { can_submit: true, can_review: true, can_take_quiz: true }&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And deadline-based checks exist separately in the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def submission_allowed(topic_id = nil)&lt;br /&gt;
  check_condition('submission_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
def can_review(topic_id = nil)&lt;br /&gt;
  check_condition('review_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These two layers (role-based and time-based) never interact, leading to inconsistencies (e.g., a user with &amp;lt;code&amp;gt;can_submit=true&amp;lt;/code&amp;gt; after deadline).&lt;br /&gt;
&lt;br /&gt;
==== Proposed Changes ====&lt;br /&gt;
&lt;br /&gt;
The new &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module integrates role-based and deadline-based checks:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDatePermissions&lt;br /&gt;
  # Permission checking methods that combine deadline-based and role-based logic&lt;br /&gt;
  #&lt;br /&gt;
  # These methods must check both:&lt;br /&gt;
  # 1. Whether the action is permitted by the current deadline (submission_allowed_id, review_allowed_id, etc.)&lt;br /&gt;
  # 2. Whether the participant has the necessary permissions (can_submit, can_review, can_take_quiz fields)&lt;br /&gt;
&lt;br /&gt;
  # Check if submission is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_submit?(participant = nil)&lt;br /&gt;
    return false unless submission_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_submit&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: submission_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if review is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_review?(participant = nil)&lt;br /&gt;
    return false unless review_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_review&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: review_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if taking a quiz is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_take_quiz?(participant = nil)&lt;br /&gt;
    return false unless quiz_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_take_quiz&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: quiz_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if teammate review is allowed&lt;br /&gt;
  def can_review_teammate?(participant = nil)&lt;br /&gt;
    return false unless teammate_review_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_review&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: teammate_review_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Generic permission checker&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    permission_field = &amp;quot;#{activity}_allowed_id&amp;quot;&lt;br /&gt;
    return false unless respond_to?(permission_field)&lt;br /&gt;
&lt;br /&gt;
    allowed_id = public_send(permission_field)&lt;br /&gt;
    return false unless allowed_id&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get permission status for an action (OK, Late, No)&lt;br /&gt;
  def permission_status_for(action)&lt;br /&gt;
    permission_field = &amp;quot;#{action}_allowed_id&amp;quot;&lt;br /&gt;
    return 'No' unless respond_to?(permission_field)&lt;br /&gt;
&lt;br /&gt;
    allowed_id = public_send(permission_field)&lt;br /&gt;
    return 'No' unless allowed_id&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name || 'No'&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This module is included in the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== DueDateActions Mixin ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module provides deadline-related operations for models that have due dates (Assignment, SignUpTopic).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDateActions&lt;br /&gt;
  # Generic activity permission checker that determines if an activity is permissible&lt;br /&gt;
  # based on the current deadline state for this parent object&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    current_deadline = next_due_date()&lt;br /&gt;
    return false unless current_deadline&lt;br /&gt;
&lt;br /&gt;
    current_deadline.activity_permissible?(activity)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Syntactic sugar methods for common activities&lt;br /&gt;
  def submission_permissible?&lt;br /&gt;
    activity_permissible?(:submission)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def review_permissible?&lt;br /&gt;
    activity_permissible?(:review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def teammate_review_permissible?&lt;br /&gt;
    activity_permissible?(:teammate_review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def quiz_permissible?&lt;br /&gt;
    activity_permissible?(:quiz)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def team_formation_permissible?&lt;br /&gt;
    activity_permissible?(:team_formation)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def signup_permissible?&lt;br /&gt;
    activity_permissible?(:signup)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def drop_topic_permissible?&lt;br /&gt;
    activity_permissible?(:drop_topic)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the next due date for this assignment&lt;br /&gt;
  def next_due_date(topic_id = nil)&lt;br /&gt;
    # If a topic is specified and this assignment has topic-specific deadlines,&lt;br /&gt;
    # look for topic due dates first&lt;br /&gt;
    if topic_id &amp;amp;&amp;amp; has_topic_specific_deadlines?&lt;br /&gt;
      topic_deadline = due_dates.where(parent_id: topic_id, parent_type: 'SignUpTopic')&lt;br /&gt;
                               .upcoming&lt;br /&gt;
                               .first&lt;br /&gt;
      return topic_deadline if topic_deadline&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Fall back to assignment-level due dates&lt;br /&gt;
    due_dates.upcoming.first&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the current stage name for display purposes&lt;br /&gt;
  def current_stage&lt;br /&gt;
    deadline = next_due_date()&lt;br /&gt;
    return 'finished' unless deadline&lt;br /&gt;
&lt;br /&gt;
    deadline.deadline_type&amp;amp;.name || 'unknown'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if assignment has topic-specific deadlines&lt;br /&gt;
  def has_topic_specific_deadlines?&lt;br /&gt;
    staggered_deadline || due_dates.where(parent_type: 'SignUpTopic').exists?&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Copy due dates to a new parent object&lt;br /&gt;
  def copy_due_dates_to(new_parent)&lt;br /&gt;
    due_dates.find_each do |due_date|&lt;br /&gt;
      new_due_date = due_date.dup&lt;br /&gt;
      new_due_date.parent = new_parent&lt;br /&gt;
      new_due_date.save!&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This module is included in &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class Assignment &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
class SignUpTopic &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Implementation Summary ==&lt;br /&gt;
&lt;br /&gt;
=== Models Created/Modified ===&lt;br /&gt;
&lt;br /&gt;
* '''DeadlineType''' - Canonical deadline type definitions with semantic helper methods&lt;br /&gt;
* '''DeadlineRight''' - Permission state model (OK/Late/No) with comparison methods&lt;br /&gt;
* '''DueDate''' - Refactored with instance methods, scopes, and permission checking&lt;br /&gt;
* '''TopicDueDate''' - STI subclass for topic-specific deadlines&lt;br /&gt;
* '''Assignment''' - Includes DueDateActions mixin&lt;br /&gt;
* '''SignUpTopic''' - Includes DueDateActions mixin&lt;br /&gt;
&lt;br /&gt;
=== Modules Created ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions''' - Permission checking combining deadline and role constraints&lt;br /&gt;
* '''DueDateActions''' - Deadline-related operations for parent models (Assignment, SignUpTopic)&lt;br /&gt;
&lt;br /&gt;
=== Key Improvements ===&lt;br /&gt;
&lt;br /&gt;
# '''Separation of Concerns''': Permission logic separated into dedicated modules&lt;br /&gt;
# '''Instance Methods''': Replaced class methods with testable instance methods&lt;br /&gt;
# '''Polymorphic Design''': Single DueDate model serves both Assignment and SignUpTopic&lt;br /&gt;
# '''Unified Permissions''': Role-based and time-based checks combined in one interface&lt;br /&gt;
# '''Data Integrity''': Removed duplicate deadline types, enforced foreign key constraints&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
The test suite includes comprehensive RSpec tests for the ''DueDate'' model and its associated models (''DeadlineType'', ''DeadlineRight'').&lt;br /&gt;
These tests verify model validations, scopes, instance methods, class methods, and callbacks with extensive coverage of edge cases and error conditions.&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
The test suite follows RSpec best practices with:&lt;br /&gt;
* '''Nested contexts''' for different scenarios (e.g., &amp;quot;when parent is missing&amp;quot;, &amp;quot;when round is 0&amp;quot;)&lt;br /&gt;
* '''Multiple assertions per context''' to verify behavior from different angles&lt;br /&gt;
* '''Edge case coverage''' including empty collections, nil values, and boundary conditions&lt;br /&gt;
* '''Error case testing''' for invalid data and non-existent records&lt;br /&gt;
* '''Clear descriptive names''' using &amp;quot;context 'when...'&amp;quot; and &amp;quot;it 'does something'&amp;quot; patterns&lt;br /&gt;
&lt;br /&gt;
=== Current Coverage ===&lt;br /&gt;
&lt;br /&gt;
==== Associations ====&lt;br /&gt;
* Tests verify &amp;lt;code&amp;gt;belongs_to :parent&amp;lt;/code&amp;gt; (polymorphic) and &amp;lt;code&amp;gt;belongs_to :deadline_type&amp;lt;/code&amp;gt; relationships&lt;br /&gt;
&lt;br /&gt;
==== Validations ====&lt;br /&gt;
* '''Required field validation''': Tests for parent, due_at, and deadline_type_id presence&lt;br /&gt;
* '''Round validation''': Tests for positive values, zero rejection, negative rejection, and nil handling&lt;br /&gt;
* '''Error messages''': Verifies specific error messages are added to appropriate fields&lt;br /&gt;
* '''Valid record creation''': Confirms records persist when all requirements are met&lt;br /&gt;
&lt;br /&gt;
==== Scopes ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;''': Returns future due dates ordered by due_at, excludes past dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;''': Returns past due dates ordered by due_at, excludes future dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;''': Filters by round number, handles non-existent rounds&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt;''': Filters by deadline type name, handles non-existent types&lt;br /&gt;
&lt;br /&gt;
==== Instance Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#overdue?&amp;lt;/code&amp;gt;''': Tests past dates (true), future dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#upcoming?&amp;lt;/code&amp;gt;''': Tests future dates (true), past dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#set&amp;lt;/code&amp;gt;''': Verifies updating deadline_type_id, parent_id, and round with persistence and error handling&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#copy&amp;lt;/code&amp;gt;''': Tests duplication to new assignment, attribute preservation, error handling for non-existent targets&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#deadline_type_name&amp;lt;/code&amp;gt;''': Returns associated deadline type name, handles nil cases&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#last_deadline?&amp;lt;/code&amp;gt;''': Tests detection of final deadline, handles multiple deadlines and single deadline scenarios&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#&amp;lt;=&amp;gt;&amp;lt;/code&amp;gt;''': Tests comparison by due_at time, handles non-DueDate objects&lt;br /&gt;
&lt;br /&gt;
==== Class Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.sort_due_dates&amp;lt;/code&amp;gt;''': Sorts by due_at ascending, handles empty arrays and single elements&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.any_future_due_dates?&amp;lt;/code&amp;gt;''': Detects future dates in collections, handles empty arrays and mixed date collections&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.copy&amp;lt;/code&amp;gt;''': Copies all due dates between assignments, preserves attributes, handles empty sources and non-existent records&lt;br /&gt;
&lt;br /&gt;
==== Callbacks ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;before_save :set_default_round&amp;lt;/code&amp;gt;''': Sets round to 1 when not specified, preserves explicit values, handles nil&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
Tests follow RSpec best practices with nested contexts for different scenarios:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
describe 'validations' do&lt;br /&gt;
  context 'when parent is missing' do&lt;br /&gt;
    it 'is invalid' do&lt;br /&gt;
      expect(due_date).to be_invalid&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'has error on parent field' do&lt;br /&gt;
      due_date.valid?&lt;br /&gt;
      expect(due_date.errors[:parent]).to include('must exist')&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when round validation' do&lt;br /&gt;
    context 'with round value of 0' do&lt;br /&gt;
      it 'is invalid' do&lt;br /&gt;
        expect(due_date).to be_invalid&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'with nil round' do&lt;br /&gt;
      it 'sets default round to 1' do&lt;br /&gt;
        expect(due_date.round).to eq(1)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Test Metrics ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDate model''': 60+ examples covering all methods and edge cases&lt;br /&gt;
* '''Context depth''': 2-3 levels for complex scenarios&lt;br /&gt;
* '''Code coverage''': 100% coverage of DueDate model methods&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
=== Demo Link ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add deadline notification system using the new permission framework&lt;br /&gt;
* Implement deadline templates for common assignment patterns&lt;br /&gt;
* Add API endpoints for mobile app integration&lt;br /&gt;
* Create admin dashboard for monitoring deadline compliance across courses&lt;br /&gt;
* Add automated deadline extension workflows based on configurable rules&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167196</id>
		<title>CSC/ECE 517 Fall 2025 - E2566. Finish DueDates</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167196"/>
		<updated>2025-12-02T04:36:46Z</updated>

		<summary type="html">&lt;p&gt;Skim253: /* DueDate Model Refactoring */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
This project aims to complete the implementation of the DueDate system in Expertiza. The current implementation consists mostly of class methods and lacks proper definition of different deadline types and permission checking functionality. This redesign will create a more robust, readable, and maintainable due date system.&lt;br /&gt;
&lt;br /&gt;
=== Existing Components ===&lt;br /&gt;
* &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model with polymorphic parent association (Assignment/SignUpTopic)&lt;br /&gt;
* &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; subclasses&lt;br /&gt;
* Basic CRUD operations (within &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AssignmentForm&amp;lt;/code&amp;gt;) and sorting functionality (&amp;lt;code&amp;gt;deadline_sort&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Database schema with &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt; field&lt;br /&gt;
&lt;br /&gt;
=== Current Behavior ===&lt;br /&gt;
&lt;br /&gt;
====Admin's View====&lt;br /&gt;
[[File:Duedates.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* When editing an assignment, due dates can be modified under the Due Dates tab.&lt;br /&gt;
* Each row contains the following columns: &amp;lt;code&amp;gt;Date &amp;amp; Time&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Use Date Updater&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Submission Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Teammate Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Meta-Review Allowed&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Reminder&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:Assignments-CurrentStage.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* In the Assignments table, the ''DueDate'' of each assignment is displayed as Current Stage and Stage Deadline.&lt;br /&gt;
&lt;br /&gt;
====Student's View====&lt;br /&gt;
[[File:Student-assignments.png|600px]]&lt;br /&gt;
* Students can view the due date information under &amp;lt;code&amp;gt;Current State&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Stage Deadline&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
* The actions available to students are determined by the current DueDate status.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-not-over.png|600px]]&lt;br /&gt;
* For example, during the submission period, students can access the &amp;lt;code&amp;gt;Submit a hyperlink&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Submit a file&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-over.png|600px]]&lt;br /&gt;
* Once the due date has passed, students cannot submit either a hyperlink or a file.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Currently, there are some glaring issues with how due_dates was created in the first place, including redundancy and lack of modularity.&lt;br /&gt;
&lt;br /&gt;
Modeling &amp;amp; Structural Issues&lt;br /&gt;
# No dedicated &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to define different kinds of deadlines. The existing &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; class only serves as an association for &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; models, and is never referenced in the current &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; implementation.&lt;br /&gt;
# Duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table, which may lead to potential bugs.&lt;br /&gt;
# Incomplete deadline type definitions - &amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt; need to be added.&lt;br /&gt;
# No clear separation of concerns - data persistence, business logic, and permission logic are mixed in a single model.&lt;br /&gt;
&lt;br /&gt;
Permission &amp;amp; Behavior Issues&lt;br /&gt;
# Overuse of class methods making the code difficult to maintain.&lt;br /&gt;
# Missing permission checking logic for user actions.&lt;br /&gt;
&lt;br /&gt;
=== Tasks Identified ===&lt;br /&gt;
&lt;br /&gt;
Modeling &amp;amp; Structural Issues&lt;br /&gt;
# Refactor the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model to separate persistence, business logic, and permission logic into distinct layers.&lt;br /&gt;
# Implement reusable mixins (e.g., &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;) to handle permission evaluation and deadline-related operations.&lt;br /&gt;
# Introduce instance-level methods (e.g., &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;) to replace class-level utilities and improve testability.&lt;br /&gt;
&lt;br /&gt;
Permission &amp;amp; Behavior Issues&lt;br /&gt;
# Integrate role-based and time-based permission checks within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; to ensure consistent access control.&lt;br /&gt;
# Redesign the &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.&lt;br /&gt;
# Add missing deadline types (&amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt;) and remove duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries for data integrity.&lt;br /&gt;
&lt;br /&gt;
== Proposed Solution ==&lt;br /&gt;
The proposed solution restructures the due date system by separating concerns across dedicated models and mixin modules.&lt;br /&gt;
Deadline definitions (&amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt;), core deadline data (&amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;), and behavioral logic (&amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;) are isolated so each component has a single clear responsibility.&lt;br /&gt;
This modular design improves readability, reduces coupling, and makes deadline-related behavior easier to extend and maintain.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Duedate-diagram.png|600px]]&lt;br /&gt;
&lt;br /&gt;
This diagram visualizes the redesigned architecture: blue/red/green indicate Rails models, orange indicates modules. &lt;br /&gt;
Parent models call DueDateActions, which manages DueDate, while DueDatePermissions checks user rights using reference models. &lt;br /&gt;
&lt;br /&gt;
=== DeadlineType Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Although the current codebase includes a &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model, it only serves as a passive lookup table for associations with &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt;.  &lt;br /&gt;
In practice, most parts of the system still rely on hard-coded numeric IDs or manual lookups such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Example of current usage&lt;br /&gt;
deadline_type_id = DeadlineType.find_by(name: 'review').id&lt;br /&gt;
if due_date.deadline_type_id == deadline_type_id&lt;br /&gt;
  # Perform review-related logic&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of using the model as an active domain object, &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; directly compares IDs or string literals.  &lt;br /&gt;
This leads to several structural issues:&lt;br /&gt;
&lt;br /&gt;
* Inconsistent references (some use IDs, others strings)&lt;br /&gt;
* Tight coupling between &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
* Lack of helper semantics (e.g., &amp;lt;code&amp;gt;is_review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;is_submission?&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Data duplication and integrity risks (two &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; rows)&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; will serve as a source of truth, defining all valid deadline types and providing meaningful interfaces.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table defines all canonical deadline categories used by the system.&lt;br /&gt;
Each row represents one distinct deadline type, and every &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; record must reference one of these entries through &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Below is a structural description of the table:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; Table Structure                                                    &lt;br /&gt;
|- &lt;br /&gt;
! Column                                                                                         &lt;br /&gt;
! Type                                                                                           &lt;br /&gt;
! Description                                                                                    &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| id                                                                                               &lt;br /&gt;
| BIGINT (Primary Key)                                                                             &lt;br /&gt;
| Unique identifier for each deadline type; referenced by &amp;lt;code&amp;gt;due_dates.deadline_type_id&amp;lt;/code&amp;gt;. &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| name                                                                                             &lt;br /&gt;
| VARCHAR(255)                                                                                     &lt;br /&gt;
| Symbolic name (e.g., &amp;lt;code&amp;gt;submission&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review&amp;lt;/code&amp;gt;); must be unique.              &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| description                                                                                      &lt;br /&gt;
| TEXT                                                                                             &lt;br /&gt;
| Human-readable explanation of the deadline category.                                             &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| created_at                                                                                       &lt;br /&gt;
| TIMESTAMP                                                                                        &lt;br /&gt;
| Time when the row was created.                                                                   &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| updated_at                                                                                       &lt;br /&gt;
| TIMESTAMP                                                                                        &lt;br /&gt;
| Time when the row was last updated.                                                              &lt;br /&gt;
|}                                                                                                &lt;br /&gt;
&lt;br /&gt;
This schema ensures that:&lt;br /&gt;
&lt;br /&gt;
* Each deadline type has a unique name and description.&lt;br /&gt;
* No duplicate entries can exist (e.g., the prior duplicated &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; was cleaned).&lt;br /&gt;
* All &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; rows reference this master table through &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
* The &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; column acts as the authoritative key used throughout permissions and due-date logic.&lt;br /&gt;
&lt;br /&gt;
Migrated constraints also ensure that &amp;lt;code&amp;gt;due_dates.deadline_type_id&amp;lt;/code&amp;gt; is consistently stored as &amp;lt;code&amp;gt;BIGINT&amp;lt;/code&amp;gt;, allowing a long-term stable keyspace.&lt;br /&gt;
&lt;br /&gt;
==== Deadline Type Definitions ====&lt;br /&gt;
&lt;br /&gt;
Because each row in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; is referenced by &amp;lt;code&amp;gt;DueDate.deadline_type_id&amp;lt;/code&amp;gt;, the following definitions map directly to the schema above.&lt;br /&gt;
&lt;br /&gt;
Each ID corresponds exactly to the &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; field in the table and serves as the system-wide canonical reference for that deadline category.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Canonical Deadline Type Entries                                       &lt;br /&gt;
|-&lt;br /&gt;
! ID                                                                    &lt;br /&gt;
! Name                                                                  &lt;br /&gt;
! Description                                                           &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 1                                                                       &lt;br /&gt;
| submission                                                              &lt;br /&gt;
| Student submission deadlines                                            &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 2                                                                       &lt;br /&gt;
| review                                                                  &lt;br /&gt;
| Peer review deadlines                                                   &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 3                                                                       &lt;br /&gt;
| teammate_review                                                         &lt;br /&gt;
| Team member evaluation deadlines                                        &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 5                                                                       &lt;br /&gt;
| metareview                                                              &lt;br /&gt;
| Meta-review deadlines (kept for backward compatibility)                 &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 6                                                                       &lt;br /&gt;
| drop_topic                                                              &lt;br /&gt;
| Topic drop deadlines                                                    &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 7                                                                       &lt;br /&gt;
| signup                                                                  &lt;br /&gt;
| Topic signup deadlines                                                  &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 8                                                                       &lt;br /&gt;
| team_formation                                                          &lt;br /&gt;
| Team formation deadlines (duplicate entries cleaned; ID 8 is canonical) &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 11                                                                      &lt;br /&gt;
| quiz                                                                    &lt;br /&gt;
| Quiz completion deadlines                                               &lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
These definitions are seeded during migration and act as the source of truth for all deadline-related logic within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;.&lt;br /&gt;
By linking each &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; to one of these entries through &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;, the system standardizes behavior and eliminates inconsistent ID or string comparisons from earlier implementations.&lt;br /&gt;
&lt;br /&gt;
==== DeadlineType Model Implementation ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineType &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
  validates :description, presence: true&lt;br /&gt;
&lt;br /&gt;
  has_many :due_dates, foreign_key: :deadline_type_id, dependent: :restrict_with_exception&lt;br /&gt;
&lt;br /&gt;
  # Semantic helper methods for deadline type identification&lt;br /&gt;
  def submission?&lt;br /&gt;
    name == 'submission'&lt;br /&gt;
  end&lt;br /&gt;
  # same code for review, teammate_review, quiz, team_formation, signup, drop_topic   &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DeadlineRight Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model represents the permission level for a specific action at a given deadline.&lt;br /&gt;
It defines three states: &lt;br /&gt;
* &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt; : Action is allowed without penalty&lt;br /&gt;
* &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt; : Action is allowed but marked as late&lt;br /&gt;
* &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt; : Action is not permitted&lt;br /&gt;
&lt;br /&gt;
These values are referenced by &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; through fields such as &amp;lt;code&amp;gt;submission_allowed_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review_allowed_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz_allowed_id&amp;lt;/code&amp;gt;, and others.&lt;br /&gt;
This allows the system to determine user permissions by combining:&lt;br /&gt;
&lt;br /&gt;
# The current deadline’s allowed state&lt;br /&gt;
# The participant’s role-based ability (submit, review, take quiz)&lt;br /&gt;
&lt;br /&gt;
This combination logic is implemented in &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
The &amp;lt;code&amp;gt;deadline_rights&amp;lt;/code&amp;gt; table stores the canonical permission states used across the entire due-date system.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_rights&amp;lt;/code&amp;gt; Table Structure                                                         &lt;br /&gt;
|-&lt;br /&gt;
! Column                                                                                               &lt;br /&gt;
! Type                                                                                                 &lt;br /&gt;
! Description                                                                                          &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| id                                                                                                     &lt;br /&gt;
| BIGINT (Primary Key)                                                                                  &lt;br /&gt;
| Unique identifier for each permission state. Referenced by &amp;lt;code&amp;gt;due_dates.*_allowed_id&amp;lt;/code&amp;gt; fields. &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| name                                                                                                   &lt;br /&gt;
| VARCHAR(255)                                                                                           &lt;br /&gt;
| Symbolic permission name (&amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;). Must be unique.        &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| description                                                                                            &lt;br /&gt;
| TEXT                                                                                                   &lt;br /&gt;
| Human-readable explanation of what the permission state means.                                         &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| created_at                                                                                             &lt;br /&gt;
| TIMESTAMP                                                                                              &lt;br /&gt;
| Timestamp when the entry was created.                                                                  &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| updated_at                                                                                             &lt;br /&gt;
| TIMESTAMP                                                                                              &lt;br /&gt;
| Timestamp when the entry was last updated.                                                             &lt;br /&gt;
|}  &lt;br /&gt;
&lt;br /&gt;
==== DeadlineRight Model ====&lt;br /&gt;
Instead of embedding permission logic inside &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;, the &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model defines the meaning of each permission state.&lt;br /&gt;
This removes magic strings (&amp;lt;code&amp;gt;&amp;quot;OK&amp;quot;&amp;lt;/code&amp;gt;) and numeric comparisons and replaces them with expressive model-level semantics.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Semantic Meaning Provided by &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt;                    &lt;br /&gt;
|-&lt;br /&gt;
! Concept                                                                    &lt;br /&gt;
! Meaning                                                                    &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| allows_action?                                                               &lt;br /&gt;
| True for &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;; false for &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;.    &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| denies_action?                                                               &lt;br /&gt;
| True only for &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;.                                               &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| allows_with_penalty?                                                         &lt;br /&gt;
| True only for &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;.                                             &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| allows_without_penalty?                                                      &lt;br /&gt;
| True only for &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.                                               &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| permission_level                                                             &lt;br /&gt;
| Numeric ordering: No = 0, Late = 1, OK = 2. Used for comparison and sorting. &lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
These behaviors allow other parts of the system—particularly &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;—to make consistent, readable decisions.&lt;br /&gt;
&lt;br /&gt;
=== DueDate Model Refactoring ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model currently mixes persistence logic, deadline calculation, and permission checking, violating SRP.  &lt;br /&gt;
Most of its logic is implemented as class methods, which makes testing and extension difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Current design&lt;br /&gt;
def self.copy(old_assignment_id, new_assignment_id)&lt;br /&gt;
  duedates = where(parent_id: old_assignment_id)&lt;br /&gt;
  duedates.each do |orig_due_date|&lt;br /&gt;
    new_due_date = orig_due_date.dup&lt;br /&gt;
    new_due_date.parent_id = new_assignment_id&lt;br /&gt;
    new_due_date.save&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Improved Design ====&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model is simpler, more modular, and focused on representing a single deadline entry.&lt;br /&gt;
Behavior previously embedded inside &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; has been extracted into appropriate modules:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Responsibility Distribution After Refactoring                        &lt;br /&gt;
|-&lt;br /&gt;
! Concern                                                              &lt;br /&gt;
! Where It Lives Now                                                   &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Permission checking                                                    &lt;br /&gt;
| &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module                                 &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Deadline-related actions (next deadline, stage name, deadline copying) &lt;br /&gt;
| &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module (included in parent models)         &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Deadline type semantics                                                &lt;br /&gt;
| &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model                                        &lt;br /&gt;
|-                                                                      &lt;br /&gt;
| Core deadline data (due_at, round, parent)                             &lt;br /&gt;
| &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model                                             &lt;br /&gt;
|}   &lt;br /&gt;
&lt;br /&gt;
===== Instance-Based Behavior Instead of Class Methods =====&lt;br /&gt;
&lt;br /&gt;
Previously, many behaviors were class methods (e.g., copying deadlines).&lt;br /&gt;
These methods did not reflect object-level behavior and often duplicated logic.&lt;br /&gt;
&lt;br /&gt;
The redesign moves these operations to:&lt;br /&gt;
&lt;br /&gt;
* instance methods for actions on a specific &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;&lt;br /&gt;
* modules (DueDateActions) for higher-level behaviors on assignments or topics&lt;br /&gt;
&lt;br /&gt;
This separation:&lt;br /&gt;
&lt;br /&gt;
* improves testability&lt;br /&gt;
* aligns with Rails conventions&lt;br /&gt;
* avoids state duplication&lt;br /&gt;
&lt;br /&gt;
===== Clearer Query Methods =====&lt;br /&gt;
&lt;br /&gt;
The model now exposes simple, intuitive instance-level questions:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Examples of Simplified State Queries    &lt;br /&gt;
|-&lt;br /&gt;
! Method                                  &lt;br /&gt;
! Meaning                                 &lt;br /&gt;
|-                                         &lt;br /&gt;
| &amp;lt;code&amp;gt;upcoming?&amp;lt;/code&amp;gt;                    &lt;br /&gt;
| Whether the deadline occurs in the future &lt;br /&gt;
|-                                         &lt;br /&gt;
| &amp;lt;code&amp;gt;overdue?&amp;lt;/code&amp;gt;                     &lt;br /&gt;
| Whether the deadline has already passed   &lt;br /&gt;
|-                                         &lt;br /&gt;
| &amp;lt;code&amp;gt;deadline_type_name&amp;lt;/code&amp;gt;           &lt;br /&gt;
| Human-readable deadline type              &lt;br /&gt;
|}                                         &lt;br /&gt;
&lt;br /&gt;
These small, composable predicates replace multiple ad-hoc comparisons scattered across the codebase.&lt;br /&gt;
&lt;br /&gt;
=== Permission-Checking Methods ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Currently, permission checks depend only on user roles and ignore the actual deadline.  &lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Role-based permissions (participant_helpers.rb)&lt;br /&gt;
def participant_permissions(authorization)&lt;br /&gt;
  case authorization&lt;br /&gt;
  when 'reader'   then { can_submit: false, can_review: true, can_take_quiz: true }&lt;br /&gt;
  when 'reviewer' then { can_submit: false, can_review: true, can_take_quiz: false }&lt;br /&gt;
  when 'submitter' then { can_submit: true, can_review: false, can_take_quiz: false }&lt;br /&gt;
  else { can_submit: true, can_review: true, can_take_quiz: true }&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And deadline-based checks exist separately in the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def submission_allowed(topic_id = nil)&lt;br /&gt;
  check_condition('submission_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
def can_review(topic_id = nil)&lt;br /&gt;
  check_condition('review_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These two layers (role-based and time-based) never interact, leading to inconsistencies (e.g., a user with &amp;lt;code&amp;gt;can_submit=true&amp;lt;/code&amp;gt; after deadline).&lt;br /&gt;
&lt;br /&gt;
==== Proposed Changes ====&lt;br /&gt;
&lt;br /&gt;
The new &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module integrates role-based and deadline-based checks:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDatePermissions&lt;br /&gt;
  # Permission checking methods that combine deadline-based and role-based logic&lt;br /&gt;
  #&lt;br /&gt;
  # These methods must check both:&lt;br /&gt;
  # 1. Whether the action is permitted by the current deadline (submission_allowed_id, review_allowed_id, etc.)&lt;br /&gt;
  # 2. Whether the participant has the necessary permissions (can_submit, can_review, can_take_quiz fields)&lt;br /&gt;
&lt;br /&gt;
  # Check if submission is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_submit?(participant = nil)&lt;br /&gt;
    return false unless submission_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_submit&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: submission_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if review is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_review?(participant = nil)&lt;br /&gt;
    return false unless review_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_review&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: review_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if taking a quiz is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_take_quiz?(participant = nil)&lt;br /&gt;
    return false unless quiz_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_take_quiz&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: quiz_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if teammate review is allowed&lt;br /&gt;
  def can_review_teammate?(participant = nil)&lt;br /&gt;
    return false unless teammate_review_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_review&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: teammate_review_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Generic permission checker&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    permission_field = &amp;quot;#{activity}_allowed_id&amp;quot;&lt;br /&gt;
    return false unless respond_to?(permission_field)&lt;br /&gt;
&lt;br /&gt;
    allowed_id = public_send(permission_field)&lt;br /&gt;
    return false unless allowed_id&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get permission status for an action (OK, Late, No)&lt;br /&gt;
  def permission_status_for(action)&lt;br /&gt;
    permission_field = &amp;quot;#{action}_allowed_id&amp;quot;&lt;br /&gt;
    return 'No' unless respond_to?(permission_field)&lt;br /&gt;
&lt;br /&gt;
    allowed_id = public_send(permission_field)&lt;br /&gt;
    return 'No' unless allowed_id&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name || 'No'&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This module is included in the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== DueDateActions Mixin ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module provides deadline-related operations for models that have due dates (Assignment, SignUpTopic).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDateActions&lt;br /&gt;
  # Generic activity permission checker that determines if an activity is permissible&lt;br /&gt;
  # based on the current deadline state for this parent object&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    current_deadline = next_due_date()&lt;br /&gt;
    return false unless current_deadline&lt;br /&gt;
&lt;br /&gt;
    current_deadline.activity_permissible?(activity)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Syntactic sugar methods for common activities&lt;br /&gt;
  def submission_permissible?&lt;br /&gt;
    activity_permissible?(:submission)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def review_permissible?&lt;br /&gt;
    activity_permissible?(:review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def teammate_review_permissible?&lt;br /&gt;
    activity_permissible?(:teammate_review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def quiz_permissible?&lt;br /&gt;
    activity_permissible?(:quiz)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def team_formation_permissible?&lt;br /&gt;
    activity_permissible?(:team_formation)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def signup_permissible?&lt;br /&gt;
    activity_permissible?(:signup)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def drop_topic_permissible?&lt;br /&gt;
    activity_permissible?(:drop_topic)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the next due date for this assignment&lt;br /&gt;
  def next_due_date(topic_id = nil)&lt;br /&gt;
    # If a topic is specified and this assignment has topic-specific deadlines,&lt;br /&gt;
    # look for topic due dates first&lt;br /&gt;
    if topic_id &amp;amp;&amp;amp; has_topic_specific_deadlines?&lt;br /&gt;
      topic_deadline = due_dates.where(parent_id: topic_id, parent_type: 'SignUpTopic')&lt;br /&gt;
                               .upcoming&lt;br /&gt;
                               .first&lt;br /&gt;
      return topic_deadline if topic_deadline&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Fall back to assignment-level due dates&lt;br /&gt;
    due_dates.upcoming.first&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the current stage name for display purposes&lt;br /&gt;
  def current_stage&lt;br /&gt;
    deadline = next_due_date()&lt;br /&gt;
    return 'finished' unless deadline&lt;br /&gt;
&lt;br /&gt;
    deadline.deadline_type&amp;amp;.name || 'unknown'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if assignment has topic-specific deadlines&lt;br /&gt;
  def has_topic_specific_deadlines?&lt;br /&gt;
    staggered_deadline || due_dates.where(parent_type: 'SignUpTopic').exists?&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Copy due dates to a new parent object&lt;br /&gt;
  def copy_due_dates_to(new_parent)&lt;br /&gt;
    due_dates.find_each do |due_date|&lt;br /&gt;
      new_due_date = due_date.dup&lt;br /&gt;
      new_due_date.parent = new_parent&lt;br /&gt;
      new_due_date.save!&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This module is included in &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class Assignment &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
class SignUpTopic &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Implementation Summary ==&lt;br /&gt;
&lt;br /&gt;
=== Models Created/Modified ===&lt;br /&gt;
&lt;br /&gt;
* '''DeadlineType''' - Canonical deadline type definitions with semantic helper methods&lt;br /&gt;
* '''DeadlineRight''' - Permission state model (OK/Late/No) with comparison methods&lt;br /&gt;
* '''DueDate''' - Refactored with instance methods, scopes, and permission checking&lt;br /&gt;
* '''TopicDueDate''' - STI subclass for topic-specific deadlines&lt;br /&gt;
* '''Assignment''' - Includes DueDateActions mixin&lt;br /&gt;
* '''SignUpTopic''' - Includes DueDateActions mixin&lt;br /&gt;
&lt;br /&gt;
=== Modules Created ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions''' - Permission checking combining deadline and role constraints&lt;br /&gt;
* '''DueDateActions''' - Deadline-related operations for parent models (Assignment, SignUpTopic)&lt;br /&gt;
&lt;br /&gt;
=== Key Improvements ===&lt;br /&gt;
&lt;br /&gt;
# '''Separation of Concerns''': Permission logic separated into dedicated modules&lt;br /&gt;
# '''Instance Methods''': Replaced class methods with testable instance methods&lt;br /&gt;
# '''Polymorphic Design''': Single DueDate model serves both Assignment and SignUpTopic&lt;br /&gt;
# '''Unified Permissions''': Role-based and time-based checks combined in one interface&lt;br /&gt;
# '''Data Integrity''': Removed duplicate deadline types, enforced foreign key constraints&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
The test suite includes comprehensive RSpec tests for the ''DueDate'' model and its associated models (''DeadlineType'', ''DeadlineRight'').&lt;br /&gt;
These tests verify model validations, scopes, instance methods, class methods, and callbacks with extensive coverage of edge cases and error conditions.&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
The test suite follows RSpec best practices with:&lt;br /&gt;
* '''Nested contexts''' for different scenarios (e.g., &amp;quot;when parent is missing&amp;quot;, &amp;quot;when round is 0&amp;quot;)&lt;br /&gt;
* '''Multiple assertions per context''' to verify behavior from different angles&lt;br /&gt;
* '''Edge case coverage''' including empty collections, nil values, and boundary conditions&lt;br /&gt;
* '''Error case testing''' for invalid data and non-existent records&lt;br /&gt;
* '''Clear descriptive names''' using &amp;quot;context 'when...'&amp;quot; and &amp;quot;it 'does something'&amp;quot; patterns&lt;br /&gt;
&lt;br /&gt;
=== Current Coverage ===&lt;br /&gt;
&lt;br /&gt;
==== Associations ====&lt;br /&gt;
* Tests verify &amp;lt;code&amp;gt;belongs_to :parent&amp;lt;/code&amp;gt; (polymorphic) and &amp;lt;code&amp;gt;belongs_to :deadline_type&amp;lt;/code&amp;gt; relationships&lt;br /&gt;
&lt;br /&gt;
==== Validations ====&lt;br /&gt;
* '''Required field validation''': Tests for parent, due_at, and deadline_type_id presence&lt;br /&gt;
* '''Round validation''': Tests for positive values, zero rejection, negative rejection, and nil handling&lt;br /&gt;
* '''Error messages''': Verifies specific error messages are added to appropriate fields&lt;br /&gt;
* '''Valid record creation''': Confirms records persist when all requirements are met&lt;br /&gt;
&lt;br /&gt;
==== Scopes ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;''': Returns future due dates ordered by due_at, excludes past dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;''': Returns past due dates ordered by due_at, excludes future dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;''': Filters by round number, handles non-existent rounds&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt;''': Filters by deadline type name, handles non-existent types&lt;br /&gt;
&lt;br /&gt;
==== Instance Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#overdue?&amp;lt;/code&amp;gt;''': Tests past dates (true), future dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#upcoming?&amp;lt;/code&amp;gt;''': Tests future dates (true), past dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#set&amp;lt;/code&amp;gt;''': Verifies updating deadline_type_id, parent_id, and round with persistence and error handling&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#copy&amp;lt;/code&amp;gt;''': Tests duplication to new assignment, attribute preservation, error handling for non-existent targets&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#deadline_type_name&amp;lt;/code&amp;gt;''': Returns associated deadline type name, handles nil cases&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#last_deadline?&amp;lt;/code&amp;gt;''': Tests detection of final deadline, handles multiple deadlines and single deadline scenarios&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#&amp;lt;=&amp;gt;&amp;lt;/code&amp;gt;''': Tests comparison by due_at time, handles non-DueDate objects&lt;br /&gt;
&lt;br /&gt;
==== Class Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.sort_due_dates&amp;lt;/code&amp;gt;''': Sorts by due_at ascending, handles empty arrays and single elements&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.any_future_due_dates?&amp;lt;/code&amp;gt;''': Detects future dates in collections, handles empty arrays and mixed date collections&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.copy&amp;lt;/code&amp;gt;''': Copies all due dates between assignments, preserves attributes, handles empty sources and non-existent records&lt;br /&gt;
&lt;br /&gt;
==== Callbacks ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;before_save :set_default_round&amp;lt;/code&amp;gt;''': Sets round to 1 when not specified, preserves explicit values, handles nil&lt;br /&gt;
&lt;br /&gt;
=== Test Examples ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Validation with multiple assertions&lt;br /&gt;
describe 'validations' do&lt;br /&gt;
  context 'when parent is missing' do&lt;br /&gt;
    let(:due_date) do&lt;br /&gt;
      DueDate.new(&lt;br /&gt;
        parent: nil,&lt;br /&gt;
        due_at: 2.days.from_now,&lt;br /&gt;
        deadline_type: submission_type,&lt;br /&gt;
        submission_allowed_id: ok_right.id,&lt;br /&gt;
        review_allowed_id: ok_right.id&lt;br /&gt;
      )&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'is invalid' do&lt;br /&gt;
      expect(due_date).to be_invalid&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'has error on parent field' do&lt;br /&gt;
      due_date.valid?&lt;br /&gt;
      expect(due_date.errors[:parent]).to include('must exist')&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when round validation' do&lt;br /&gt;
    context 'with round value of 0' do&lt;br /&gt;
      let(:due_date) do&lt;br /&gt;
        DueDate.new(&lt;br /&gt;
          parent: assignment,&lt;br /&gt;
          due_at: 2.days.from_now,&lt;br /&gt;
          deadline_type: submission_type,&lt;br /&gt;
          submission_allowed_id: ok_right.id,&lt;br /&gt;
          review_allowed_id: ok_right.id,&lt;br /&gt;
          round: 0&lt;br /&gt;
        )&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'is invalid' do&lt;br /&gt;
        expect(due_date).to be_invalid&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'has error on round field' do&lt;br /&gt;
        due_date.valid?&lt;br /&gt;
        expect(due_date.errors[:round]).to be_present&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'with nil round' do&lt;br /&gt;
      let(:due_date) do&lt;br /&gt;
        DueDate.create(&lt;br /&gt;
          parent: assignment,&lt;br /&gt;
          due_at: 2.days.from_now,&lt;br /&gt;
          deadline_type: submission_type,&lt;br /&gt;
          submission_allowed_id: ok_right.id,&lt;br /&gt;
          review_allowed_id: ok_right.id,&lt;br /&gt;
          round: nil&lt;br /&gt;
        )&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'is valid' do&lt;br /&gt;
        expect(due_date).to be_valid&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'sets default round to 1' do&lt;br /&gt;
        expect(due_date.round).to eq(1)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
# Scope with edge cases&lt;br /&gt;
describe '.upcoming' do&lt;br /&gt;
  it 'returns only future due dates' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).to contain_exactly(upcoming_due_date1, upcoming_due_date2)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'orders results by due_at ascending' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).to eq([upcoming_due_date1, upcoming_due_date2])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'excludes past due dates' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).not_to include(past_due_date)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when all due dates are in the past' do&lt;br /&gt;
    before do&lt;br /&gt;
      DueDate.destroy_all&lt;br /&gt;
      DueDate.create(&lt;br /&gt;
        parent: assignment,&lt;br /&gt;
        due_at: 3.days.ago,&lt;br /&gt;
        deadline_type: submission_type,&lt;br /&gt;
        submission_allowed_id: ok_right.id,&lt;br /&gt;
        review_allowed_id: ok_right.id&lt;br /&gt;
      )&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns empty collection' do&lt;br /&gt;
      expect(DueDate.upcoming).to be_empty&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
# Instance method with error handling&lt;br /&gt;
describe '#copy' do&lt;br /&gt;
  let(:original) do&lt;br /&gt;
    DueDate.create(&lt;br /&gt;
      parent: assignment,&lt;br /&gt;
      due_at: 2.days.from_now,&lt;br /&gt;
      deadline_type: submission_type,&lt;br /&gt;
      submission_allowed_id: ok_right.id,&lt;br /&gt;
      review_allowed_id: late_right.id,&lt;br /&gt;
      round: 1&lt;br /&gt;
    )&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  let(:copied) { original.copy(assignment2.id) }&lt;br /&gt;
&lt;br /&gt;
  it 'creates a new persisted record' do&lt;br /&gt;
    expect(copied).to be_persisted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'has different id from original' do&lt;br /&gt;
    expect(copied.id).not_to eq(original.id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'preserves all attributes' do&lt;br /&gt;
    expect(copied.due_at).to eq(original.due_at)&lt;br /&gt;
    expect(copied.deadline_type_id).to eq(original.deadline_type_id)&lt;br /&gt;
    expect(copied.round).to eq(original.round)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when copying to non-existent assignment' do&lt;br /&gt;
    it 'raises error' do&lt;br /&gt;
      expect do&lt;br /&gt;
        original.copy(99999)&lt;br /&gt;
      end.to raise_error(ActiveRecord::RecordNotFound)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Test Metrics ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDate model''': 60+ examples covering all methods and edge cases&lt;br /&gt;
* '''Context depth''': 2-3 levels for complex scenarios&lt;br /&gt;
* '''Code coverage''': 100% coverage of DueDate model methods&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
=== Demo Link ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add deadline notification system using the new permission framework&lt;br /&gt;
* Implement deadline templates for common assignment patterns&lt;br /&gt;
* Add API endpoints for mobile app integration&lt;br /&gt;
* Create admin dashboard for monitoring deadline compliance across courses&lt;br /&gt;
* Add automated deadline extension workflows based on configurable rules&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167195</id>
		<title>CSC/ECE 517 Fall 2025 - E2566. Finish DueDates</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167195"/>
		<updated>2025-12-02T04:30:44Z</updated>

		<summary type="html">&lt;p&gt;Skim253: /* DeadlineRight Model Implementation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
This project aims to complete the implementation of the DueDate system in Expertiza. The current implementation consists mostly of class methods and lacks proper definition of different deadline types and permission checking functionality. This redesign will create a more robust, readable, and maintainable due date system.&lt;br /&gt;
&lt;br /&gt;
=== Existing Components ===&lt;br /&gt;
* &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model with polymorphic parent association (Assignment/SignUpTopic)&lt;br /&gt;
* &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; subclasses&lt;br /&gt;
* Basic CRUD operations (within &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AssignmentForm&amp;lt;/code&amp;gt;) and sorting functionality (&amp;lt;code&amp;gt;deadline_sort&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Database schema with &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt; field&lt;br /&gt;
&lt;br /&gt;
=== Current Behavior ===&lt;br /&gt;
&lt;br /&gt;
====Admin's View====&lt;br /&gt;
[[File:Duedates.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* When editing an assignment, due dates can be modified under the Due Dates tab.&lt;br /&gt;
* Each row contains the following columns: &amp;lt;code&amp;gt;Date &amp;amp; Time&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Use Date Updater&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Submission Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Teammate Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Meta-Review Allowed&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Reminder&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:Assignments-CurrentStage.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* In the Assignments table, the ''DueDate'' of each assignment is displayed as Current Stage and Stage Deadline.&lt;br /&gt;
&lt;br /&gt;
====Student's View====&lt;br /&gt;
[[File:Student-assignments.png|600px]]&lt;br /&gt;
* Students can view the due date information under &amp;lt;code&amp;gt;Current State&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Stage Deadline&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
* The actions available to students are determined by the current DueDate status.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-not-over.png|600px]]&lt;br /&gt;
* For example, during the submission period, students can access the &amp;lt;code&amp;gt;Submit a hyperlink&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Submit a file&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-over.png|600px]]&lt;br /&gt;
* Once the due date has passed, students cannot submit either a hyperlink or a file.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Currently, there are some glaring issues with how due_dates was created in the first place, including redundancy and lack of modularity.&lt;br /&gt;
&lt;br /&gt;
Modeling &amp;amp; Structural Issues&lt;br /&gt;
# No dedicated &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to define different kinds of deadlines. The existing &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; class only serves as an association for &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; models, and is never referenced in the current &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; implementation.&lt;br /&gt;
# Duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table, which may lead to potential bugs.&lt;br /&gt;
# Incomplete deadline type definitions - &amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt; need to be added.&lt;br /&gt;
# No clear separation of concerns - data persistence, business logic, and permission logic are mixed in a single model.&lt;br /&gt;
&lt;br /&gt;
Permission &amp;amp; Behavior Issues&lt;br /&gt;
# Overuse of class methods making the code difficult to maintain.&lt;br /&gt;
# Missing permission checking logic for user actions.&lt;br /&gt;
&lt;br /&gt;
=== Tasks Identified ===&lt;br /&gt;
&lt;br /&gt;
Modeling &amp;amp; Structural Issues&lt;br /&gt;
# Refactor the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model to separate persistence, business logic, and permission logic into distinct layers.&lt;br /&gt;
# Implement reusable mixins (e.g., &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;) to handle permission evaluation and deadline-related operations.&lt;br /&gt;
# Introduce instance-level methods (e.g., &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;) to replace class-level utilities and improve testability.&lt;br /&gt;
&lt;br /&gt;
Permission &amp;amp; Behavior Issues&lt;br /&gt;
# Integrate role-based and time-based permission checks within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; to ensure consistent access control.&lt;br /&gt;
# Redesign the &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.&lt;br /&gt;
# Add missing deadline types (&amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt;) and remove duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries for data integrity.&lt;br /&gt;
&lt;br /&gt;
== Proposed Solution ==&lt;br /&gt;
The proposed solution restructures the due date system by separating concerns across dedicated models and mixin modules.&lt;br /&gt;
Deadline definitions (&amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt;), core deadline data (&amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;), and behavioral logic (&amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;) are isolated so each component has a single clear responsibility.&lt;br /&gt;
This modular design improves readability, reduces coupling, and makes deadline-related behavior easier to extend and maintain.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Duedate-diagram.png|600px]]&lt;br /&gt;
&lt;br /&gt;
This diagram visualizes the redesigned architecture: blue/red/green indicate Rails models, orange indicates modules. &lt;br /&gt;
Parent models call DueDateActions, which manages DueDate, while DueDatePermissions checks user rights using reference models. &lt;br /&gt;
&lt;br /&gt;
=== DeadlineType Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Although the current codebase includes a &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model, it only serves as a passive lookup table for associations with &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt;.  &lt;br /&gt;
In practice, most parts of the system still rely on hard-coded numeric IDs or manual lookups such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Example of current usage&lt;br /&gt;
deadline_type_id = DeadlineType.find_by(name: 'review').id&lt;br /&gt;
if due_date.deadline_type_id == deadline_type_id&lt;br /&gt;
  # Perform review-related logic&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of using the model as an active domain object, &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; directly compares IDs or string literals.  &lt;br /&gt;
This leads to several structural issues:&lt;br /&gt;
&lt;br /&gt;
* Inconsistent references (some use IDs, others strings)&lt;br /&gt;
* Tight coupling between &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
* Lack of helper semantics (e.g., &amp;lt;code&amp;gt;is_review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;is_submission?&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Data duplication and integrity risks (two &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; rows)&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; will serve as a source of truth, defining all valid deadline types and providing meaningful interfaces.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table defines all canonical deadline categories used by the system.&lt;br /&gt;
Each row represents one distinct deadline type, and every &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; record must reference one of these entries through &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Below is a structural description of the table:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; Table Structure                                                    &lt;br /&gt;
|- &lt;br /&gt;
! Column                                                                                         &lt;br /&gt;
! Type                                                                                           &lt;br /&gt;
! Description                                                                                    &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| id                                                                                               &lt;br /&gt;
| BIGINT (Primary Key)                                                                             &lt;br /&gt;
| Unique identifier for each deadline type; referenced by &amp;lt;code&amp;gt;due_dates.deadline_type_id&amp;lt;/code&amp;gt;. &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| name                                                                                             &lt;br /&gt;
| VARCHAR(255)                                                                                     &lt;br /&gt;
| Symbolic name (e.g., &amp;lt;code&amp;gt;submission&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review&amp;lt;/code&amp;gt;); must be unique.              &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| description                                                                                      &lt;br /&gt;
| TEXT                                                                                             &lt;br /&gt;
| Human-readable explanation of the deadline category.                                             &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| created_at                                                                                       &lt;br /&gt;
| TIMESTAMP                                                                                        &lt;br /&gt;
| Time when the row was created.                                                                   &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| updated_at                                                                                       &lt;br /&gt;
| TIMESTAMP                                                                                        &lt;br /&gt;
| Time when the row was last updated.                                                              &lt;br /&gt;
|}                                                                                                &lt;br /&gt;
&lt;br /&gt;
This schema ensures that:&lt;br /&gt;
&lt;br /&gt;
* Each deadline type has a unique name and description.&lt;br /&gt;
* No duplicate entries can exist (e.g., the prior duplicated &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; was cleaned).&lt;br /&gt;
* All &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; rows reference this master table through &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
* The &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; column acts as the authoritative key used throughout permissions and due-date logic.&lt;br /&gt;
&lt;br /&gt;
Migrated constraints also ensure that &amp;lt;code&amp;gt;due_dates.deadline_type_id&amp;lt;/code&amp;gt; is consistently stored as &amp;lt;code&amp;gt;BIGINT&amp;lt;/code&amp;gt;, allowing a long-term stable keyspace.&lt;br /&gt;
&lt;br /&gt;
==== Deadline Type Definitions ====&lt;br /&gt;
&lt;br /&gt;
Because each row in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; is referenced by &amp;lt;code&amp;gt;DueDate.deadline_type_id&amp;lt;/code&amp;gt;, the following definitions map directly to the schema above.&lt;br /&gt;
&lt;br /&gt;
Each ID corresponds exactly to the &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; field in the table and serves as the system-wide canonical reference for that deadline category.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Canonical Deadline Type Entries                                       &lt;br /&gt;
|-&lt;br /&gt;
! ID                                                                    &lt;br /&gt;
! Name                                                                  &lt;br /&gt;
! Description                                                           &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 1                                                                       &lt;br /&gt;
| submission                                                              &lt;br /&gt;
| Student submission deadlines                                            &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 2                                                                       &lt;br /&gt;
| review                                                                  &lt;br /&gt;
| Peer review deadlines                                                   &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 3                                                                       &lt;br /&gt;
| teammate_review                                                         &lt;br /&gt;
| Team member evaluation deadlines                                        &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 5                                                                       &lt;br /&gt;
| metareview                                                              &lt;br /&gt;
| Meta-review deadlines (kept for backward compatibility)                 &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 6                                                                       &lt;br /&gt;
| drop_topic                                                              &lt;br /&gt;
| Topic drop deadlines                                                    &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 7                                                                       &lt;br /&gt;
| signup                                                                  &lt;br /&gt;
| Topic signup deadlines                                                  &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 8                                                                       &lt;br /&gt;
| team_formation                                                          &lt;br /&gt;
| Team formation deadlines (duplicate entries cleaned; ID 8 is canonical) &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 11                                                                      &lt;br /&gt;
| quiz                                                                    &lt;br /&gt;
| Quiz completion deadlines                                               &lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
These definitions are seeded during migration and act as the source of truth for all deadline-related logic within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;.&lt;br /&gt;
By linking each &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; to one of these entries through &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;, the system standardizes behavior and eliminates inconsistent ID or string comparisons from earlier implementations.&lt;br /&gt;
&lt;br /&gt;
==== DeadlineType Model Implementation ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineType &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
  validates :description, presence: true&lt;br /&gt;
&lt;br /&gt;
  has_many :due_dates, foreign_key: :deadline_type_id, dependent: :restrict_with_exception&lt;br /&gt;
&lt;br /&gt;
  # Semantic helper methods for deadline type identification&lt;br /&gt;
  def submission?&lt;br /&gt;
    name == 'submission'&lt;br /&gt;
  end&lt;br /&gt;
  # same code for review, teammate_review, quiz, team_formation, signup, drop_topic   &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DeadlineRight Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model represents the permission level for a specific action at a given deadline.&lt;br /&gt;
It defines three states: &lt;br /&gt;
* &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt; : Action is allowed without penalty&lt;br /&gt;
* &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt; : Action is allowed but marked as late&lt;br /&gt;
* &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt; : Action is not permitted&lt;br /&gt;
&lt;br /&gt;
These values are referenced by &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; through fields such as &amp;lt;code&amp;gt;submission_allowed_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review_allowed_id&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz_allowed_id&amp;lt;/code&amp;gt;, and others.&lt;br /&gt;
This allows the system to determine user permissions by combining:&lt;br /&gt;
&lt;br /&gt;
# The current deadline’s allowed state&lt;br /&gt;
# The participant’s role-based ability (submit, review, take quiz)&lt;br /&gt;
&lt;br /&gt;
This combination logic is implemented in &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
The &amp;lt;code&amp;gt;deadline_rights&amp;lt;/code&amp;gt; table stores the canonical permission states used across the entire due-date system.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_rights&amp;lt;/code&amp;gt; Table Structure                                                         &lt;br /&gt;
|-&lt;br /&gt;
! Column                                                                                               &lt;br /&gt;
! Type                                                                                                 &lt;br /&gt;
! Description                                                                                          &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| id                                                                                                     &lt;br /&gt;
| BIGINT (Primary Key)                                                                                  &lt;br /&gt;
| Unique identifier for each permission state. Referenced by &amp;lt;code&amp;gt;due_dates.*_allowed_id&amp;lt;/code&amp;gt; fields. &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| name                                                                                                   &lt;br /&gt;
| VARCHAR(255)                                                                                           &lt;br /&gt;
| Symbolic permission name (&amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;). Must be unique.        &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| description                                                                                            &lt;br /&gt;
| TEXT                                                                                                   &lt;br /&gt;
| Human-readable explanation of what the permission state means.                                         &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| created_at                                                                                             &lt;br /&gt;
| TIMESTAMP                                                                                              &lt;br /&gt;
| Timestamp when the entry was created.                                                                  &lt;br /&gt;
|-                                                                                                      &lt;br /&gt;
| updated_at                                                                                             &lt;br /&gt;
| TIMESTAMP                                                                                              &lt;br /&gt;
| Timestamp when the entry was last updated.                                                             &lt;br /&gt;
|}  &lt;br /&gt;
&lt;br /&gt;
==== DeadlineRight Model ====&lt;br /&gt;
Instead of embedding permission logic inside &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;, the &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model defines the meaning of each permission state.&lt;br /&gt;
This removes magic strings (&amp;lt;code&amp;gt;&amp;quot;OK&amp;quot;&amp;lt;/code&amp;gt;) and numeric comparisons and replaces them with expressive model-level semantics.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Semantic Meaning Provided by &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt;                    &lt;br /&gt;
|-&lt;br /&gt;
! Concept                                                                    &lt;br /&gt;
! Meaning                                                                    &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| allows_action?                                                               &lt;br /&gt;
| True for &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt; or &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;; false for &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;.    &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| denies_action?                                                               &lt;br /&gt;
| True only for &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt;.                                               &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| allows_with_penalty?                                                         &lt;br /&gt;
| True only for &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt;.                                             &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| allows_without_penalty?                                                      &lt;br /&gt;
| True only for &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt;.                                               &lt;br /&gt;
|-                                                                            &lt;br /&gt;
| permission_level                                                             &lt;br /&gt;
| Numeric ordering: No = 0, Late = 1, OK = 2. Used for comparison and sorting. &lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
These behaviors allow other parts of the system—particularly &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;—to make consistent, readable decisions.&lt;br /&gt;
&lt;br /&gt;
=== DueDate Model Refactoring ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model currently mixes persistence logic, deadline calculation, and permission checking, violating SRP.  &lt;br /&gt;
Most of its logic is implemented as class methods, which makes testing and extension difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Current design&lt;br /&gt;
def self.copy(old_assignment_id, new_assignment_id)&lt;br /&gt;
  duedates = where(parent_id: old_assignment_id)&lt;br /&gt;
  duedates.each do |orig_due_date|&lt;br /&gt;
    new_due_date = orig_due_date.dup&lt;br /&gt;
    new_due_date.parent_id = new_assignment_id&lt;br /&gt;
    new_due_date.save&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Improved Design ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
&lt;br /&gt;
  belongs_to :parent, polymorphic: true&lt;br /&gt;
  belongs_to :deadline_type, foreign_key: :deadline_type_id&lt;br /&gt;
&lt;br /&gt;
  validates :due_at, presence: true&lt;br /&gt;
  validates :deadline_type_id, presence: true&lt;br /&gt;
  validates :parent, presence: true&lt;br /&gt;
  validates :round, numericality: { greater_than: 0 }, allow_nil: true&lt;br /&gt;
&lt;br /&gt;
  # Scopes for common queries&lt;br /&gt;
  scope :upcoming, -&amp;gt; { where('due_at &amp;gt; ?', Time.current).order(:due_at) }&lt;br /&gt;
  scope :overdue, -&amp;gt; { where('due_at &amp;lt; ?', Time.current).order(:due_at) }&lt;br /&gt;
  scope :for_round, -&amp;gt;(round_num) { where(round: round_num) }&lt;br /&gt;
  scope :for_deadline_type, -&amp;gt;(type_name) { joins(:deadline_type).where(deadline_types: { name: type_name }) }&lt;br /&gt;
&lt;br /&gt;
  # Instance methods replace class methods&lt;br /&gt;
  def copy(to_assignment_id)&lt;br /&gt;
    to_assignment = Assignment.find(to_assignment_id)&lt;br /&gt;
    new_due_date = dup&lt;br /&gt;
    new_due_date.parent = to_assignment&lt;br /&gt;
    new_due_date.save!&lt;br /&gt;
    new_due_date&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def set(deadline_type_id, parent_id, round)&lt;br /&gt;
    self.deadline_type_id = deadline_type_id&lt;br /&gt;
    self.parent_id = parent_id&lt;br /&gt;
    self.round = round&lt;br /&gt;
    save!&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if this deadline has passed&lt;br /&gt;
  def overdue?&lt;br /&gt;
    due_at &amp;lt; Time.current&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if this deadline is upcoming&lt;br /&gt;
  def upcoming?&lt;br /&gt;
    due_at &amp;gt; Time.current&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the deadline type name&lt;br /&gt;
  def deadline_type_name&lt;br /&gt;
    deadline_type&amp;amp;.name&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Comparison method for sorting&lt;br /&gt;
  def &amp;lt;=&amp;gt;(other)&lt;br /&gt;
    return nil unless other.is_a?(DueDate)&lt;br /&gt;
    due_at &amp;lt;=&amp;gt; other.due_at&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Class methods for collection operations&lt;br /&gt;
  class &amp;lt;&amp;lt; self&lt;br /&gt;
    def sort_due_dates(due_dates)&lt;br /&gt;
      due_dates.sort_by(&amp;amp;:due_at)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    def any_future_due_dates?(due_dates)&lt;br /&gt;
      due_dates.any?(&amp;amp;:upcoming?)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    def copy(from_assignment_id, to_assignment_id)&lt;br /&gt;
      from_assignment = Assignment.find(from_assignment_id)&lt;br /&gt;
      to_assignment = Assignment.find(to_assignment_id)&lt;br /&gt;
&lt;br /&gt;
      from_assignment.due_dates.each do |due_date|&lt;br /&gt;
        new_due_date = due_date.dup&lt;br /&gt;
        new_due_date.parent = to_assignment&lt;br /&gt;
        new_due_date.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  before_save :set_default_round&lt;br /&gt;
&lt;br /&gt;
  def set_default_round&lt;br /&gt;
    self.round ||= 1&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Permission-Checking Methods ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Currently, permission checks depend only on user roles and ignore the actual deadline.  &lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Role-based permissions (participant_helpers.rb)&lt;br /&gt;
def participant_permissions(authorization)&lt;br /&gt;
  case authorization&lt;br /&gt;
  when 'reader'   then { can_submit: false, can_review: true, can_take_quiz: true }&lt;br /&gt;
  when 'reviewer' then { can_submit: false, can_review: true, can_take_quiz: false }&lt;br /&gt;
  when 'submitter' then { can_submit: true, can_review: false, can_take_quiz: false }&lt;br /&gt;
  else { can_submit: true, can_review: true, can_take_quiz: true }&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And deadline-based checks exist separately in the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def submission_allowed(topic_id = nil)&lt;br /&gt;
  check_condition('submission_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
def can_review(topic_id = nil)&lt;br /&gt;
  check_condition('review_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These two layers (role-based and time-based) never interact, leading to inconsistencies (e.g., a user with &amp;lt;code&amp;gt;can_submit=true&amp;lt;/code&amp;gt; after deadline).&lt;br /&gt;
&lt;br /&gt;
==== Proposed Changes ====&lt;br /&gt;
&lt;br /&gt;
The new &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module integrates role-based and deadline-based checks:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDatePermissions&lt;br /&gt;
  # Permission checking methods that combine deadline-based and role-based logic&lt;br /&gt;
  #&lt;br /&gt;
  # These methods must check both:&lt;br /&gt;
  # 1. Whether the action is permitted by the current deadline (submission_allowed_id, review_allowed_id, etc.)&lt;br /&gt;
  # 2. Whether the participant has the necessary permissions (can_submit, can_review, can_take_quiz fields)&lt;br /&gt;
&lt;br /&gt;
  # Check if submission is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_submit?(participant = nil)&lt;br /&gt;
    return false unless submission_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_submit&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: submission_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if review is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_review?(participant = nil)&lt;br /&gt;
    return false unless review_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_review&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: review_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if taking a quiz is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_take_quiz?(participant = nil)&lt;br /&gt;
    return false unless quiz_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_take_quiz&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: quiz_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if teammate review is allowed&lt;br /&gt;
  def can_review_teammate?(participant = nil)&lt;br /&gt;
    return false unless teammate_review_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_review&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: teammate_review_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Generic permission checker&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    permission_field = &amp;quot;#{activity}_allowed_id&amp;quot;&lt;br /&gt;
    return false unless respond_to?(permission_field)&lt;br /&gt;
&lt;br /&gt;
    allowed_id = public_send(permission_field)&lt;br /&gt;
    return false unless allowed_id&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get permission status for an action (OK, Late, No)&lt;br /&gt;
  def permission_status_for(action)&lt;br /&gt;
    permission_field = &amp;quot;#{action}_allowed_id&amp;quot;&lt;br /&gt;
    return 'No' unless respond_to?(permission_field)&lt;br /&gt;
&lt;br /&gt;
    allowed_id = public_send(permission_field)&lt;br /&gt;
    return 'No' unless allowed_id&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name || 'No'&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This module is included in the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== DueDateActions Mixin ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module provides deadline-related operations for models that have due dates (Assignment, SignUpTopic).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDateActions&lt;br /&gt;
  # Generic activity permission checker that determines if an activity is permissible&lt;br /&gt;
  # based on the current deadline state for this parent object&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    current_deadline = next_due_date()&lt;br /&gt;
    return false unless current_deadline&lt;br /&gt;
&lt;br /&gt;
    current_deadline.activity_permissible?(activity)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Syntactic sugar methods for common activities&lt;br /&gt;
  def submission_permissible?&lt;br /&gt;
    activity_permissible?(:submission)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def review_permissible?&lt;br /&gt;
    activity_permissible?(:review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def teammate_review_permissible?&lt;br /&gt;
    activity_permissible?(:teammate_review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def quiz_permissible?&lt;br /&gt;
    activity_permissible?(:quiz)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def team_formation_permissible?&lt;br /&gt;
    activity_permissible?(:team_formation)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def signup_permissible?&lt;br /&gt;
    activity_permissible?(:signup)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def drop_topic_permissible?&lt;br /&gt;
    activity_permissible?(:drop_topic)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the next due date for this assignment&lt;br /&gt;
  def next_due_date(topic_id = nil)&lt;br /&gt;
    # If a topic is specified and this assignment has topic-specific deadlines,&lt;br /&gt;
    # look for topic due dates first&lt;br /&gt;
    if topic_id &amp;amp;&amp;amp; has_topic_specific_deadlines?&lt;br /&gt;
      topic_deadline = due_dates.where(parent_id: topic_id, parent_type: 'SignUpTopic')&lt;br /&gt;
                               .upcoming&lt;br /&gt;
                               .first&lt;br /&gt;
      return topic_deadline if topic_deadline&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Fall back to assignment-level due dates&lt;br /&gt;
    due_dates.upcoming.first&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the current stage name for display purposes&lt;br /&gt;
  def current_stage&lt;br /&gt;
    deadline = next_due_date()&lt;br /&gt;
    return 'finished' unless deadline&lt;br /&gt;
&lt;br /&gt;
    deadline.deadline_type&amp;amp;.name || 'unknown'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if assignment has topic-specific deadlines&lt;br /&gt;
  def has_topic_specific_deadlines?&lt;br /&gt;
    staggered_deadline || due_dates.where(parent_type: 'SignUpTopic').exists?&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Copy due dates to a new parent object&lt;br /&gt;
  def copy_due_dates_to(new_parent)&lt;br /&gt;
    due_dates.find_each do |due_date|&lt;br /&gt;
      new_due_date = due_date.dup&lt;br /&gt;
      new_due_date.parent = new_parent&lt;br /&gt;
      new_due_date.save!&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This module is included in &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class Assignment &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
class SignUpTopic &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Implementation Summary ==&lt;br /&gt;
&lt;br /&gt;
=== Models Created/Modified ===&lt;br /&gt;
&lt;br /&gt;
* '''DeadlineType''' - Canonical deadline type definitions with semantic helper methods&lt;br /&gt;
* '''DeadlineRight''' - Permission state model (OK/Late/No) with comparison methods&lt;br /&gt;
* '''DueDate''' - Refactored with instance methods, scopes, and permission checking&lt;br /&gt;
* '''TopicDueDate''' - STI subclass for topic-specific deadlines&lt;br /&gt;
* '''Assignment''' - Includes DueDateActions mixin&lt;br /&gt;
* '''SignUpTopic''' - Includes DueDateActions mixin&lt;br /&gt;
&lt;br /&gt;
=== Modules Created ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions''' - Permission checking combining deadline and role constraints&lt;br /&gt;
* '''DueDateActions''' - Deadline-related operations for parent models (Assignment, SignUpTopic)&lt;br /&gt;
&lt;br /&gt;
=== Key Improvements ===&lt;br /&gt;
&lt;br /&gt;
# '''Separation of Concerns''': Permission logic separated into dedicated modules&lt;br /&gt;
# '''Instance Methods''': Replaced class methods with testable instance methods&lt;br /&gt;
# '''Polymorphic Design''': Single DueDate model serves both Assignment and SignUpTopic&lt;br /&gt;
# '''Unified Permissions''': Role-based and time-based checks combined in one interface&lt;br /&gt;
# '''Data Integrity''': Removed duplicate deadline types, enforced foreign key constraints&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
The test suite includes comprehensive RSpec tests for the ''DueDate'' model and its associated models (''DeadlineType'', ''DeadlineRight'').&lt;br /&gt;
These tests verify model validations, scopes, instance methods, class methods, and callbacks with extensive coverage of edge cases and error conditions.&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
The test suite follows RSpec best practices with:&lt;br /&gt;
* '''Nested contexts''' for different scenarios (e.g., &amp;quot;when parent is missing&amp;quot;, &amp;quot;when round is 0&amp;quot;)&lt;br /&gt;
* '''Multiple assertions per context''' to verify behavior from different angles&lt;br /&gt;
* '''Edge case coverage''' including empty collections, nil values, and boundary conditions&lt;br /&gt;
* '''Error case testing''' for invalid data and non-existent records&lt;br /&gt;
* '''Clear descriptive names''' using &amp;quot;context 'when...'&amp;quot; and &amp;quot;it 'does something'&amp;quot; patterns&lt;br /&gt;
&lt;br /&gt;
=== Current Coverage ===&lt;br /&gt;
&lt;br /&gt;
==== Associations ====&lt;br /&gt;
* Tests verify &amp;lt;code&amp;gt;belongs_to :parent&amp;lt;/code&amp;gt; (polymorphic) and &amp;lt;code&amp;gt;belongs_to :deadline_type&amp;lt;/code&amp;gt; relationships&lt;br /&gt;
&lt;br /&gt;
==== Validations ====&lt;br /&gt;
* '''Required field validation''': Tests for parent, due_at, and deadline_type_id presence&lt;br /&gt;
* '''Round validation''': Tests for positive values, zero rejection, negative rejection, and nil handling&lt;br /&gt;
* '''Error messages''': Verifies specific error messages are added to appropriate fields&lt;br /&gt;
* '''Valid record creation''': Confirms records persist when all requirements are met&lt;br /&gt;
&lt;br /&gt;
==== Scopes ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;''': Returns future due dates ordered by due_at, excludes past dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;''': Returns past due dates ordered by due_at, excludes future dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;''': Filters by round number, handles non-existent rounds&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt;''': Filters by deadline type name, handles non-existent types&lt;br /&gt;
&lt;br /&gt;
==== Instance Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#overdue?&amp;lt;/code&amp;gt;''': Tests past dates (true), future dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#upcoming?&amp;lt;/code&amp;gt;''': Tests future dates (true), past dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#set&amp;lt;/code&amp;gt;''': Verifies updating deadline_type_id, parent_id, and round with persistence and error handling&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#copy&amp;lt;/code&amp;gt;''': Tests duplication to new assignment, attribute preservation, error handling for non-existent targets&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#deadline_type_name&amp;lt;/code&amp;gt;''': Returns associated deadline type name, handles nil cases&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#last_deadline?&amp;lt;/code&amp;gt;''': Tests detection of final deadline, handles multiple deadlines and single deadline scenarios&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#&amp;lt;=&amp;gt;&amp;lt;/code&amp;gt;''': Tests comparison by due_at time, handles non-DueDate objects&lt;br /&gt;
&lt;br /&gt;
==== Class Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.sort_due_dates&amp;lt;/code&amp;gt;''': Sorts by due_at ascending, handles empty arrays and single elements&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.any_future_due_dates?&amp;lt;/code&amp;gt;''': Detects future dates in collections, handles empty arrays and mixed date collections&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.copy&amp;lt;/code&amp;gt;''': Copies all due dates between assignments, preserves attributes, handles empty sources and non-existent records&lt;br /&gt;
&lt;br /&gt;
==== Callbacks ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;before_save :set_default_round&amp;lt;/code&amp;gt;''': Sets round to 1 when not specified, preserves explicit values, handles nil&lt;br /&gt;
&lt;br /&gt;
=== Test Examples ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Validation with multiple assertions&lt;br /&gt;
describe 'validations' do&lt;br /&gt;
  context 'when parent is missing' do&lt;br /&gt;
    let(:due_date) do&lt;br /&gt;
      DueDate.new(&lt;br /&gt;
        parent: nil,&lt;br /&gt;
        due_at: 2.days.from_now,&lt;br /&gt;
        deadline_type: submission_type,&lt;br /&gt;
        submission_allowed_id: ok_right.id,&lt;br /&gt;
        review_allowed_id: ok_right.id&lt;br /&gt;
      )&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'is invalid' do&lt;br /&gt;
      expect(due_date).to be_invalid&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'has error on parent field' do&lt;br /&gt;
      due_date.valid?&lt;br /&gt;
      expect(due_date.errors[:parent]).to include('must exist')&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when round validation' do&lt;br /&gt;
    context 'with round value of 0' do&lt;br /&gt;
      let(:due_date) do&lt;br /&gt;
        DueDate.new(&lt;br /&gt;
          parent: assignment,&lt;br /&gt;
          due_at: 2.days.from_now,&lt;br /&gt;
          deadline_type: submission_type,&lt;br /&gt;
          submission_allowed_id: ok_right.id,&lt;br /&gt;
          review_allowed_id: ok_right.id,&lt;br /&gt;
          round: 0&lt;br /&gt;
        )&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'is invalid' do&lt;br /&gt;
        expect(due_date).to be_invalid&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'has error on round field' do&lt;br /&gt;
        due_date.valid?&lt;br /&gt;
        expect(due_date.errors[:round]).to be_present&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'with nil round' do&lt;br /&gt;
      let(:due_date) do&lt;br /&gt;
        DueDate.create(&lt;br /&gt;
          parent: assignment,&lt;br /&gt;
          due_at: 2.days.from_now,&lt;br /&gt;
          deadline_type: submission_type,&lt;br /&gt;
          submission_allowed_id: ok_right.id,&lt;br /&gt;
          review_allowed_id: ok_right.id,&lt;br /&gt;
          round: nil&lt;br /&gt;
        )&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'is valid' do&lt;br /&gt;
        expect(due_date).to be_valid&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'sets default round to 1' do&lt;br /&gt;
        expect(due_date.round).to eq(1)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
# Scope with edge cases&lt;br /&gt;
describe '.upcoming' do&lt;br /&gt;
  it 'returns only future due dates' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).to contain_exactly(upcoming_due_date1, upcoming_due_date2)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'orders results by due_at ascending' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).to eq([upcoming_due_date1, upcoming_due_date2])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'excludes past due dates' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).not_to include(past_due_date)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when all due dates are in the past' do&lt;br /&gt;
    before do&lt;br /&gt;
      DueDate.destroy_all&lt;br /&gt;
      DueDate.create(&lt;br /&gt;
        parent: assignment,&lt;br /&gt;
        due_at: 3.days.ago,&lt;br /&gt;
        deadline_type: submission_type,&lt;br /&gt;
        submission_allowed_id: ok_right.id,&lt;br /&gt;
        review_allowed_id: ok_right.id&lt;br /&gt;
      )&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns empty collection' do&lt;br /&gt;
      expect(DueDate.upcoming).to be_empty&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
# Instance method with error handling&lt;br /&gt;
describe '#copy' do&lt;br /&gt;
  let(:original) do&lt;br /&gt;
    DueDate.create(&lt;br /&gt;
      parent: assignment,&lt;br /&gt;
      due_at: 2.days.from_now,&lt;br /&gt;
      deadline_type: submission_type,&lt;br /&gt;
      submission_allowed_id: ok_right.id,&lt;br /&gt;
      review_allowed_id: late_right.id,&lt;br /&gt;
      round: 1&lt;br /&gt;
    )&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  let(:copied) { original.copy(assignment2.id) }&lt;br /&gt;
&lt;br /&gt;
  it 'creates a new persisted record' do&lt;br /&gt;
    expect(copied).to be_persisted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'has different id from original' do&lt;br /&gt;
    expect(copied.id).not_to eq(original.id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'preserves all attributes' do&lt;br /&gt;
    expect(copied.due_at).to eq(original.due_at)&lt;br /&gt;
    expect(copied.deadline_type_id).to eq(original.deadline_type_id)&lt;br /&gt;
    expect(copied.round).to eq(original.round)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when copying to non-existent assignment' do&lt;br /&gt;
    it 'raises error' do&lt;br /&gt;
      expect do&lt;br /&gt;
        original.copy(99999)&lt;br /&gt;
      end.to raise_error(ActiveRecord::RecordNotFound)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Test Metrics ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDate model''': 60+ examples covering all methods and edge cases&lt;br /&gt;
* '''Context depth''': 2-3 levels for complex scenarios&lt;br /&gt;
* '''Code coverage''': 100% coverage of DueDate model methods&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
=== Demo Link ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add deadline notification system using the new permission framework&lt;br /&gt;
* Implement deadline templates for common assignment patterns&lt;br /&gt;
* Add API endpoints for mobile app integration&lt;br /&gt;
* Create admin dashboard for monitoring deadline compliance across courses&lt;br /&gt;
* Add automated deadline extension workflows based on configurable rules&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167194</id>
		<title>CSC/ECE 517 Fall 2025 - E2566. Finish DueDates</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167194"/>
		<updated>2025-12-02T04:22:50Z</updated>

		<summary type="html">&lt;p&gt;Skim253: /* DeadlineType Model Implementation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
This project aims to complete the implementation of the DueDate system in Expertiza. The current implementation consists mostly of class methods and lacks proper definition of different deadline types and permission checking functionality. This redesign will create a more robust, readable, and maintainable due date system.&lt;br /&gt;
&lt;br /&gt;
=== Existing Components ===&lt;br /&gt;
* &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model with polymorphic parent association (Assignment/SignUpTopic)&lt;br /&gt;
* &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; subclasses&lt;br /&gt;
* Basic CRUD operations (within &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AssignmentForm&amp;lt;/code&amp;gt;) and sorting functionality (&amp;lt;code&amp;gt;deadline_sort&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Database schema with &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt; field&lt;br /&gt;
&lt;br /&gt;
=== Current Behavior ===&lt;br /&gt;
&lt;br /&gt;
====Admin's View====&lt;br /&gt;
[[File:Duedates.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* When editing an assignment, due dates can be modified under the Due Dates tab.&lt;br /&gt;
* Each row contains the following columns: &amp;lt;code&amp;gt;Date &amp;amp; Time&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Use Date Updater&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Submission Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Teammate Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Meta-Review Allowed&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Reminder&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:Assignments-CurrentStage.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* In the Assignments table, the ''DueDate'' of each assignment is displayed as Current Stage and Stage Deadline.&lt;br /&gt;
&lt;br /&gt;
====Student's View====&lt;br /&gt;
[[File:Student-assignments.png|600px]]&lt;br /&gt;
* Students can view the due date information under &amp;lt;code&amp;gt;Current State&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Stage Deadline&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
* The actions available to students are determined by the current DueDate status.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-not-over.png|600px]]&lt;br /&gt;
* For example, during the submission period, students can access the &amp;lt;code&amp;gt;Submit a hyperlink&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Submit a file&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-over.png|600px]]&lt;br /&gt;
* Once the due date has passed, students cannot submit either a hyperlink or a file.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Currently, there are some glaring issues with how due_dates was created in the first place, including redundancy and lack of modularity.&lt;br /&gt;
&lt;br /&gt;
Modeling &amp;amp; Structural Issues&lt;br /&gt;
# No dedicated &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to define different kinds of deadlines. The existing &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; class only serves as an association for &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; models, and is never referenced in the current &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; implementation.&lt;br /&gt;
# Duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table, which may lead to potential bugs.&lt;br /&gt;
# Incomplete deadline type definitions - &amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt; need to be added.&lt;br /&gt;
# No clear separation of concerns - data persistence, business logic, and permission logic are mixed in a single model.&lt;br /&gt;
&lt;br /&gt;
Permission &amp;amp; Behavior Issues&lt;br /&gt;
# Overuse of class methods making the code difficult to maintain.&lt;br /&gt;
# Missing permission checking logic for user actions.&lt;br /&gt;
&lt;br /&gt;
=== Tasks Identified ===&lt;br /&gt;
&lt;br /&gt;
Modeling &amp;amp; Structural Issues&lt;br /&gt;
# Refactor the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model to separate persistence, business logic, and permission logic into distinct layers.&lt;br /&gt;
# Implement reusable mixins (e.g., &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;) to handle permission evaluation and deadline-related operations.&lt;br /&gt;
# Introduce instance-level methods (e.g., &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;) to replace class-level utilities and improve testability.&lt;br /&gt;
&lt;br /&gt;
Permission &amp;amp; Behavior Issues&lt;br /&gt;
# Integrate role-based and time-based permission checks within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; to ensure consistent access control.&lt;br /&gt;
# Redesign the &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.&lt;br /&gt;
# Add missing deadline types (&amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt;) and remove duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries for data integrity.&lt;br /&gt;
&lt;br /&gt;
== Proposed Solution ==&lt;br /&gt;
The proposed solution restructures the due date system by separating concerns across dedicated models and mixin modules.&lt;br /&gt;
Deadline definitions (&amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt;), core deadline data (&amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;), and behavioral logic (&amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;) are isolated so each component has a single clear responsibility.&lt;br /&gt;
This modular design improves readability, reduces coupling, and makes deadline-related behavior easier to extend and maintain.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Duedate-diagram.png|600px]]&lt;br /&gt;
&lt;br /&gt;
This diagram visualizes the redesigned architecture: blue/red/green indicate Rails models, orange indicates modules. &lt;br /&gt;
Parent models call DueDateActions, which manages DueDate, while DueDatePermissions checks user rights using reference models. &lt;br /&gt;
&lt;br /&gt;
=== DeadlineType Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Although the current codebase includes a &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model, it only serves as a passive lookup table for associations with &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt;.  &lt;br /&gt;
In practice, most parts of the system still rely on hard-coded numeric IDs or manual lookups such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Example of current usage&lt;br /&gt;
deadline_type_id = DeadlineType.find_by(name: 'review').id&lt;br /&gt;
if due_date.deadline_type_id == deadline_type_id&lt;br /&gt;
  # Perform review-related logic&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of using the model as an active domain object, &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; directly compares IDs or string literals.  &lt;br /&gt;
This leads to several structural issues:&lt;br /&gt;
&lt;br /&gt;
* Inconsistent references (some use IDs, others strings)&lt;br /&gt;
* Tight coupling between &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
* Lack of helper semantics (e.g., &amp;lt;code&amp;gt;is_review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;is_submission?&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Data duplication and integrity risks (two &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; rows)&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; will serve as a source of truth, defining all valid deadline types and providing meaningful interfaces.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table defines all canonical deadline categories used by the system.&lt;br /&gt;
Each row represents one distinct deadline type, and every &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; record must reference one of these entries through &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Below is a structural description of the table:&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; Table Structure                                                    &lt;br /&gt;
|- &lt;br /&gt;
! Column                                                                                         &lt;br /&gt;
! Type                                                                                           &lt;br /&gt;
! Description                                                                                    &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| id                                                                                               &lt;br /&gt;
| BIGINT (Primary Key)                                                                             &lt;br /&gt;
| Unique identifier for each deadline type; referenced by &amp;lt;code&amp;gt;due_dates.deadline_type_id&amp;lt;/code&amp;gt;. &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| name                                                                                             &lt;br /&gt;
| VARCHAR(255)                                                                                     &lt;br /&gt;
| Symbolic name (e.g., &amp;lt;code&amp;gt;submission&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;review&amp;lt;/code&amp;gt;); must be unique.              &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| description                                                                                      &lt;br /&gt;
| TEXT                                                                                             &lt;br /&gt;
| Human-readable explanation of the deadline category.                                             &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| created_at                                                                                       &lt;br /&gt;
| TIMESTAMP                                                                                        &lt;br /&gt;
| Time when the row was created.                                                                   &lt;br /&gt;
|-                                                                                                &lt;br /&gt;
| updated_at                                                                                       &lt;br /&gt;
| TIMESTAMP                                                                                        &lt;br /&gt;
| Time when the row was last updated.                                                              &lt;br /&gt;
|}                                                                                                &lt;br /&gt;
&lt;br /&gt;
This schema ensures that:&lt;br /&gt;
&lt;br /&gt;
* Each deadline type has a unique name and description.&lt;br /&gt;
* No duplicate entries can exist (e.g., the prior duplicated &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; was cleaned).&lt;br /&gt;
* All &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; rows reference this master table through &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;.&lt;br /&gt;
* The &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; column acts as the authoritative key used throughout permissions and due-date logic.&lt;br /&gt;
&lt;br /&gt;
Migrated constraints also ensure that &amp;lt;code&amp;gt;due_dates.deadline_type_id&amp;lt;/code&amp;gt; is consistently stored as &amp;lt;code&amp;gt;BIGINT&amp;lt;/code&amp;gt;, allowing a long-term stable keyspace.&lt;br /&gt;
&lt;br /&gt;
==== Deadline Type Definitions ====&lt;br /&gt;
&lt;br /&gt;
Because each row in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; is referenced by &amp;lt;code&amp;gt;DueDate.deadline_type_id&amp;lt;/code&amp;gt;, the following definitions map directly to the schema above.&lt;br /&gt;
&lt;br /&gt;
Each ID corresponds exactly to the &amp;lt;code&amp;gt;id&amp;lt;/code&amp;gt; field in the table and serves as the system-wide canonical reference for that deadline category.&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|+ Canonical Deadline Type Entries                                       &lt;br /&gt;
|-&lt;br /&gt;
! ID                                                                    &lt;br /&gt;
! Name                                                                  &lt;br /&gt;
! Description                                                           &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 1                                                                       &lt;br /&gt;
| submission                                                              &lt;br /&gt;
| Student submission deadlines                                            &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 2                                                                       &lt;br /&gt;
| review                                                                  &lt;br /&gt;
| Peer review deadlines                                                   &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 3                                                                       &lt;br /&gt;
| teammate_review                                                         &lt;br /&gt;
| Team member evaluation deadlines                                        &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 5                                                                       &lt;br /&gt;
| metareview                                                              &lt;br /&gt;
| Meta-review deadlines (kept for backward compatibility)                 &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 6                                                                       &lt;br /&gt;
| drop_topic                                                              &lt;br /&gt;
| Topic drop deadlines                                                    &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 7                                                                       &lt;br /&gt;
| signup                                                                  &lt;br /&gt;
| Topic signup deadlines                                                  &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 8                                                                       &lt;br /&gt;
| team_formation                                                          &lt;br /&gt;
| Team formation deadlines (duplicate entries cleaned; ID 8 is canonical) &lt;br /&gt;
|-                                                                       &lt;br /&gt;
| 11                                                                      &lt;br /&gt;
| quiz                                                                    &lt;br /&gt;
| Quiz completion deadlines                                               &lt;br /&gt;
|} &lt;br /&gt;
&lt;br /&gt;
These definitions are seeded during migration and act as the source of truth for all deadline-related logic within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;.&lt;br /&gt;
By linking each &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; to one of these entries through &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;, the system standardizes behavior and eliminates inconsistent ID or string comparisons from earlier implementations.&lt;br /&gt;
&lt;br /&gt;
==== DeadlineType Model Implementation ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineType &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
  validates :description, presence: true&lt;br /&gt;
&lt;br /&gt;
  has_many :due_dates, foreign_key: :deadline_type_id, dependent: :restrict_with_exception&lt;br /&gt;
&lt;br /&gt;
  # Semantic helper methods for deadline type identification&lt;br /&gt;
  def submission?&lt;br /&gt;
    name == 'submission'&lt;br /&gt;
  end&lt;br /&gt;
  # same code for review, teammate_review, quiz, team_formation, signup, drop_topic   &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DeadlineRight Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model represents the permission level for a specific action at a given deadline.&lt;br /&gt;
It defines three states: &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt; (action allowed), &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt; (action allowed with penalty), and &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt; (action denied).&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE deadline_rights (&lt;br /&gt;
  id BIGINT PRIMARY KEY,&lt;br /&gt;
  name VARCHAR(255) NOT NULL UNIQUE,&lt;br /&gt;
  created_at TIMESTAMP,&lt;br /&gt;
  updated_at TIMESTAMP&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== DeadlineRight Model ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineRight &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
&lt;br /&gt;
  # Permission checking methods&lt;br /&gt;
  def allows_action?&lt;br /&gt;
    %w[OK Late].include?(name)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def denies_action?&lt;br /&gt;
    name == 'No'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def allows_with_penalty?&lt;br /&gt;
    name == 'Late'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def allows_without_penalty?&lt;br /&gt;
    name == 'OK'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Display methods&lt;br /&gt;
  def to_s&lt;br /&gt;
    name&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def permission_level&lt;br /&gt;
    case name&lt;br /&gt;
    when 'No'   then 0&lt;br /&gt;
    when 'Late' then 1&lt;br /&gt;
    when 'OK'   then 2&lt;br /&gt;
    else -1&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== DueDate Model Refactoring ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model currently mixes persistence logic, deadline calculation, and permission checking, violating SRP.  &lt;br /&gt;
Most of its logic is implemented as class methods, which makes testing and extension difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Current design&lt;br /&gt;
def self.copy(old_assignment_id, new_assignment_id)&lt;br /&gt;
  duedates = where(parent_id: old_assignment_id)&lt;br /&gt;
  duedates.each do |orig_due_date|&lt;br /&gt;
    new_due_date = orig_due_date.dup&lt;br /&gt;
    new_due_date.parent_id = new_assignment_id&lt;br /&gt;
    new_due_date.save&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Improved Design ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
&lt;br /&gt;
  belongs_to :parent, polymorphic: true&lt;br /&gt;
  belongs_to :deadline_type, foreign_key: :deadline_type_id&lt;br /&gt;
&lt;br /&gt;
  validates :due_at, presence: true&lt;br /&gt;
  validates :deadline_type_id, presence: true&lt;br /&gt;
  validates :parent, presence: true&lt;br /&gt;
  validates :round, numericality: { greater_than: 0 }, allow_nil: true&lt;br /&gt;
&lt;br /&gt;
  # Scopes for common queries&lt;br /&gt;
  scope :upcoming, -&amp;gt; { where('due_at &amp;gt; ?', Time.current).order(:due_at) }&lt;br /&gt;
  scope :overdue, -&amp;gt; { where('due_at &amp;lt; ?', Time.current).order(:due_at) }&lt;br /&gt;
  scope :for_round, -&amp;gt;(round_num) { where(round: round_num) }&lt;br /&gt;
  scope :for_deadline_type, -&amp;gt;(type_name) { joins(:deadline_type).where(deadline_types: { name: type_name }) }&lt;br /&gt;
&lt;br /&gt;
  # Instance methods replace class methods&lt;br /&gt;
  def copy(to_assignment_id)&lt;br /&gt;
    to_assignment = Assignment.find(to_assignment_id)&lt;br /&gt;
    new_due_date = dup&lt;br /&gt;
    new_due_date.parent = to_assignment&lt;br /&gt;
    new_due_date.save!&lt;br /&gt;
    new_due_date&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def set(deadline_type_id, parent_id, round)&lt;br /&gt;
    self.deadline_type_id = deadline_type_id&lt;br /&gt;
    self.parent_id = parent_id&lt;br /&gt;
    self.round = round&lt;br /&gt;
    save!&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if this deadline has passed&lt;br /&gt;
  def overdue?&lt;br /&gt;
    due_at &amp;lt; Time.current&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if this deadline is upcoming&lt;br /&gt;
  def upcoming?&lt;br /&gt;
    due_at &amp;gt; Time.current&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the deadline type name&lt;br /&gt;
  def deadline_type_name&lt;br /&gt;
    deadline_type&amp;amp;.name&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Comparison method for sorting&lt;br /&gt;
  def &amp;lt;=&amp;gt;(other)&lt;br /&gt;
    return nil unless other.is_a?(DueDate)&lt;br /&gt;
    due_at &amp;lt;=&amp;gt; other.due_at&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Class methods for collection operations&lt;br /&gt;
  class &amp;lt;&amp;lt; self&lt;br /&gt;
    def sort_due_dates(due_dates)&lt;br /&gt;
      due_dates.sort_by(&amp;amp;:due_at)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    def any_future_due_dates?(due_dates)&lt;br /&gt;
      due_dates.any?(&amp;amp;:upcoming?)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    def copy(from_assignment_id, to_assignment_id)&lt;br /&gt;
      from_assignment = Assignment.find(from_assignment_id)&lt;br /&gt;
      to_assignment = Assignment.find(to_assignment_id)&lt;br /&gt;
&lt;br /&gt;
      from_assignment.due_dates.each do |due_date|&lt;br /&gt;
        new_due_date = due_date.dup&lt;br /&gt;
        new_due_date.parent = to_assignment&lt;br /&gt;
        new_due_date.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  before_save :set_default_round&lt;br /&gt;
&lt;br /&gt;
  def set_default_round&lt;br /&gt;
    self.round ||= 1&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Permission-Checking Methods ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Currently, permission checks depend only on user roles and ignore the actual deadline.  &lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Role-based permissions (participant_helpers.rb)&lt;br /&gt;
def participant_permissions(authorization)&lt;br /&gt;
  case authorization&lt;br /&gt;
  when 'reader'   then { can_submit: false, can_review: true, can_take_quiz: true }&lt;br /&gt;
  when 'reviewer' then { can_submit: false, can_review: true, can_take_quiz: false }&lt;br /&gt;
  when 'submitter' then { can_submit: true, can_review: false, can_take_quiz: false }&lt;br /&gt;
  else { can_submit: true, can_review: true, can_take_quiz: true }&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And deadline-based checks exist separately in the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def submission_allowed(topic_id = nil)&lt;br /&gt;
  check_condition('submission_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
def can_review(topic_id = nil)&lt;br /&gt;
  check_condition('review_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These two layers (role-based and time-based) never interact, leading to inconsistencies (e.g., a user with &amp;lt;code&amp;gt;can_submit=true&amp;lt;/code&amp;gt; after deadline).&lt;br /&gt;
&lt;br /&gt;
==== Proposed Changes ====&lt;br /&gt;
&lt;br /&gt;
The new &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module integrates role-based and deadline-based checks:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDatePermissions&lt;br /&gt;
  # Permission checking methods that combine deadline-based and role-based logic&lt;br /&gt;
  #&lt;br /&gt;
  # These methods must check both:&lt;br /&gt;
  # 1. Whether the action is permitted by the current deadline (submission_allowed_id, review_allowed_id, etc.)&lt;br /&gt;
  # 2. Whether the participant has the necessary permissions (can_submit, can_review, can_take_quiz fields)&lt;br /&gt;
&lt;br /&gt;
  # Check if submission is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_submit?(participant = nil)&lt;br /&gt;
    return false unless submission_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_submit&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: submission_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if review is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_review?(participant = nil)&lt;br /&gt;
    return false unless review_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_review&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: review_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if taking a quiz is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_take_quiz?(participant = nil)&lt;br /&gt;
    return false unless quiz_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_take_quiz&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: quiz_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if teammate review is allowed&lt;br /&gt;
  def can_review_teammate?(participant = nil)&lt;br /&gt;
    return false unless teammate_review_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_review&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: teammate_review_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Generic permission checker&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    permission_field = &amp;quot;#{activity}_allowed_id&amp;quot;&lt;br /&gt;
    return false unless respond_to?(permission_field)&lt;br /&gt;
&lt;br /&gt;
    allowed_id = public_send(permission_field)&lt;br /&gt;
    return false unless allowed_id&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get permission status for an action (OK, Late, No)&lt;br /&gt;
  def permission_status_for(action)&lt;br /&gt;
    permission_field = &amp;quot;#{action}_allowed_id&amp;quot;&lt;br /&gt;
    return 'No' unless respond_to?(permission_field)&lt;br /&gt;
&lt;br /&gt;
    allowed_id = public_send(permission_field)&lt;br /&gt;
    return 'No' unless allowed_id&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name || 'No'&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This module is included in the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== DueDateActions Mixin ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module provides deadline-related operations for models that have due dates (Assignment, SignUpTopic).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDateActions&lt;br /&gt;
  # Generic activity permission checker that determines if an activity is permissible&lt;br /&gt;
  # based on the current deadline state for this parent object&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    current_deadline = next_due_date()&lt;br /&gt;
    return false unless current_deadline&lt;br /&gt;
&lt;br /&gt;
    current_deadline.activity_permissible?(activity)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Syntactic sugar methods for common activities&lt;br /&gt;
  def submission_permissible?&lt;br /&gt;
    activity_permissible?(:submission)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def review_permissible?&lt;br /&gt;
    activity_permissible?(:review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def teammate_review_permissible?&lt;br /&gt;
    activity_permissible?(:teammate_review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def quiz_permissible?&lt;br /&gt;
    activity_permissible?(:quiz)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def team_formation_permissible?&lt;br /&gt;
    activity_permissible?(:team_formation)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def signup_permissible?&lt;br /&gt;
    activity_permissible?(:signup)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def drop_topic_permissible?&lt;br /&gt;
    activity_permissible?(:drop_topic)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the next due date for this assignment&lt;br /&gt;
  def next_due_date(topic_id = nil)&lt;br /&gt;
    # If a topic is specified and this assignment has topic-specific deadlines,&lt;br /&gt;
    # look for topic due dates first&lt;br /&gt;
    if topic_id &amp;amp;&amp;amp; has_topic_specific_deadlines?&lt;br /&gt;
      topic_deadline = due_dates.where(parent_id: topic_id, parent_type: 'SignUpTopic')&lt;br /&gt;
                               .upcoming&lt;br /&gt;
                               .first&lt;br /&gt;
      return topic_deadline if topic_deadline&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Fall back to assignment-level due dates&lt;br /&gt;
    due_dates.upcoming.first&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the current stage name for display purposes&lt;br /&gt;
  def current_stage&lt;br /&gt;
    deadline = next_due_date()&lt;br /&gt;
    return 'finished' unless deadline&lt;br /&gt;
&lt;br /&gt;
    deadline.deadline_type&amp;amp;.name || 'unknown'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if assignment has topic-specific deadlines&lt;br /&gt;
  def has_topic_specific_deadlines?&lt;br /&gt;
    staggered_deadline || due_dates.where(parent_type: 'SignUpTopic').exists?&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Copy due dates to a new parent object&lt;br /&gt;
  def copy_due_dates_to(new_parent)&lt;br /&gt;
    due_dates.find_each do |due_date|&lt;br /&gt;
      new_due_date = due_date.dup&lt;br /&gt;
      new_due_date.parent = new_parent&lt;br /&gt;
      new_due_date.save!&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This module is included in &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class Assignment &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
class SignUpTopic &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Implementation Summary ==&lt;br /&gt;
&lt;br /&gt;
=== Models Created/Modified ===&lt;br /&gt;
&lt;br /&gt;
* '''DeadlineType''' - Canonical deadline type definitions with semantic helper methods&lt;br /&gt;
* '''DeadlineRight''' - Permission state model (OK/Late/No) with comparison methods&lt;br /&gt;
* '''DueDate''' - Refactored with instance methods, scopes, and permission checking&lt;br /&gt;
* '''TopicDueDate''' - STI subclass for topic-specific deadlines&lt;br /&gt;
* '''Assignment''' - Includes DueDateActions mixin&lt;br /&gt;
* '''SignUpTopic''' - Includes DueDateActions mixin&lt;br /&gt;
&lt;br /&gt;
=== Modules Created ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions''' - Permission checking combining deadline and role constraints&lt;br /&gt;
* '''DueDateActions''' - Deadline-related operations for parent models (Assignment, SignUpTopic)&lt;br /&gt;
&lt;br /&gt;
=== Key Improvements ===&lt;br /&gt;
&lt;br /&gt;
# '''Separation of Concerns''': Permission logic separated into dedicated modules&lt;br /&gt;
# '''Instance Methods''': Replaced class methods with testable instance methods&lt;br /&gt;
# '''Polymorphic Design''': Single DueDate model serves both Assignment and SignUpTopic&lt;br /&gt;
# '''Unified Permissions''': Role-based and time-based checks combined in one interface&lt;br /&gt;
# '''Data Integrity''': Removed duplicate deadline types, enforced foreign key constraints&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
The test suite includes comprehensive RSpec tests for the ''DueDate'' model and its associated models (''DeadlineType'', ''DeadlineRight'').&lt;br /&gt;
These tests verify model validations, scopes, instance methods, class methods, and callbacks with extensive coverage of edge cases and error conditions.&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
The test suite follows RSpec best practices with:&lt;br /&gt;
* '''Nested contexts''' for different scenarios (e.g., &amp;quot;when parent is missing&amp;quot;, &amp;quot;when round is 0&amp;quot;)&lt;br /&gt;
* '''Multiple assertions per context''' to verify behavior from different angles&lt;br /&gt;
* '''Edge case coverage''' including empty collections, nil values, and boundary conditions&lt;br /&gt;
* '''Error case testing''' for invalid data and non-existent records&lt;br /&gt;
* '''Clear descriptive names''' using &amp;quot;context 'when...'&amp;quot; and &amp;quot;it 'does something'&amp;quot; patterns&lt;br /&gt;
&lt;br /&gt;
=== Current Coverage ===&lt;br /&gt;
&lt;br /&gt;
==== Associations ====&lt;br /&gt;
* Tests verify &amp;lt;code&amp;gt;belongs_to :parent&amp;lt;/code&amp;gt; (polymorphic) and &amp;lt;code&amp;gt;belongs_to :deadline_type&amp;lt;/code&amp;gt; relationships&lt;br /&gt;
&lt;br /&gt;
==== Validations ====&lt;br /&gt;
* '''Required field validation''': Tests for parent, due_at, and deadline_type_id presence&lt;br /&gt;
* '''Round validation''': Tests for positive values, zero rejection, negative rejection, and nil handling&lt;br /&gt;
* '''Error messages''': Verifies specific error messages are added to appropriate fields&lt;br /&gt;
* '''Valid record creation''': Confirms records persist when all requirements are met&lt;br /&gt;
&lt;br /&gt;
==== Scopes ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;''': Returns future due dates ordered by due_at, excludes past dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;''': Returns past due dates ordered by due_at, excludes future dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;''': Filters by round number, handles non-existent rounds&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt;''': Filters by deadline type name, handles non-existent types&lt;br /&gt;
&lt;br /&gt;
==== Instance Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#overdue?&amp;lt;/code&amp;gt;''': Tests past dates (true), future dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#upcoming?&amp;lt;/code&amp;gt;''': Tests future dates (true), past dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#set&amp;lt;/code&amp;gt;''': Verifies updating deadline_type_id, parent_id, and round with persistence and error handling&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#copy&amp;lt;/code&amp;gt;''': Tests duplication to new assignment, attribute preservation, error handling for non-existent targets&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#deadline_type_name&amp;lt;/code&amp;gt;''': Returns associated deadline type name, handles nil cases&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#last_deadline?&amp;lt;/code&amp;gt;''': Tests detection of final deadline, handles multiple deadlines and single deadline scenarios&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#&amp;lt;=&amp;gt;&amp;lt;/code&amp;gt;''': Tests comparison by due_at time, handles non-DueDate objects&lt;br /&gt;
&lt;br /&gt;
==== Class Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.sort_due_dates&amp;lt;/code&amp;gt;''': Sorts by due_at ascending, handles empty arrays and single elements&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.any_future_due_dates?&amp;lt;/code&amp;gt;''': Detects future dates in collections, handles empty arrays and mixed date collections&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.copy&amp;lt;/code&amp;gt;''': Copies all due dates between assignments, preserves attributes, handles empty sources and non-existent records&lt;br /&gt;
&lt;br /&gt;
==== Callbacks ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;before_save :set_default_round&amp;lt;/code&amp;gt;''': Sets round to 1 when not specified, preserves explicit values, handles nil&lt;br /&gt;
&lt;br /&gt;
=== Test Examples ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Validation with multiple assertions&lt;br /&gt;
describe 'validations' do&lt;br /&gt;
  context 'when parent is missing' do&lt;br /&gt;
    let(:due_date) do&lt;br /&gt;
      DueDate.new(&lt;br /&gt;
        parent: nil,&lt;br /&gt;
        due_at: 2.days.from_now,&lt;br /&gt;
        deadline_type: submission_type,&lt;br /&gt;
        submission_allowed_id: ok_right.id,&lt;br /&gt;
        review_allowed_id: ok_right.id&lt;br /&gt;
      )&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'is invalid' do&lt;br /&gt;
      expect(due_date).to be_invalid&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'has error on parent field' do&lt;br /&gt;
      due_date.valid?&lt;br /&gt;
      expect(due_date.errors[:parent]).to include('must exist')&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when round validation' do&lt;br /&gt;
    context 'with round value of 0' do&lt;br /&gt;
      let(:due_date) do&lt;br /&gt;
        DueDate.new(&lt;br /&gt;
          parent: assignment,&lt;br /&gt;
          due_at: 2.days.from_now,&lt;br /&gt;
          deadline_type: submission_type,&lt;br /&gt;
          submission_allowed_id: ok_right.id,&lt;br /&gt;
          review_allowed_id: ok_right.id,&lt;br /&gt;
          round: 0&lt;br /&gt;
        )&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'is invalid' do&lt;br /&gt;
        expect(due_date).to be_invalid&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'has error on round field' do&lt;br /&gt;
        due_date.valid?&lt;br /&gt;
        expect(due_date.errors[:round]).to be_present&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'with nil round' do&lt;br /&gt;
      let(:due_date) do&lt;br /&gt;
        DueDate.create(&lt;br /&gt;
          parent: assignment,&lt;br /&gt;
          due_at: 2.days.from_now,&lt;br /&gt;
          deadline_type: submission_type,&lt;br /&gt;
          submission_allowed_id: ok_right.id,&lt;br /&gt;
          review_allowed_id: ok_right.id,&lt;br /&gt;
          round: nil&lt;br /&gt;
        )&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'is valid' do&lt;br /&gt;
        expect(due_date).to be_valid&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'sets default round to 1' do&lt;br /&gt;
        expect(due_date.round).to eq(1)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
# Scope with edge cases&lt;br /&gt;
describe '.upcoming' do&lt;br /&gt;
  it 'returns only future due dates' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).to contain_exactly(upcoming_due_date1, upcoming_due_date2)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'orders results by due_at ascending' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).to eq([upcoming_due_date1, upcoming_due_date2])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'excludes past due dates' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).not_to include(past_due_date)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when all due dates are in the past' do&lt;br /&gt;
    before do&lt;br /&gt;
      DueDate.destroy_all&lt;br /&gt;
      DueDate.create(&lt;br /&gt;
        parent: assignment,&lt;br /&gt;
        due_at: 3.days.ago,&lt;br /&gt;
        deadline_type: submission_type,&lt;br /&gt;
        submission_allowed_id: ok_right.id,&lt;br /&gt;
        review_allowed_id: ok_right.id&lt;br /&gt;
      )&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns empty collection' do&lt;br /&gt;
      expect(DueDate.upcoming).to be_empty&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
# Instance method with error handling&lt;br /&gt;
describe '#copy' do&lt;br /&gt;
  let(:original) do&lt;br /&gt;
    DueDate.create(&lt;br /&gt;
      parent: assignment,&lt;br /&gt;
      due_at: 2.days.from_now,&lt;br /&gt;
      deadline_type: submission_type,&lt;br /&gt;
      submission_allowed_id: ok_right.id,&lt;br /&gt;
      review_allowed_id: late_right.id,&lt;br /&gt;
      round: 1&lt;br /&gt;
    )&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  let(:copied) { original.copy(assignment2.id) }&lt;br /&gt;
&lt;br /&gt;
  it 'creates a new persisted record' do&lt;br /&gt;
    expect(copied).to be_persisted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'has different id from original' do&lt;br /&gt;
    expect(copied.id).not_to eq(original.id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'preserves all attributes' do&lt;br /&gt;
    expect(copied.due_at).to eq(original.due_at)&lt;br /&gt;
    expect(copied.deadline_type_id).to eq(original.deadline_type_id)&lt;br /&gt;
    expect(copied.round).to eq(original.round)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when copying to non-existent assignment' do&lt;br /&gt;
    it 'raises error' do&lt;br /&gt;
      expect do&lt;br /&gt;
        original.copy(99999)&lt;br /&gt;
      end.to raise_error(ActiveRecord::RecordNotFound)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Test Metrics ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDate model''': 60+ examples covering all methods and edge cases&lt;br /&gt;
* '''Context depth''': 2-3 levels for complex scenarios&lt;br /&gt;
* '''Code coverage''': 100% coverage of DueDate model methods&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
=== Demo Link ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add deadline notification system using the new permission framework&lt;br /&gt;
* Implement deadline templates for common assignment patterns&lt;br /&gt;
* Add API endpoints for mobile app integration&lt;br /&gt;
* Create admin dashboard for monitoring deadline compliance across courses&lt;br /&gt;
* Add automated deadline extension workflows based on configurable rules&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167193</id>
		<title>CSC/ECE 517 Fall 2025 - E2566. Finish DueDates</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167193"/>
		<updated>2025-12-02T04:05:08Z</updated>

		<summary type="html">&lt;p&gt;Skim253: /* Proposed Solution */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
This project aims to complete the implementation of the DueDate system in Expertiza. The current implementation consists mostly of class methods and lacks proper definition of different deadline types and permission checking functionality. This redesign will create a more robust, readable, and maintainable due date system.&lt;br /&gt;
&lt;br /&gt;
=== Existing Components ===&lt;br /&gt;
* &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model with polymorphic parent association (Assignment/SignUpTopic)&lt;br /&gt;
* &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; subclasses&lt;br /&gt;
* Basic CRUD operations (within &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AssignmentForm&amp;lt;/code&amp;gt;) and sorting functionality (&amp;lt;code&amp;gt;deadline_sort&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Database schema with &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt; field&lt;br /&gt;
&lt;br /&gt;
=== Current Behavior ===&lt;br /&gt;
&lt;br /&gt;
====Admin's View====&lt;br /&gt;
[[File:Duedates.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* When editing an assignment, due dates can be modified under the Due Dates tab.&lt;br /&gt;
* Each row contains the following columns: &amp;lt;code&amp;gt;Date &amp;amp; Time&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Use Date Updater&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Submission Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Teammate Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Meta-Review Allowed&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Reminder&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:Assignments-CurrentStage.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* In the Assignments table, the ''DueDate'' of each assignment is displayed as Current Stage and Stage Deadline.&lt;br /&gt;
&lt;br /&gt;
====Student's View====&lt;br /&gt;
[[File:Student-assignments.png|600px]]&lt;br /&gt;
* Students can view the due date information under &amp;lt;code&amp;gt;Current State&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Stage Deadline&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
* The actions available to students are determined by the current DueDate status.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-not-over.png|600px]]&lt;br /&gt;
* For example, during the submission period, students can access the &amp;lt;code&amp;gt;Submit a hyperlink&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Submit a file&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-over.png|600px]]&lt;br /&gt;
* Once the due date has passed, students cannot submit either a hyperlink or a file.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Currently, there are some glaring issues with how due_dates was created in the first place, including redundancy and lack of modularity.&lt;br /&gt;
&lt;br /&gt;
Modeling &amp;amp; Structural Issues&lt;br /&gt;
# No dedicated &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to define different kinds of deadlines. The existing &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; class only serves as an association for &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; models, and is never referenced in the current &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; implementation.&lt;br /&gt;
# Duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table, which may lead to potential bugs.&lt;br /&gt;
# Incomplete deadline type definitions - &amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt; need to be added.&lt;br /&gt;
# No clear separation of concerns - data persistence, business logic, and permission logic are mixed in a single model.&lt;br /&gt;
&lt;br /&gt;
Permission &amp;amp; Behavior Issues&lt;br /&gt;
# Overuse of class methods making the code difficult to maintain.&lt;br /&gt;
# Missing permission checking logic for user actions.&lt;br /&gt;
&lt;br /&gt;
=== Tasks Identified ===&lt;br /&gt;
&lt;br /&gt;
Modeling &amp;amp; Structural Issues&lt;br /&gt;
# Refactor the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model to separate persistence, business logic, and permission logic into distinct layers.&lt;br /&gt;
# Implement reusable mixins (e.g., &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;) to handle permission evaluation and deadline-related operations.&lt;br /&gt;
# Introduce instance-level methods (e.g., &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;) to replace class-level utilities and improve testability.&lt;br /&gt;
&lt;br /&gt;
Permission &amp;amp; Behavior Issues&lt;br /&gt;
# Integrate role-based and time-based permission checks within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; to ensure consistent access control.&lt;br /&gt;
# Redesign the &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.&lt;br /&gt;
# Add missing deadline types (&amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt;) and remove duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries for data integrity.&lt;br /&gt;
&lt;br /&gt;
== Proposed Solution ==&lt;br /&gt;
The proposed solution restructures the due date system by separating concerns across dedicated models and mixin modules.&lt;br /&gt;
Deadline definitions (&amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt;), core deadline data (&amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;), and behavioral logic (&amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;) are isolated so each component has a single clear responsibility.&lt;br /&gt;
This modular design improves readability, reduces coupling, and makes deadline-related behavior easier to extend and maintain.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Duedate-diagram.png|600px]]&lt;br /&gt;
&lt;br /&gt;
This diagram visualizes the redesigned architecture: blue/red/green indicate Rails models, orange indicates modules. &lt;br /&gt;
Parent models call DueDateActions, which manages DueDate, while DueDatePermissions checks user rights using reference models. &lt;br /&gt;
&lt;br /&gt;
=== DeadlineType Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Although the current codebase includes a &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model, it only serves as a passive lookup table for associations with &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt;.  &lt;br /&gt;
In practice, most parts of the system still rely on hard-coded numeric IDs or manual lookups such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Example of current usage&lt;br /&gt;
deadline_type_id = DeadlineType.find_by(name: 'review').id&lt;br /&gt;
if due_date.deadline_type_id == deadline_type_id&lt;br /&gt;
  # Perform review-related logic&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of using the model as an active domain object, &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; directly compares IDs or string literals.  &lt;br /&gt;
This leads to several structural issues:&lt;br /&gt;
&lt;br /&gt;
* Inconsistent references (some use IDs, others strings)&lt;br /&gt;
* Tight coupling between &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
* Lack of helper semantics (e.g., &amp;lt;code&amp;gt;is_review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;is_submission?&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Data duplication and integrity risks (two &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; rows)&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; will serve as a source of truth, defining all valid deadline types and providing meaningful interfaces.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE deadline_types (&lt;br /&gt;
  id BIGINT PRIMARY KEY,&lt;br /&gt;
  name VARCHAR(255) NOT NULL UNIQUE,&lt;br /&gt;
  description TEXT NOT NULL,&lt;br /&gt;
  created_at TIMESTAMP,&lt;br /&gt;
  updated_at TIMESTAMP&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Deadline Type Definitions ====&lt;br /&gt;
* &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;: submission - Student work submission deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt;: review - Peer review deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;3&amp;lt;/code&amp;gt;: teammate_review - Team member evaluation deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;5&amp;lt;/code&amp;gt;: metareview - Meta-review deadlines (kept for backward compatibility)  &lt;br /&gt;
* &amp;lt;code&amp;gt;6&amp;lt;/code&amp;gt;: drop_topic - Topic drop deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;7&amp;lt;/code&amp;gt;: signup - Course/assignment signup deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;8&amp;lt;/code&amp;gt;: team_formation - Team formation deadlines (clean up duplicate ID 10)  &lt;br /&gt;
* &amp;lt;code&amp;gt;11&amp;lt;/code&amp;gt;: quiz - Quiz completion deadlines&lt;br /&gt;
&lt;br /&gt;
==== DeadlineType Model Implementation ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineType &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
  validates :description, presence: true&lt;br /&gt;
&lt;br /&gt;
  has_many :due_dates, foreign_key: :deadline_type_id, dependent: :restrict_with_exception&lt;br /&gt;
&lt;br /&gt;
  # Semantic helper methods for deadline type identification&lt;br /&gt;
  def submission?&lt;br /&gt;
    name == 'submission'&lt;br /&gt;
  end&lt;br /&gt;
  # same code for review, teammate_review, quiz, team_formation, signup, drop_topic   &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DeadlineRight Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model represents the permission level for a specific action at a given deadline.&lt;br /&gt;
It defines three states: &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt; (action allowed), &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt; (action allowed with penalty), and &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt; (action denied).&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE deadline_rights (&lt;br /&gt;
  id BIGINT PRIMARY KEY,&lt;br /&gt;
  name VARCHAR(255) NOT NULL UNIQUE,&lt;br /&gt;
  created_at TIMESTAMP,&lt;br /&gt;
  updated_at TIMESTAMP&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== DeadlineRight Model ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineRight &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
&lt;br /&gt;
  # Permission checking methods&lt;br /&gt;
  def allows_action?&lt;br /&gt;
    %w[OK Late].include?(name)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def denies_action?&lt;br /&gt;
    name == 'No'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def allows_with_penalty?&lt;br /&gt;
    name == 'Late'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def allows_without_penalty?&lt;br /&gt;
    name == 'OK'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Display methods&lt;br /&gt;
  def to_s&lt;br /&gt;
    name&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def permission_level&lt;br /&gt;
    case name&lt;br /&gt;
    when 'No'   then 0&lt;br /&gt;
    when 'Late' then 1&lt;br /&gt;
    when 'OK'   then 2&lt;br /&gt;
    else -1&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== DueDate Model Refactoring ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model currently mixes persistence logic, deadline calculation, and permission checking, violating SRP.  &lt;br /&gt;
Most of its logic is implemented as class methods, which makes testing and extension difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Current design&lt;br /&gt;
def self.copy(old_assignment_id, new_assignment_id)&lt;br /&gt;
  duedates = where(parent_id: old_assignment_id)&lt;br /&gt;
  duedates.each do |orig_due_date|&lt;br /&gt;
    new_due_date = orig_due_date.dup&lt;br /&gt;
    new_due_date.parent_id = new_assignment_id&lt;br /&gt;
    new_due_date.save&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Improved Design ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
&lt;br /&gt;
  belongs_to :parent, polymorphic: true&lt;br /&gt;
  belongs_to :deadline_type, foreign_key: :deadline_type_id&lt;br /&gt;
&lt;br /&gt;
  validates :due_at, presence: true&lt;br /&gt;
  validates :deadline_type_id, presence: true&lt;br /&gt;
  validates :parent, presence: true&lt;br /&gt;
  validates :round, numericality: { greater_than: 0 }, allow_nil: true&lt;br /&gt;
&lt;br /&gt;
  # Scopes for common queries&lt;br /&gt;
  scope :upcoming, -&amp;gt; { where('due_at &amp;gt; ?', Time.current).order(:due_at) }&lt;br /&gt;
  scope :overdue, -&amp;gt; { where('due_at &amp;lt; ?', Time.current).order(:due_at) }&lt;br /&gt;
  scope :for_round, -&amp;gt;(round_num) { where(round: round_num) }&lt;br /&gt;
  scope :for_deadline_type, -&amp;gt;(type_name) { joins(:deadline_type).where(deadline_types: { name: type_name }) }&lt;br /&gt;
&lt;br /&gt;
  # Instance methods replace class methods&lt;br /&gt;
  def copy(to_assignment_id)&lt;br /&gt;
    to_assignment = Assignment.find(to_assignment_id)&lt;br /&gt;
    new_due_date = dup&lt;br /&gt;
    new_due_date.parent = to_assignment&lt;br /&gt;
    new_due_date.save!&lt;br /&gt;
    new_due_date&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def set(deadline_type_id, parent_id, round)&lt;br /&gt;
    self.deadline_type_id = deadline_type_id&lt;br /&gt;
    self.parent_id = parent_id&lt;br /&gt;
    self.round = round&lt;br /&gt;
    save!&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if this deadline has passed&lt;br /&gt;
  def overdue?&lt;br /&gt;
    due_at &amp;lt; Time.current&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if this deadline is upcoming&lt;br /&gt;
  def upcoming?&lt;br /&gt;
    due_at &amp;gt; Time.current&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the deadline type name&lt;br /&gt;
  def deadline_type_name&lt;br /&gt;
    deadline_type&amp;amp;.name&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Comparison method for sorting&lt;br /&gt;
  def &amp;lt;=&amp;gt;(other)&lt;br /&gt;
    return nil unless other.is_a?(DueDate)&lt;br /&gt;
    due_at &amp;lt;=&amp;gt; other.due_at&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Class methods for collection operations&lt;br /&gt;
  class &amp;lt;&amp;lt; self&lt;br /&gt;
    def sort_due_dates(due_dates)&lt;br /&gt;
      due_dates.sort_by(&amp;amp;:due_at)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    def any_future_due_dates?(due_dates)&lt;br /&gt;
      due_dates.any?(&amp;amp;:upcoming?)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    def copy(from_assignment_id, to_assignment_id)&lt;br /&gt;
      from_assignment = Assignment.find(from_assignment_id)&lt;br /&gt;
      to_assignment = Assignment.find(to_assignment_id)&lt;br /&gt;
&lt;br /&gt;
      from_assignment.due_dates.each do |due_date|&lt;br /&gt;
        new_due_date = due_date.dup&lt;br /&gt;
        new_due_date.parent = to_assignment&lt;br /&gt;
        new_due_date.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  before_save :set_default_round&lt;br /&gt;
&lt;br /&gt;
  def set_default_round&lt;br /&gt;
    self.round ||= 1&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Permission-Checking Methods ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Currently, permission checks depend only on user roles and ignore the actual deadline.  &lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Role-based permissions (participant_helpers.rb)&lt;br /&gt;
def participant_permissions(authorization)&lt;br /&gt;
  case authorization&lt;br /&gt;
  when 'reader'   then { can_submit: false, can_review: true, can_take_quiz: true }&lt;br /&gt;
  when 'reviewer' then { can_submit: false, can_review: true, can_take_quiz: false }&lt;br /&gt;
  when 'submitter' then { can_submit: true, can_review: false, can_take_quiz: false }&lt;br /&gt;
  else { can_submit: true, can_review: true, can_take_quiz: true }&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And deadline-based checks exist separately in the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def submission_allowed(topic_id = nil)&lt;br /&gt;
  check_condition('submission_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
def can_review(topic_id = nil)&lt;br /&gt;
  check_condition('review_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These two layers (role-based and time-based) never interact, leading to inconsistencies (e.g., a user with &amp;lt;code&amp;gt;can_submit=true&amp;lt;/code&amp;gt; after deadline).&lt;br /&gt;
&lt;br /&gt;
==== Proposed Changes ====&lt;br /&gt;
&lt;br /&gt;
The new &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module integrates role-based and deadline-based checks:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDatePermissions&lt;br /&gt;
  # Permission checking methods that combine deadline-based and role-based logic&lt;br /&gt;
  #&lt;br /&gt;
  # These methods must check both:&lt;br /&gt;
  # 1. Whether the action is permitted by the current deadline (submission_allowed_id, review_allowed_id, etc.)&lt;br /&gt;
  # 2. Whether the participant has the necessary permissions (can_submit, can_review, can_take_quiz fields)&lt;br /&gt;
&lt;br /&gt;
  # Check if submission is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_submit?(participant = nil)&lt;br /&gt;
    return false unless submission_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_submit&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: submission_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if review is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_review?(participant = nil)&lt;br /&gt;
    return false unless review_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_review&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: review_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if taking a quiz is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_take_quiz?(participant = nil)&lt;br /&gt;
    return false unless quiz_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_take_quiz&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: quiz_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if teammate review is allowed&lt;br /&gt;
  def can_review_teammate?(participant = nil)&lt;br /&gt;
    return false unless teammate_review_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_review&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: teammate_review_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Generic permission checker&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    permission_field = &amp;quot;#{activity}_allowed_id&amp;quot;&lt;br /&gt;
    return false unless respond_to?(permission_field)&lt;br /&gt;
&lt;br /&gt;
    allowed_id = public_send(permission_field)&lt;br /&gt;
    return false unless allowed_id&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get permission status for an action (OK, Late, No)&lt;br /&gt;
  def permission_status_for(action)&lt;br /&gt;
    permission_field = &amp;quot;#{action}_allowed_id&amp;quot;&lt;br /&gt;
    return 'No' unless respond_to?(permission_field)&lt;br /&gt;
&lt;br /&gt;
    allowed_id = public_send(permission_field)&lt;br /&gt;
    return 'No' unless allowed_id&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name || 'No'&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This module is included in the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== DueDateActions Mixin ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module provides deadline-related operations for models that have due dates (Assignment, SignUpTopic).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDateActions&lt;br /&gt;
  # Generic activity permission checker that determines if an activity is permissible&lt;br /&gt;
  # based on the current deadline state for this parent object&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    current_deadline = next_due_date()&lt;br /&gt;
    return false unless current_deadline&lt;br /&gt;
&lt;br /&gt;
    current_deadline.activity_permissible?(activity)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Syntactic sugar methods for common activities&lt;br /&gt;
  def submission_permissible?&lt;br /&gt;
    activity_permissible?(:submission)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def review_permissible?&lt;br /&gt;
    activity_permissible?(:review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def teammate_review_permissible?&lt;br /&gt;
    activity_permissible?(:teammate_review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def quiz_permissible?&lt;br /&gt;
    activity_permissible?(:quiz)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def team_formation_permissible?&lt;br /&gt;
    activity_permissible?(:team_formation)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def signup_permissible?&lt;br /&gt;
    activity_permissible?(:signup)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def drop_topic_permissible?&lt;br /&gt;
    activity_permissible?(:drop_topic)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the next due date for this assignment&lt;br /&gt;
  def next_due_date(topic_id = nil)&lt;br /&gt;
    # If a topic is specified and this assignment has topic-specific deadlines,&lt;br /&gt;
    # look for topic due dates first&lt;br /&gt;
    if topic_id &amp;amp;&amp;amp; has_topic_specific_deadlines?&lt;br /&gt;
      topic_deadline = due_dates.where(parent_id: topic_id, parent_type: 'SignUpTopic')&lt;br /&gt;
                               .upcoming&lt;br /&gt;
                               .first&lt;br /&gt;
      return topic_deadline if topic_deadline&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Fall back to assignment-level due dates&lt;br /&gt;
    due_dates.upcoming.first&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the current stage name for display purposes&lt;br /&gt;
  def current_stage&lt;br /&gt;
    deadline = next_due_date()&lt;br /&gt;
    return 'finished' unless deadline&lt;br /&gt;
&lt;br /&gt;
    deadline.deadline_type&amp;amp;.name || 'unknown'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if assignment has topic-specific deadlines&lt;br /&gt;
  def has_topic_specific_deadlines?&lt;br /&gt;
    staggered_deadline || due_dates.where(parent_type: 'SignUpTopic').exists?&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Copy due dates to a new parent object&lt;br /&gt;
  def copy_due_dates_to(new_parent)&lt;br /&gt;
    due_dates.find_each do |due_date|&lt;br /&gt;
      new_due_date = due_date.dup&lt;br /&gt;
      new_due_date.parent = new_parent&lt;br /&gt;
      new_due_date.save!&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This module is included in &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class Assignment &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
class SignUpTopic &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Implementation Summary ==&lt;br /&gt;
&lt;br /&gt;
=== Models Created/Modified ===&lt;br /&gt;
&lt;br /&gt;
* '''DeadlineType''' - Canonical deadline type definitions with semantic helper methods&lt;br /&gt;
* '''DeadlineRight''' - Permission state model (OK/Late/No) with comparison methods&lt;br /&gt;
* '''DueDate''' - Refactored with instance methods, scopes, and permission checking&lt;br /&gt;
* '''TopicDueDate''' - STI subclass for topic-specific deadlines&lt;br /&gt;
* '''Assignment''' - Includes DueDateActions mixin&lt;br /&gt;
* '''SignUpTopic''' - Includes DueDateActions mixin&lt;br /&gt;
&lt;br /&gt;
=== Modules Created ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions''' - Permission checking combining deadline and role constraints&lt;br /&gt;
* '''DueDateActions''' - Deadline-related operations for parent models (Assignment, SignUpTopic)&lt;br /&gt;
&lt;br /&gt;
=== Key Improvements ===&lt;br /&gt;
&lt;br /&gt;
# '''Separation of Concerns''': Permission logic separated into dedicated modules&lt;br /&gt;
# '''Instance Methods''': Replaced class methods with testable instance methods&lt;br /&gt;
# '''Polymorphic Design''': Single DueDate model serves both Assignment and SignUpTopic&lt;br /&gt;
# '''Unified Permissions''': Role-based and time-based checks combined in one interface&lt;br /&gt;
# '''Data Integrity''': Removed duplicate deadline types, enforced foreign key constraints&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
The test suite includes comprehensive RSpec tests for the ''DueDate'' model and its associated models (''DeadlineType'', ''DeadlineRight'').&lt;br /&gt;
These tests verify model validations, scopes, instance methods, class methods, and callbacks with extensive coverage of edge cases and error conditions.&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
The test suite follows RSpec best practices with:&lt;br /&gt;
* '''Nested contexts''' for different scenarios (e.g., &amp;quot;when parent is missing&amp;quot;, &amp;quot;when round is 0&amp;quot;)&lt;br /&gt;
* '''Multiple assertions per context''' to verify behavior from different angles&lt;br /&gt;
* '''Edge case coverage''' including empty collections, nil values, and boundary conditions&lt;br /&gt;
* '''Error case testing''' for invalid data and non-existent records&lt;br /&gt;
* '''Clear descriptive names''' using &amp;quot;context 'when...'&amp;quot; and &amp;quot;it 'does something'&amp;quot; patterns&lt;br /&gt;
&lt;br /&gt;
=== Current Coverage ===&lt;br /&gt;
&lt;br /&gt;
==== Associations ====&lt;br /&gt;
* Tests verify &amp;lt;code&amp;gt;belongs_to :parent&amp;lt;/code&amp;gt; (polymorphic) and &amp;lt;code&amp;gt;belongs_to :deadline_type&amp;lt;/code&amp;gt; relationships&lt;br /&gt;
&lt;br /&gt;
==== Validations ====&lt;br /&gt;
* '''Required field validation''': Tests for parent, due_at, and deadline_type_id presence&lt;br /&gt;
* '''Round validation''': Tests for positive values, zero rejection, negative rejection, and nil handling&lt;br /&gt;
* '''Error messages''': Verifies specific error messages are added to appropriate fields&lt;br /&gt;
* '''Valid record creation''': Confirms records persist when all requirements are met&lt;br /&gt;
&lt;br /&gt;
==== Scopes ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;''': Returns future due dates ordered by due_at, excludes past dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;''': Returns past due dates ordered by due_at, excludes future dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;''': Filters by round number, handles non-existent rounds&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt;''': Filters by deadline type name, handles non-existent types&lt;br /&gt;
&lt;br /&gt;
==== Instance Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#overdue?&amp;lt;/code&amp;gt;''': Tests past dates (true), future dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#upcoming?&amp;lt;/code&amp;gt;''': Tests future dates (true), past dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#set&amp;lt;/code&amp;gt;''': Verifies updating deadline_type_id, parent_id, and round with persistence and error handling&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#copy&amp;lt;/code&amp;gt;''': Tests duplication to new assignment, attribute preservation, error handling for non-existent targets&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#deadline_type_name&amp;lt;/code&amp;gt;''': Returns associated deadline type name, handles nil cases&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#last_deadline?&amp;lt;/code&amp;gt;''': Tests detection of final deadline, handles multiple deadlines and single deadline scenarios&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#&amp;lt;=&amp;gt;&amp;lt;/code&amp;gt;''': Tests comparison by due_at time, handles non-DueDate objects&lt;br /&gt;
&lt;br /&gt;
==== Class Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.sort_due_dates&amp;lt;/code&amp;gt;''': Sorts by due_at ascending, handles empty arrays and single elements&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.any_future_due_dates?&amp;lt;/code&amp;gt;''': Detects future dates in collections, handles empty arrays and mixed date collections&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.copy&amp;lt;/code&amp;gt;''': Copies all due dates between assignments, preserves attributes, handles empty sources and non-existent records&lt;br /&gt;
&lt;br /&gt;
==== Callbacks ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;before_save :set_default_round&amp;lt;/code&amp;gt;''': Sets round to 1 when not specified, preserves explicit values, handles nil&lt;br /&gt;
&lt;br /&gt;
=== Test Examples ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Validation with multiple assertions&lt;br /&gt;
describe 'validations' do&lt;br /&gt;
  context 'when parent is missing' do&lt;br /&gt;
    let(:due_date) do&lt;br /&gt;
      DueDate.new(&lt;br /&gt;
        parent: nil,&lt;br /&gt;
        due_at: 2.days.from_now,&lt;br /&gt;
        deadline_type: submission_type,&lt;br /&gt;
        submission_allowed_id: ok_right.id,&lt;br /&gt;
        review_allowed_id: ok_right.id&lt;br /&gt;
      )&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'is invalid' do&lt;br /&gt;
      expect(due_date).to be_invalid&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'has error on parent field' do&lt;br /&gt;
      due_date.valid?&lt;br /&gt;
      expect(due_date.errors[:parent]).to include('must exist')&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when round validation' do&lt;br /&gt;
    context 'with round value of 0' do&lt;br /&gt;
      let(:due_date) do&lt;br /&gt;
        DueDate.new(&lt;br /&gt;
          parent: assignment,&lt;br /&gt;
          due_at: 2.days.from_now,&lt;br /&gt;
          deadline_type: submission_type,&lt;br /&gt;
          submission_allowed_id: ok_right.id,&lt;br /&gt;
          review_allowed_id: ok_right.id,&lt;br /&gt;
          round: 0&lt;br /&gt;
        )&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'is invalid' do&lt;br /&gt;
        expect(due_date).to be_invalid&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'has error on round field' do&lt;br /&gt;
        due_date.valid?&lt;br /&gt;
        expect(due_date.errors[:round]).to be_present&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'with nil round' do&lt;br /&gt;
      let(:due_date) do&lt;br /&gt;
        DueDate.create(&lt;br /&gt;
          parent: assignment,&lt;br /&gt;
          due_at: 2.days.from_now,&lt;br /&gt;
          deadline_type: submission_type,&lt;br /&gt;
          submission_allowed_id: ok_right.id,&lt;br /&gt;
          review_allowed_id: ok_right.id,&lt;br /&gt;
          round: nil&lt;br /&gt;
        )&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'is valid' do&lt;br /&gt;
        expect(due_date).to be_valid&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'sets default round to 1' do&lt;br /&gt;
        expect(due_date.round).to eq(1)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
# Scope with edge cases&lt;br /&gt;
describe '.upcoming' do&lt;br /&gt;
  it 'returns only future due dates' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).to contain_exactly(upcoming_due_date1, upcoming_due_date2)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'orders results by due_at ascending' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).to eq([upcoming_due_date1, upcoming_due_date2])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'excludes past due dates' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).not_to include(past_due_date)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when all due dates are in the past' do&lt;br /&gt;
    before do&lt;br /&gt;
      DueDate.destroy_all&lt;br /&gt;
      DueDate.create(&lt;br /&gt;
        parent: assignment,&lt;br /&gt;
        due_at: 3.days.ago,&lt;br /&gt;
        deadline_type: submission_type,&lt;br /&gt;
        submission_allowed_id: ok_right.id,&lt;br /&gt;
        review_allowed_id: ok_right.id&lt;br /&gt;
      )&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns empty collection' do&lt;br /&gt;
      expect(DueDate.upcoming).to be_empty&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
# Instance method with error handling&lt;br /&gt;
describe '#copy' do&lt;br /&gt;
  let(:original) do&lt;br /&gt;
    DueDate.create(&lt;br /&gt;
      parent: assignment,&lt;br /&gt;
      due_at: 2.days.from_now,&lt;br /&gt;
      deadline_type: submission_type,&lt;br /&gt;
      submission_allowed_id: ok_right.id,&lt;br /&gt;
      review_allowed_id: late_right.id,&lt;br /&gt;
      round: 1&lt;br /&gt;
    )&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  let(:copied) { original.copy(assignment2.id) }&lt;br /&gt;
&lt;br /&gt;
  it 'creates a new persisted record' do&lt;br /&gt;
    expect(copied).to be_persisted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'has different id from original' do&lt;br /&gt;
    expect(copied.id).not_to eq(original.id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'preserves all attributes' do&lt;br /&gt;
    expect(copied.due_at).to eq(original.due_at)&lt;br /&gt;
    expect(copied.deadline_type_id).to eq(original.deadline_type_id)&lt;br /&gt;
    expect(copied.round).to eq(original.round)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when copying to non-existent assignment' do&lt;br /&gt;
    it 'raises error' do&lt;br /&gt;
      expect do&lt;br /&gt;
        original.copy(99999)&lt;br /&gt;
      end.to raise_error(ActiveRecord::RecordNotFound)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Test Metrics ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDate model''': 60+ examples covering all methods and edge cases&lt;br /&gt;
* '''Context depth''': 2-3 levels for complex scenarios&lt;br /&gt;
* '''Code coverage''': 100% coverage of DueDate model methods&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
=== Demo Link ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add deadline notification system using the new permission framework&lt;br /&gt;
* Implement deadline templates for common assignment patterns&lt;br /&gt;
* Add API endpoints for mobile app integration&lt;br /&gt;
* Create admin dashboard for monitoring deadline compliance across courses&lt;br /&gt;
* Add automated deadline extension workflows based on configurable rules&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167192</id>
		<title>CSC/ECE 517 Fall 2025 - E2566. Finish DueDates</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167192"/>
		<updated>2025-12-02T04:04:31Z</updated>

		<summary type="html">&lt;p&gt;Skim253: /* Proposed Solution */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
This project aims to complete the implementation of the DueDate system in Expertiza. The current implementation consists mostly of class methods and lacks proper definition of different deadline types and permission checking functionality. This redesign will create a more robust, readable, and maintainable due date system.&lt;br /&gt;
&lt;br /&gt;
=== Existing Components ===&lt;br /&gt;
* &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model with polymorphic parent association (Assignment/SignUpTopic)&lt;br /&gt;
* &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; subclasses&lt;br /&gt;
* Basic CRUD operations (within &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AssignmentForm&amp;lt;/code&amp;gt;) and sorting functionality (&amp;lt;code&amp;gt;deadline_sort&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Database schema with &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt; field&lt;br /&gt;
&lt;br /&gt;
=== Current Behavior ===&lt;br /&gt;
&lt;br /&gt;
====Admin's View====&lt;br /&gt;
[[File:Duedates.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* When editing an assignment, due dates can be modified under the Due Dates tab.&lt;br /&gt;
* Each row contains the following columns: &amp;lt;code&amp;gt;Date &amp;amp; Time&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Use Date Updater&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Submission Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Teammate Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Meta-Review Allowed&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Reminder&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:Assignments-CurrentStage.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* In the Assignments table, the ''DueDate'' of each assignment is displayed as Current Stage and Stage Deadline.&lt;br /&gt;
&lt;br /&gt;
====Student's View====&lt;br /&gt;
[[File:Student-assignments.png|600px]]&lt;br /&gt;
* Students can view the due date information under &amp;lt;code&amp;gt;Current State&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Stage Deadline&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
* The actions available to students are determined by the current DueDate status.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-not-over.png|600px]]&lt;br /&gt;
* For example, during the submission period, students can access the &amp;lt;code&amp;gt;Submit a hyperlink&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Submit a file&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-over.png|600px]]&lt;br /&gt;
* Once the due date has passed, students cannot submit either a hyperlink or a file.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Currently, there are some glaring issues with how due_dates was created in the first place, including redundancy and lack of modularity.&lt;br /&gt;
&lt;br /&gt;
Modeling &amp;amp; Structural Issues&lt;br /&gt;
# No dedicated &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to define different kinds of deadlines. The existing &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; class only serves as an association for &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; models, and is never referenced in the current &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; implementation.&lt;br /&gt;
# Duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table, which may lead to potential bugs.&lt;br /&gt;
# Incomplete deadline type definitions - &amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt; need to be added.&lt;br /&gt;
# No clear separation of concerns - data persistence, business logic, and permission logic are mixed in a single model.&lt;br /&gt;
&lt;br /&gt;
Permission &amp;amp; Behavior Issues&lt;br /&gt;
# Overuse of class methods making the code difficult to maintain.&lt;br /&gt;
# Missing permission checking logic for user actions.&lt;br /&gt;
&lt;br /&gt;
=== Tasks Identified ===&lt;br /&gt;
&lt;br /&gt;
Modeling &amp;amp; Structural Issues&lt;br /&gt;
# Refactor the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model to separate persistence, business logic, and permission logic into distinct layers.&lt;br /&gt;
# Implement reusable mixins (e.g., &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;) to handle permission evaluation and deadline-related operations.&lt;br /&gt;
# Introduce instance-level methods (e.g., &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;) to replace class-level utilities and improve testability.&lt;br /&gt;
&lt;br /&gt;
Permission &amp;amp; Behavior Issues&lt;br /&gt;
# Integrate role-based and time-based permission checks within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; to ensure consistent access control.&lt;br /&gt;
# Redesign the &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.&lt;br /&gt;
# Add missing deadline types (&amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt;) and remove duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries for data integrity.&lt;br /&gt;
&lt;br /&gt;
== Proposed Solution ==&lt;br /&gt;
The proposed solution restructures the due date system by separating concerns across dedicated models and mixin modules.&lt;br /&gt;
Deadline definitions (&amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt;), core deadline data (&amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;), and behavioral logic (&amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;) are isolated so each component has a single clear responsibility.&lt;br /&gt;
This modular design improves readability, reduces coupling, and makes deadline-related behavior easier to extend and maintain.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[File:Duedate-diagram.png|600px]]&lt;br /&gt;
&lt;br /&gt;
This diagram visualizes the redesigned architecture: blue/red/green indicate Rails models, orange indicates modules. &lt;br /&gt;
Parent models call DueDateActions, which manages DueDate, while DueDatePermissions checks user rights using reference models. &lt;br /&gt;
This separation clarifies responsibilities and improves modularity.&lt;br /&gt;
&lt;br /&gt;
=== DeadlineType Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Although the current codebase includes a &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model, it only serves as a passive lookup table for associations with &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt;.  &lt;br /&gt;
In practice, most parts of the system still rely on hard-coded numeric IDs or manual lookups such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Example of current usage&lt;br /&gt;
deadline_type_id = DeadlineType.find_by(name: 'review').id&lt;br /&gt;
if due_date.deadline_type_id == deadline_type_id&lt;br /&gt;
  # Perform review-related logic&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of using the model as an active domain object, &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; directly compares IDs or string literals.  &lt;br /&gt;
This leads to several structural issues:&lt;br /&gt;
&lt;br /&gt;
* Inconsistent references (some use IDs, others strings)&lt;br /&gt;
* Tight coupling between &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
* Lack of helper semantics (e.g., &amp;lt;code&amp;gt;is_review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;is_submission?&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Data duplication and integrity risks (two &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; rows)&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; will serve as a source of truth, defining all valid deadline types and providing meaningful interfaces.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE deadline_types (&lt;br /&gt;
  id BIGINT PRIMARY KEY,&lt;br /&gt;
  name VARCHAR(255) NOT NULL UNIQUE,&lt;br /&gt;
  description TEXT NOT NULL,&lt;br /&gt;
  created_at TIMESTAMP,&lt;br /&gt;
  updated_at TIMESTAMP&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Deadline Type Definitions ====&lt;br /&gt;
* &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;: submission - Student work submission deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt;: review - Peer review deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;3&amp;lt;/code&amp;gt;: teammate_review - Team member evaluation deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;5&amp;lt;/code&amp;gt;: metareview - Meta-review deadlines (kept for backward compatibility)  &lt;br /&gt;
* &amp;lt;code&amp;gt;6&amp;lt;/code&amp;gt;: drop_topic - Topic drop deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;7&amp;lt;/code&amp;gt;: signup - Course/assignment signup deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;8&amp;lt;/code&amp;gt;: team_formation - Team formation deadlines (clean up duplicate ID 10)  &lt;br /&gt;
* &amp;lt;code&amp;gt;11&amp;lt;/code&amp;gt;: quiz - Quiz completion deadlines&lt;br /&gt;
&lt;br /&gt;
==== DeadlineType Model Implementation ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineType &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
  validates :description, presence: true&lt;br /&gt;
&lt;br /&gt;
  has_many :due_dates, foreign_key: :deadline_type_id, dependent: :restrict_with_exception&lt;br /&gt;
&lt;br /&gt;
  # Semantic helper methods for deadline type identification&lt;br /&gt;
  def submission?&lt;br /&gt;
    name == 'submission'&lt;br /&gt;
  end&lt;br /&gt;
  # same code for review, teammate_review, quiz, team_formation, signup, drop_topic   &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DeadlineRight Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model represents the permission level for a specific action at a given deadline.&lt;br /&gt;
It defines three states: &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt; (action allowed), &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt; (action allowed with penalty), and &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt; (action denied).&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE deadline_rights (&lt;br /&gt;
  id BIGINT PRIMARY KEY,&lt;br /&gt;
  name VARCHAR(255) NOT NULL UNIQUE,&lt;br /&gt;
  created_at TIMESTAMP,&lt;br /&gt;
  updated_at TIMESTAMP&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== DeadlineRight Model ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineRight &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
&lt;br /&gt;
  # Permission checking methods&lt;br /&gt;
  def allows_action?&lt;br /&gt;
    %w[OK Late].include?(name)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def denies_action?&lt;br /&gt;
    name == 'No'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def allows_with_penalty?&lt;br /&gt;
    name == 'Late'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def allows_without_penalty?&lt;br /&gt;
    name == 'OK'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Display methods&lt;br /&gt;
  def to_s&lt;br /&gt;
    name&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def permission_level&lt;br /&gt;
    case name&lt;br /&gt;
    when 'No'   then 0&lt;br /&gt;
    when 'Late' then 1&lt;br /&gt;
    when 'OK'   then 2&lt;br /&gt;
    else -1&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== DueDate Model Refactoring ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model currently mixes persistence logic, deadline calculation, and permission checking, violating SRP.  &lt;br /&gt;
Most of its logic is implemented as class methods, which makes testing and extension difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Current design&lt;br /&gt;
def self.copy(old_assignment_id, new_assignment_id)&lt;br /&gt;
  duedates = where(parent_id: old_assignment_id)&lt;br /&gt;
  duedates.each do |orig_due_date|&lt;br /&gt;
    new_due_date = orig_due_date.dup&lt;br /&gt;
    new_due_date.parent_id = new_assignment_id&lt;br /&gt;
    new_due_date.save&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Improved Design ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
&lt;br /&gt;
  belongs_to :parent, polymorphic: true&lt;br /&gt;
  belongs_to :deadline_type, foreign_key: :deadline_type_id&lt;br /&gt;
&lt;br /&gt;
  validates :due_at, presence: true&lt;br /&gt;
  validates :deadline_type_id, presence: true&lt;br /&gt;
  validates :parent, presence: true&lt;br /&gt;
  validates :round, numericality: { greater_than: 0 }, allow_nil: true&lt;br /&gt;
&lt;br /&gt;
  # Scopes for common queries&lt;br /&gt;
  scope :upcoming, -&amp;gt; { where('due_at &amp;gt; ?', Time.current).order(:due_at) }&lt;br /&gt;
  scope :overdue, -&amp;gt; { where('due_at &amp;lt; ?', Time.current).order(:due_at) }&lt;br /&gt;
  scope :for_round, -&amp;gt;(round_num) { where(round: round_num) }&lt;br /&gt;
  scope :for_deadline_type, -&amp;gt;(type_name) { joins(:deadline_type).where(deadline_types: { name: type_name }) }&lt;br /&gt;
&lt;br /&gt;
  # Instance methods replace class methods&lt;br /&gt;
  def copy(to_assignment_id)&lt;br /&gt;
    to_assignment = Assignment.find(to_assignment_id)&lt;br /&gt;
    new_due_date = dup&lt;br /&gt;
    new_due_date.parent = to_assignment&lt;br /&gt;
    new_due_date.save!&lt;br /&gt;
    new_due_date&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def set(deadline_type_id, parent_id, round)&lt;br /&gt;
    self.deadline_type_id = deadline_type_id&lt;br /&gt;
    self.parent_id = parent_id&lt;br /&gt;
    self.round = round&lt;br /&gt;
    save!&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if this deadline has passed&lt;br /&gt;
  def overdue?&lt;br /&gt;
    due_at &amp;lt; Time.current&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if this deadline is upcoming&lt;br /&gt;
  def upcoming?&lt;br /&gt;
    due_at &amp;gt; Time.current&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the deadline type name&lt;br /&gt;
  def deadline_type_name&lt;br /&gt;
    deadline_type&amp;amp;.name&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Comparison method for sorting&lt;br /&gt;
  def &amp;lt;=&amp;gt;(other)&lt;br /&gt;
    return nil unless other.is_a?(DueDate)&lt;br /&gt;
    due_at &amp;lt;=&amp;gt; other.due_at&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Class methods for collection operations&lt;br /&gt;
  class &amp;lt;&amp;lt; self&lt;br /&gt;
    def sort_due_dates(due_dates)&lt;br /&gt;
      due_dates.sort_by(&amp;amp;:due_at)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    def any_future_due_dates?(due_dates)&lt;br /&gt;
      due_dates.any?(&amp;amp;:upcoming?)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    def copy(from_assignment_id, to_assignment_id)&lt;br /&gt;
      from_assignment = Assignment.find(from_assignment_id)&lt;br /&gt;
      to_assignment = Assignment.find(to_assignment_id)&lt;br /&gt;
&lt;br /&gt;
      from_assignment.due_dates.each do |due_date|&lt;br /&gt;
        new_due_date = due_date.dup&lt;br /&gt;
        new_due_date.parent = to_assignment&lt;br /&gt;
        new_due_date.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  before_save :set_default_round&lt;br /&gt;
&lt;br /&gt;
  def set_default_round&lt;br /&gt;
    self.round ||= 1&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Permission-Checking Methods ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Currently, permission checks depend only on user roles and ignore the actual deadline.  &lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Role-based permissions (participant_helpers.rb)&lt;br /&gt;
def participant_permissions(authorization)&lt;br /&gt;
  case authorization&lt;br /&gt;
  when 'reader'   then { can_submit: false, can_review: true, can_take_quiz: true }&lt;br /&gt;
  when 'reviewer' then { can_submit: false, can_review: true, can_take_quiz: false }&lt;br /&gt;
  when 'submitter' then { can_submit: true, can_review: false, can_take_quiz: false }&lt;br /&gt;
  else { can_submit: true, can_review: true, can_take_quiz: true }&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And deadline-based checks exist separately in the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def submission_allowed(topic_id = nil)&lt;br /&gt;
  check_condition('submission_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
def can_review(topic_id = nil)&lt;br /&gt;
  check_condition('review_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These two layers (role-based and time-based) never interact, leading to inconsistencies (e.g., a user with &amp;lt;code&amp;gt;can_submit=true&amp;lt;/code&amp;gt; after deadline).&lt;br /&gt;
&lt;br /&gt;
==== Proposed Changes ====&lt;br /&gt;
&lt;br /&gt;
The new &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module integrates role-based and deadline-based checks:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDatePermissions&lt;br /&gt;
  # Permission checking methods that combine deadline-based and role-based logic&lt;br /&gt;
  #&lt;br /&gt;
  # These methods must check both:&lt;br /&gt;
  # 1. Whether the action is permitted by the current deadline (submission_allowed_id, review_allowed_id, etc.)&lt;br /&gt;
  # 2. Whether the participant has the necessary permissions (can_submit, can_review, can_take_quiz fields)&lt;br /&gt;
&lt;br /&gt;
  # Check if submission is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_submit?(participant = nil)&lt;br /&gt;
    return false unless submission_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_submit&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: submission_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if review is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_review?(participant = nil)&lt;br /&gt;
    return false unless review_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_review&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: review_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if taking a quiz is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_take_quiz?(participant = nil)&lt;br /&gt;
    return false unless quiz_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_take_quiz&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: quiz_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if teammate review is allowed&lt;br /&gt;
  def can_review_teammate?(participant = nil)&lt;br /&gt;
    return false unless teammate_review_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_review&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: teammate_review_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Generic permission checker&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    permission_field = &amp;quot;#{activity}_allowed_id&amp;quot;&lt;br /&gt;
    return false unless respond_to?(permission_field)&lt;br /&gt;
&lt;br /&gt;
    allowed_id = public_send(permission_field)&lt;br /&gt;
    return false unless allowed_id&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get permission status for an action (OK, Late, No)&lt;br /&gt;
  def permission_status_for(action)&lt;br /&gt;
    permission_field = &amp;quot;#{action}_allowed_id&amp;quot;&lt;br /&gt;
    return 'No' unless respond_to?(permission_field)&lt;br /&gt;
&lt;br /&gt;
    allowed_id = public_send(permission_field)&lt;br /&gt;
    return 'No' unless allowed_id&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name || 'No'&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This module is included in the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== DueDateActions Mixin ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module provides deadline-related operations for models that have due dates (Assignment, SignUpTopic).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDateActions&lt;br /&gt;
  # Generic activity permission checker that determines if an activity is permissible&lt;br /&gt;
  # based on the current deadline state for this parent object&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    current_deadline = next_due_date()&lt;br /&gt;
    return false unless current_deadline&lt;br /&gt;
&lt;br /&gt;
    current_deadline.activity_permissible?(activity)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Syntactic sugar methods for common activities&lt;br /&gt;
  def submission_permissible?&lt;br /&gt;
    activity_permissible?(:submission)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def review_permissible?&lt;br /&gt;
    activity_permissible?(:review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def teammate_review_permissible?&lt;br /&gt;
    activity_permissible?(:teammate_review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def quiz_permissible?&lt;br /&gt;
    activity_permissible?(:quiz)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def team_formation_permissible?&lt;br /&gt;
    activity_permissible?(:team_formation)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def signup_permissible?&lt;br /&gt;
    activity_permissible?(:signup)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def drop_topic_permissible?&lt;br /&gt;
    activity_permissible?(:drop_topic)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the next due date for this assignment&lt;br /&gt;
  def next_due_date(topic_id = nil)&lt;br /&gt;
    # If a topic is specified and this assignment has topic-specific deadlines,&lt;br /&gt;
    # look for topic due dates first&lt;br /&gt;
    if topic_id &amp;amp;&amp;amp; has_topic_specific_deadlines?&lt;br /&gt;
      topic_deadline = due_dates.where(parent_id: topic_id, parent_type: 'SignUpTopic')&lt;br /&gt;
                               .upcoming&lt;br /&gt;
                               .first&lt;br /&gt;
      return topic_deadline if topic_deadline&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Fall back to assignment-level due dates&lt;br /&gt;
    due_dates.upcoming.first&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the current stage name for display purposes&lt;br /&gt;
  def current_stage&lt;br /&gt;
    deadline = next_due_date()&lt;br /&gt;
    return 'finished' unless deadline&lt;br /&gt;
&lt;br /&gt;
    deadline.deadline_type&amp;amp;.name || 'unknown'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if assignment has topic-specific deadlines&lt;br /&gt;
  def has_topic_specific_deadlines?&lt;br /&gt;
    staggered_deadline || due_dates.where(parent_type: 'SignUpTopic').exists?&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Copy due dates to a new parent object&lt;br /&gt;
  def copy_due_dates_to(new_parent)&lt;br /&gt;
    due_dates.find_each do |due_date|&lt;br /&gt;
      new_due_date = due_date.dup&lt;br /&gt;
      new_due_date.parent = new_parent&lt;br /&gt;
      new_due_date.save!&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This module is included in &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class Assignment &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
class SignUpTopic &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Implementation Summary ==&lt;br /&gt;
&lt;br /&gt;
=== Models Created/Modified ===&lt;br /&gt;
&lt;br /&gt;
* '''DeadlineType''' - Canonical deadline type definitions with semantic helper methods&lt;br /&gt;
* '''DeadlineRight''' - Permission state model (OK/Late/No) with comparison methods&lt;br /&gt;
* '''DueDate''' - Refactored with instance methods, scopes, and permission checking&lt;br /&gt;
* '''TopicDueDate''' - STI subclass for topic-specific deadlines&lt;br /&gt;
* '''Assignment''' - Includes DueDateActions mixin&lt;br /&gt;
* '''SignUpTopic''' - Includes DueDateActions mixin&lt;br /&gt;
&lt;br /&gt;
=== Modules Created ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions''' - Permission checking combining deadline and role constraints&lt;br /&gt;
* '''DueDateActions''' - Deadline-related operations for parent models (Assignment, SignUpTopic)&lt;br /&gt;
&lt;br /&gt;
=== Key Improvements ===&lt;br /&gt;
&lt;br /&gt;
# '''Separation of Concerns''': Permission logic separated into dedicated modules&lt;br /&gt;
# '''Instance Methods''': Replaced class methods with testable instance methods&lt;br /&gt;
# '''Polymorphic Design''': Single DueDate model serves both Assignment and SignUpTopic&lt;br /&gt;
# '''Unified Permissions''': Role-based and time-based checks combined in one interface&lt;br /&gt;
# '''Data Integrity''': Removed duplicate deadline types, enforced foreign key constraints&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
The test suite includes comprehensive RSpec tests for the ''DueDate'' model and its associated models (''DeadlineType'', ''DeadlineRight'').&lt;br /&gt;
These tests verify model validations, scopes, instance methods, class methods, and callbacks with extensive coverage of edge cases and error conditions.&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
The test suite follows RSpec best practices with:&lt;br /&gt;
* '''Nested contexts''' for different scenarios (e.g., &amp;quot;when parent is missing&amp;quot;, &amp;quot;when round is 0&amp;quot;)&lt;br /&gt;
* '''Multiple assertions per context''' to verify behavior from different angles&lt;br /&gt;
* '''Edge case coverage''' including empty collections, nil values, and boundary conditions&lt;br /&gt;
* '''Error case testing''' for invalid data and non-existent records&lt;br /&gt;
* '''Clear descriptive names''' using &amp;quot;context 'when...'&amp;quot; and &amp;quot;it 'does something'&amp;quot; patterns&lt;br /&gt;
&lt;br /&gt;
=== Current Coverage ===&lt;br /&gt;
&lt;br /&gt;
==== Associations ====&lt;br /&gt;
* Tests verify &amp;lt;code&amp;gt;belongs_to :parent&amp;lt;/code&amp;gt; (polymorphic) and &amp;lt;code&amp;gt;belongs_to :deadline_type&amp;lt;/code&amp;gt; relationships&lt;br /&gt;
&lt;br /&gt;
==== Validations ====&lt;br /&gt;
* '''Required field validation''': Tests for parent, due_at, and deadline_type_id presence&lt;br /&gt;
* '''Round validation''': Tests for positive values, zero rejection, negative rejection, and nil handling&lt;br /&gt;
* '''Error messages''': Verifies specific error messages are added to appropriate fields&lt;br /&gt;
* '''Valid record creation''': Confirms records persist when all requirements are met&lt;br /&gt;
&lt;br /&gt;
==== Scopes ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;''': Returns future due dates ordered by due_at, excludes past dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;''': Returns past due dates ordered by due_at, excludes future dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;''': Filters by round number, handles non-existent rounds&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt;''': Filters by deadline type name, handles non-existent types&lt;br /&gt;
&lt;br /&gt;
==== Instance Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#overdue?&amp;lt;/code&amp;gt;''': Tests past dates (true), future dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#upcoming?&amp;lt;/code&amp;gt;''': Tests future dates (true), past dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#set&amp;lt;/code&amp;gt;''': Verifies updating deadline_type_id, parent_id, and round with persistence and error handling&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#copy&amp;lt;/code&amp;gt;''': Tests duplication to new assignment, attribute preservation, error handling for non-existent targets&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#deadline_type_name&amp;lt;/code&amp;gt;''': Returns associated deadline type name, handles nil cases&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#last_deadline?&amp;lt;/code&amp;gt;''': Tests detection of final deadline, handles multiple deadlines and single deadline scenarios&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#&amp;lt;=&amp;gt;&amp;lt;/code&amp;gt;''': Tests comparison by due_at time, handles non-DueDate objects&lt;br /&gt;
&lt;br /&gt;
==== Class Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.sort_due_dates&amp;lt;/code&amp;gt;''': Sorts by due_at ascending, handles empty arrays and single elements&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.any_future_due_dates?&amp;lt;/code&amp;gt;''': Detects future dates in collections, handles empty arrays and mixed date collections&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.copy&amp;lt;/code&amp;gt;''': Copies all due dates between assignments, preserves attributes, handles empty sources and non-existent records&lt;br /&gt;
&lt;br /&gt;
==== Callbacks ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;before_save :set_default_round&amp;lt;/code&amp;gt;''': Sets round to 1 when not specified, preserves explicit values, handles nil&lt;br /&gt;
&lt;br /&gt;
=== Test Examples ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Validation with multiple assertions&lt;br /&gt;
describe 'validations' do&lt;br /&gt;
  context 'when parent is missing' do&lt;br /&gt;
    let(:due_date) do&lt;br /&gt;
      DueDate.new(&lt;br /&gt;
        parent: nil,&lt;br /&gt;
        due_at: 2.days.from_now,&lt;br /&gt;
        deadline_type: submission_type,&lt;br /&gt;
        submission_allowed_id: ok_right.id,&lt;br /&gt;
        review_allowed_id: ok_right.id&lt;br /&gt;
      )&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'is invalid' do&lt;br /&gt;
      expect(due_date).to be_invalid&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'has error on parent field' do&lt;br /&gt;
      due_date.valid?&lt;br /&gt;
      expect(due_date.errors[:parent]).to include('must exist')&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when round validation' do&lt;br /&gt;
    context 'with round value of 0' do&lt;br /&gt;
      let(:due_date) do&lt;br /&gt;
        DueDate.new(&lt;br /&gt;
          parent: assignment,&lt;br /&gt;
          due_at: 2.days.from_now,&lt;br /&gt;
          deadline_type: submission_type,&lt;br /&gt;
          submission_allowed_id: ok_right.id,&lt;br /&gt;
          review_allowed_id: ok_right.id,&lt;br /&gt;
          round: 0&lt;br /&gt;
        )&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'is invalid' do&lt;br /&gt;
        expect(due_date).to be_invalid&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'has error on round field' do&lt;br /&gt;
        due_date.valid?&lt;br /&gt;
        expect(due_date.errors[:round]).to be_present&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'with nil round' do&lt;br /&gt;
      let(:due_date) do&lt;br /&gt;
        DueDate.create(&lt;br /&gt;
          parent: assignment,&lt;br /&gt;
          due_at: 2.days.from_now,&lt;br /&gt;
          deadline_type: submission_type,&lt;br /&gt;
          submission_allowed_id: ok_right.id,&lt;br /&gt;
          review_allowed_id: ok_right.id,&lt;br /&gt;
          round: nil&lt;br /&gt;
        )&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'is valid' do&lt;br /&gt;
        expect(due_date).to be_valid&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'sets default round to 1' do&lt;br /&gt;
        expect(due_date.round).to eq(1)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
# Scope with edge cases&lt;br /&gt;
describe '.upcoming' do&lt;br /&gt;
  it 'returns only future due dates' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).to contain_exactly(upcoming_due_date1, upcoming_due_date2)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'orders results by due_at ascending' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).to eq([upcoming_due_date1, upcoming_due_date2])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'excludes past due dates' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).not_to include(past_due_date)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when all due dates are in the past' do&lt;br /&gt;
    before do&lt;br /&gt;
      DueDate.destroy_all&lt;br /&gt;
      DueDate.create(&lt;br /&gt;
        parent: assignment,&lt;br /&gt;
        due_at: 3.days.ago,&lt;br /&gt;
        deadline_type: submission_type,&lt;br /&gt;
        submission_allowed_id: ok_right.id,&lt;br /&gt;
        review_allowed_id: ok_right.id&lt;br /&gt;
      )&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns empty collection' do&lt;br /&gt;
      expect(DueDate.upcoming).to be_empty&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
# Instance method with error handling&lt;br /&gt;
describe '#copy' do&lt;br /&gt;
  let(:original) do&lt;br /&gt;
    DueDate.create(&lt;br /&gt;
      parent: assignment,&lt;br /&gt;
      due_at: 2.days.from_now,&lt;br /&gt;
      deadline_type: submission_type,&lt;br /&gt;
      submission_allowed_id: ok_right.id,&lt;br /&gt;
      review_allowed_id: late_right.id,&lt;br /&gt;
      round: 1&lt;br /&gt;
    )&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  let(:copied) { original.copy(assignment2.id) }&lt;br /&gt;
&lt;br /&gt;
  it 'creates a new persisted record' do&lt;br /&gt;
    expect(copied).to be_persisted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'has different id from original' do&lt;br /&gt;
    expect(copied.id).not_to eq(original.id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'preserves all attributes' do&lt;br /&gt;
    expect(copied.due_at).to eq(original.due_at)&lt;br /&gt;
    expect(copied.deadline_type_id).to eq(original.deadline_type_id)&lt;br /&gt;
    expect(copied.round).to eq(original.round)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when copying to non-existent assignment' do&lt;br /&gt;
    it 'raises error' do&lt;br /&gt;
      expect do&lt;br /&gt;
        original.copy(99999)&lt;br /&gt;
      end.to raise_error(ActiveRecord::RecordNotFound)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Test Metrics ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDate model''': 60+ examples covering all methods and edge cases&lt;br /&gt;
* '''Context depth''': 2-3 levels for complex scenarios&lt;br /&gt;
* '''Code coverage''': 100% coverage of DueDate model methods&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
=== Demo Link ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add deadline notification system using the new permission framework&lt;br /&gt;
* Implement deadline templates for common assignment patterns&lt;br /&gt;
* Add API endpoints for mobile app integration&lt;br /&gt;
* Create admin dashboard for monitoring deadline compliance across courses&lt;br /&gt;
* Add automated deadline extension workflows based on configurable rules&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167191</id>
		<title>CSC/ECE 517 Fall 2025 - E2566. Finish DueDates</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167191"/>
		<updated>2025-12-02T04:04:11Z</updated>

		<summary type="html">&lt;p&gt;Skim253: /* Proposed Solution */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
This project aims to complete the implementation of the DueDate system in Expertiza. The current implementation consists mostly of class methods and lacks proper definition of different deadline types and permission checking functionality. This redesign will create a more robust, readable, and maintainable due date system.&lt;br /&gt;
&lt;br /&gt;
=== Existing Components ===&lt;br /&gt;
* &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model with polymorphic parent association (Assignment/SignUpTopic)&lt;br /&gt;
* &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; subclasses&lt;br /&gt;
* Basic CRUD operations (within &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AssignmentForm&amp;lt;/code&amp;gt;) and sorting functionality (&amp;lt;code&amp;gt;deadline_sort&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Database schema with &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt; field&lt;br /&gt;
&lt;br /&gt;
=== Current Behavior ===&lt;br /&gt;
&lt;br /&gt;
====Admin's View====&lt;br /&gt;
[[File:Duedates.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* When editing an assignment, due dates can be modified under the Due Dates tab.&lt;br /&gt;
* Each row contains the following columns: &amp;lt;code&amp;gt;Date &amp;amp; Time&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Use Date Updater&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Submission Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Teammate Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Meta-Review Allowed&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Reminder&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:Assignments-CurrentStage.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* In the Assignments table, the ''DueDate'' of each assignment is displayed as Current Stage and Stage Deadline.&lt;br /&gt;
&lt;br /&gt;
====Student's View====&lt;br /&gt;
[[File:Student-assignments.png|600px]]&lt;br /&gt;
* Students can view the due date information under &amp;lt;code&amp;gt;Current State&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Stage Deadline&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
* The actions available to students are determined by the current DueDate status.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-not-over.png|600px]]&lt;br /&gt;
* For example, during the submission period, students can access the &amp;lt;code&amp;gt;Submit a hyperlink&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Submit a file&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-over.png|600px]]&lt;br /&gt;
* Once the due date has passed, students cannot submit either a hyperlink or a file.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Currently, there are some glaring issues with how due_dates was created in the first place, including redundancy and lack of modularity.&lt;br /&gt;
&lt;br /&gt;
Modeling &amp;amp; Structural Issues&lt;br /&gt;
# No dedicated &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to define different kinds of deadlines. The existing &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; class only serves as an association for &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; models, and is never referenced in the current &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; implementation.&lt;br /&gt;
# Duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table, which may lead to potential bugs.&lt;br /&gt;
# Incomplete deadline type definitions - &amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt; need to be added.&lt;br /&gt;
# No clear separation of concerns - data persistence, business logic, and permission logic are mixed in a single model.&lt;br /&gt;
&lt;br /&gt;
Permission &amp;amp; Behavior Issues&lt;br /&gt;
# Overuse of class methods making the code difficult to maintain.&lt;br /&gt;
# Missing permission checking logic for user actions.&lt;br /&gt;
&lt;br /&gt;
=== Tasks Identified ===&lt;br /&gt;
&lt;br /&gt;
Modeling &amp;amp; Structural Issues&lt;br /&gt;
# Refactor the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model to separate persistence, business logic, and permission logic into distinct layers.&lt;br /&gt;
# Implement reusable mixins (e.g., &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;) to handle permission evaluation and deadline-related operations.&lt;br /&gt;
# Introduce instance-level methods (e.g., &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;) to replace class-level utilities and improve testability.&lt;br /&gt;
&lt;br /&gt;
Permission &amp;amp; Behavior Issues&lt;br /&gt;
# Integrate role-based and time-based permission checks within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; to ensure consistent access control.&lt;br /&gt;
# Redesign the &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.&lt;br /&gt;
# Add missing deadline types (&amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt;) and remove duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries for data integrity.&lt;br /&gt;
&lt;br /&gt;
== Proposed Solution ==&lt;br /&gt;
The proposed solution restructures the due date system by separating concerns across dedicated models and mixin modules.&lt;br /&gt;
Deadline definitions (&amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt;), core deadline data (&amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt;), and behavioral logic (&amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;) are isolated so each component has a single clear responsibility.&lt;br /&gt;
This modular design improves readability, reduces coupling, and makes deadline-related behavior easier to extend and maintain.&lt;br /&gt;
&lt;br /&gt;
[[File:Duedate-diagram.png|600px]]&lt;br /&gt;
This diagram visualizes the redesigned architecture: blue/red/green indicate Rails models, orange indicates modules. &lt;br /&gt;
Parent models call DueDateActions, which manages DueDate, while DueDatePermissions checks user rights using reference models. &lt;br /&gt;
This separation clarifies responsibilities and improves modularity.&lt;br /&gt;
&lt;br /&gt;
=== DeadlineType Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Although the current codebase includes a &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model, it only serves as a passive lookup table for associations with &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt;.  &lt;br /&gt;
In practice, most parts of the system still rely on hard-coded numeric IDs or manual lookups such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Example of current usage&lt;br /&gt;
deadline_type_id = DeadlineType.find_by(name: 'review').id&lt;br /&gt;
if due_date.deadline_type_id == deadline_type_id&lt;br /&gt;
  # Perform review-related logic&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of using the model as an active domain object, &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; directly compares IDs or string literals.  &lt;br /&gt;
This leads to several structural issues:&lt;br /&gt;
&lt;br /&gt;
* Inconsistent references (some use IDs, others strings)&lt;br /&gt;
* Tight coupling between &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
* Lack of helper semantics (e.g., &amp;lt;code&amp;gt;is_review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;is_submission?&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Data duplication and integrity risks (two &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; rows)&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; will serve as a source of truth, defining all valid deadline types and providing meaningful interfaces.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE deadline_types (&lt;br /&gt;
  id BIGINT PRIMARY KEY,&lt;br /&gt;
  name VARCHAR(255) NOT NULL UNIQUE,&lt;br /&gt;
  description TEXT NOT NULL,&lt;br /&gt;
  created_at TIMESTAMP,&lt;br /&gt;
  updated_at TIMESTAMP&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Deadline Type Definitions ====&lt;br /&gt;
* &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;: submission - Student work submission deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt;: review - Peer review deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;3&amp;lt;/code&amp;gt;: teammate_review - Team member evaluation deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;5&amp;lt;/code&amp;gt;: metareview - Meta-review deadlines (kept for backward compatibility)  &lt;br /&gt;
* &amp;lt;code&amp;gt;6&amp;lt;/code&amp;gt;: drop_topic - Topic drop deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;7&amp;lt;/code&amp;gt;: signup - Course/assignment signup deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;8&amp;lt;/code&amp;gt;: team_formation - Team formation deadlines (clean up duplicate ID 10)  &lt;br /&gt;
* &amp;lt;code&amp;gt;11&amp;lt;/code&amp;gt;: quiz - Quiz completion deadlines&lt;br /&gt;
&lt;br /&gt;
==== DeadlineType Model Implementation ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineType &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
  validates :description, presence: true&lt;br /&gt;
&lt;br /&gt;
  has_many :due_dates, foreign_key: :deadline_type_id, dependent: :restrict_with_exception&lt;br /&gt;
&lt;br /&gt;
  # Semantic helper methods for deadline type identification&lt;br /&gt;
  def submission?&lt;br /&gt;
    name == 'submission'&lt;br /&gt;
  end&lt;br /&gt;
  # same code for review, teammate_review, quiz, team_formation, signup, drop_topic   &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DeadlineRight Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model represents the permission level for a specific action at a given deadline.&lt;br /&gt;
It defines three states: &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt; (action allowed), &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt; (action allowed with penalty), and &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt; (action denied).&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE deadline_rights (&lt;br /&gt;
  id BIGINT PRIMARY KEY,&lt;br /&gt;
  name VARCHAR(255) NOT NULL UNIQUE,&lt;br /&gt;
  created_at TIMESTAMP,&lt;br /&gt;
  updated_at TIMESTAMP&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== DeadlineRight Model ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineRight &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
&lt;br /&gt;
  # Permission checking methods&lt;br /&gt;
  def allows_action?&lt;br /&gt;
    %w[OK Late].include?(name)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def denies_action?&lt;br /&gt;
    name == 'No'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def allows_with_penalty?&lt;br /&gt;
    name == 'Late'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def allows_without_penalty?&lt;br /&gt;
    name == 'OK'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Display methods&lt;br /&gt;
  def to_s&lt;br /&gt;
    name&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def permission_level&lt;br /&gt;
    case name&lt;br /&gt;
    when 'No'   then 0&lt;br /&gt;
    when 'Late' then 1&lt;br /&gt;
    when 'OK'   then 2&lt;br /&gt;
    else -1&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== DueDate Model Refactoring ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model currently mixes persistence logic, deadline calculation, and permission checking, violating SRP.  &lt;br /&gt;
Most of its logic is implemented as class methods, which makes testing and extension difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Current design&lt;br /&gt;
def self.copy(old_assignment_id, new_assignment_id)&lt;br /&gt;
  duedates = where(parent_id: old_assignment_id)&lt;br /&gt;
  duedates.each do |orig_due_date|&lt;br /&gt;
    new_due_date = orig_due_date.dup&lt;br /&gt;
    new_due_date.parent_id = new_assignment_id&lt;br /&gt;
    new_due_date.save&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Improved Design ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
&lt;br /&gt;
  belongs_to :parent, polymorphic: true&lt;br /&gt;
  belongs_to :deadline_type, foreign_key: :deadline_type_id&lt;br /&gt;
&lt;br /&gt;
  validates :due_at, presence: true&lt;br /&gt;
  validates :deadline_type_id, presence: true&lt;br /&gt;
  validates :parent, presence: true&lt;br /&gt;
  validates :round, numericality: { greater_than: 0 }, allow_nil: true&lt;br /&gt;
&lt;br /&gt;
  # Scopes for common queries&lt;br /&gt;
  scope :upcoming, -&amp;gt; { where('due_at &amp;gt; ?', Time.current).order(:due_at) }&lt;br /&gt;
  scope :overdue, -&amp;gt; { where('due_at &amp;lt; ?', Time.current).order(:due_at) }&lt;br /&gt;
  scope :for_round, -&amp;gt;(round_num) { where(round: round_num) }&lt;br /&gt;
  scope :for_deadline_type, -&amp;gt;(type_name) { joins(:deadline_type).where(deadline_types: { name: type_name }) }&lt;br /&gt;
&lt;br /&gt;
  # Instance methods replace class methods&lt;br /&gt;
  def copy(to_assignment_id)&lt;br /&gt;
    to_assignment = Assignment.find(to_assignment_id)&lt;br /&gt;
    new_due_date = dup&lt;br /&gt;
    new_due_date.parent = to_assignment&lt;br /&gt;
    new_due_date.save!&lt;br /&gt;
    new_due_date&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def set(deadline_type_id, parent_id, round)&lt;br /&gt;
    self.deadline_type_id = deadline_type_id&lt;br /&gt;
    self.parent_id = parent_id&lt;br /&gt;
    self.round = round&lt;br /&gt;
    save!&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if this deadline has passed&lt;br /&gt;
  def overdue?&lt;br /&gt;
    due_at &amp;lt; Time.current&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if this deadline is upcoming&lt;br /&gt;
  def upcoming?&lt;br /&gt;
    due_at &amp;gt; Time.current&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the deadline type name&lt;br /&gt;
  def deadline_type_name&lt;br /&gt;
    deadline_type&amp;amp;.name&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Comparison method for sorting&lt;br /&gt;
  def &amp;lt;=&amp;gt;(other)&lt;br /&gt;
    return nil unless other.is_a?(DueDate)&lt;br /&gt;
    due_at &amp;lt;=&amp;gt; other.due_at&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Class methods for collection operations&lt;br /&gt;
  class &amp;lt;&amp;lt; self&lt;br /&gt;
    def sort_due_dates(due_dates)&lt;br /&gt;
      due_dates.sort_by(&amp;amp;:due_at)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    def any_future_due_dates?(due_dates)&lt;br /&gt;
      due_dates.any?(&amp;amp;:upcoming?)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    def copy(from_assignment_id, to_assignment_id)&lt;br /&gt;
      from_assignment = Assignment.find(from_assignment_id)&lt;br /&gt;
      to_assignment = Assignment.find(to_assignment_id)&lt;br /&gt;
&lt;br /&gt;
      from_assignment.due_dates.each do |due_date|&lt;br /&gt;
        new_due_date = due_date.dup&lt;br /&gt;
        new_due_date.parent = to_assignment&lt;br /&gt;
        new_due_date.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  before_save :set_default_round&lt;br /&gt;
&lt;br /&gt;
  def set_default_round&lt;br /&gt;
    self.round ||= 1&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Permission-Checking Methods ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Currently, permission checks depend only on user roles and ignore the actual deadline.  &lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Role-based permissions (participant_helpers.rb)&lt;br /&gt;
def participant_permissions(authorization)&lt;br /&gt;
  case authorization&lt;br /&gt;
  when 'reader'   then { can_submit: false, can_review: true, can_take_quiz: true }&lt;br /&gt;
  when 'reviewer' then { can_submit: false, can_review: true, can_take_quiz: false }&lt;br /&gt;
  when 'submitter' then { can_submit: true, can_review: false, can_take_quiz: false }&lt;br /&gt;
  else { can_submit: true, can_review: true, can_take_quiz: true }&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And deadline-based checks exist separately in the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def submission_allowed(topic_id = nil)&lt;br /&gt;
  check_condition('submission_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
def can_review(topic_id = nil)&lt;br /&gt;
  check_condition('review_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These two layers (role-based and time-based) never interact, leading to inconsistencies (e.g., a user with &amp;lt;code&amp;gt;can_submit=true&amp;lt;/code&amp;gt; after deadline).&lt;br /&gt;
&lt;br /&gt;
==== Proposed Changes ====&lt;br /&gt;
&lt;br /&gt;
The new &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module integrates role-based and deadline-based checks:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDatePermissions&lt;br /&gt;
  # Permission checking methods that combine deadline-based and role-based logic&lt;br /&gt;
  #&lt;br /&gt;
  # These methods must check both:&lt;br /&gt;
  # 1. Whether the action is permitted by the current deadline (submission_allowed_id, review_allowed_id, etc.)&lt;br /&gt;
  # 2. Whether the participant has the necessary permissions (can_submit, can_review, can_take_quiz fields)&lt;br /&gt;
&lt;br /&gt;
  # Check if submission is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_submit?(participant = nil)&lt;br /&gt;
    return false unless submission_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_submit&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: submission_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if review is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_review?(participant = nil)&lt;br /&gt;
    return false unless review_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_review&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: review_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if taking a quiz is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_take_quiz?(participant = nil)&lt;br /&gt;
    return false unless quiz_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_take_quiz&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: quiz_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if teammate review is allowed&lt;br /&gt;
  def can_review_teammate?(participant = nil)&lt;br /&gt;
    return false unless teammate_review_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_review&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: teammate_review_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Generic permission checker&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    permission_field = &amp;quot;#{activity}_allowed_id&amp;quot;&lt;br /&gt;
    return false unless respond_to?(permission_field)&lt;br /&gt;
&lt;br /&gt;
    allowed_id = public_send(permission_field)&lt;br /&gt;
    return false unless allowed_id&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get permission status for an action (OK, Late, No)&lt;br /&gt;
  def permission_status_for(action)&lt;br /&gt;
    permission_field = &amp;quot;#{action}_allowed_id&amp;quot;&lt;br /&gt;
    return 'No' unless respond_to?(permission_field)&lt;br /&gt;
&lt;br /&gt;
    allowed_id = public_send(permission_field)&lt;br /&gt;
    return 'No' unless allowed_id&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name || 'No'&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This module is included in the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== DueDateActions Mixin ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module provides deadline-related operations for models that have due dates (Assignment, SignUpTopic).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDateActions&lt;br /&gt;
  # Generic activity permission checker that determines if an activity is permissible&lt;br /&gt;
  # based on the current deadline state for this parent object&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    current_deadline = next_due_date()&lt;br /&gt;
    return false unless current_deadline&lt;br /&gt;
&lt;br /&gt;
    current_deadline.activity_permissible?(activity)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Syntactic sugar methods for common activities&lt;br /&gt;
  def submission_permissible?&lt;br /&gt;
    activity_permissible?(:submission)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def review_permissible?&lt;br /&gt;
    activity_permissible?(:review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def teammate_review_permissible?&lt;br /&gt;
    activity_permissible?(:teammate_review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def quiz_permissible?&lt;br /&gt;
    activity_permissible?(:quiz)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def team_formation_permissible?&lt;br /&gt;
    activity_permissible?(:team_formation)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def signup_permissible?&lt;br /&gt;
    activity_permissible?(:signup)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def drop_topic_permissible?&lt;br /&gt;
    activity_permissible?(:drop_topic)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the next due date for this assignment&lt;br /&gt;
  def next_due_date(topic_id = nil)&lt;br /&gt;
    # If a topic is specified and this assignment has topic-specific deadlines,&lt;br /&gt;
    # look for topic due dates first&lt;br /&gt;
    if topic_id &amp;amp;&amp;amp; has_topic_specific_deadlines?&lt;br /&gt;
      topic_deadline = due_dates.where(parent_id: topic_id, parent_type: 'SignUpTopic')&lt;br /&gt;
                               .upcoming&lt;br /&gt;
                               .first&lt;br /&gt;
      return topic_deadline if topic_deadline&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Fall back to assignment-level due dates&lt;br /&gt;
    due_dates.upcoming.first&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the current stage name for display purposes&lt;br /&gt;
  def current_stage&lt;br /&gt;
    deadline = next_due_date()&lt;br /&gt;
    return 'finished' unless deadline&lt;br /&gt;
&lt;br /&gt;
    deadline.deadline_type&amp;amp;.name || 'unknown'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if assignment has topic-specific deadlines&lt;br /&gt;
  def has_topic_specific_deadlines?&lt;br /&gt;
    staggered_deadline || due_dates.where(parent_type: 'SignUpTopic').exists?&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Copy due dates to a new parent object&lt;br /&gt;
  def copy_due_dates_to(new_parent)&lt;br /&gt;
    due_dates.find_each do |due_date|&lt;br /&gt;
      new_due_date = due_date.dup&lt;br /&gt;
      new_due_date.parent = new_parent&lt;br /&gt;
      new_due_date.save!&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This module is included in &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class Assignment &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
class SignUpTopic &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Implementation Summary ==&lt;br /&gt;
&lt;br /&gt;
=== Models Created/Modified ===&lt;br /&gt;
&lt;br /&gt;
* '''DeadlineType''' - Canonical deadline type definitions with semantic helper methods&lt;br /&gt;
* '''DeadlineRight''' - Permission state model (OK/Late/No) with comparison methods&lt;br /&gt;
* '''DueDate''' - Refactored with instance methods, scopes, and permission checking&lt;br /&gt;
* '''TopicDueDate''' - STI subclass for topic-specific deadlines&lt;br /&gt;
* '''Assignment''' - Includes DueDateActions mixin&lt;br /&gt;
* '''SignUpTopic''' - Includes DueDateActions mixin&lt;br /&gt;
&lt;br /&gt;
=== Modules Created ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions''' - Permission checking combining deadline and role constraints&lt;br /&gt;
* '''DueDateActions''' - Deadline-related operations for parent models (Assignment, SignUpTopic)&lt;br /&gt;
&lt;br /&gt;
=== Key Improvements ===&lt;br /&gt;
&lt;br /&gt;
# '''Separation of Concerns''': Permission logic separated into dedicated modules&lt;br /&gt;
# '''Instance Methods''': Replaced class methods with testable instance methods&lt;br /&gt;
# '''Polymorphic Design''': Single DueDate model serves both Assignment and SignUpTopic&lt;br /&gt;
# '''Unified Permissions''': Role-based and time-based checks combined in one interface&lt;br /&gt;
# '''Data Integrity''': Removed duplicate deadline types, enforced foreign key constraints&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
The test suite includes comprehensive RSpec tests for the ''DueDate'' model and its associated models (''DeadlineType'', ''DeadlineRight'').&lt;br /&gt;
These tests verify model validations, scopes, instance methods, class methods, and callbacks with extensive coverage of edge cases and error conditions.&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
The test suite follows RSpec best practices with:&lt;br /&gt;
* '''Nested contexts''' for different scenarios (e.g., &amp;quot;when parent is missing&amp;quot;, &amp;quot;when round is 0&amp;quot;)&lt;br /&gt;
* '''Multiple assertions per context''' to verify behavior from different angles&lt;br /&gt;
* '''Edge case coverage''' including empty collections, nil values, and boundary conditions&lt;br /&gt;
* '''Error case testing''' for invalid data and non-existent records&lt;br /&gt;
* '''Clear descriptive names''' using &amp;quot;context 'when...'&amp;quot; and &amp;quot;it 'does something'&amp;quot; patterns&lt;br /&gt;
&lt;br /&gt;
=== Current Coverage ===&lt;br /&gt;
&lt;br /&gt;
==== Associations ====&lt;br /&gt;
* Tests verify &amp;lt;code&amp;gt;belongs_to :parent&amp;lt;/code&amp;gt; (polymorphic) and &amp;lt;code&amp;gt;belongs_to :deadline_type&amp;lt;/code&amp;gt; relationships&lt;br /&gt;
&lt;br /&gt;
==== Validations ====&lt;br /&gt;
* '''Required field validation''': Tests for parent, due_at, and deadline_type_id presence&lt;br /&gt;
* '''Round validation''': Tests for positive values, zero rejection, negative rejection, and nil handling&lt;br /&gt;
* '''Error messages''': Verifies specific error messages are added to appropriate fields&lt;br /&gt;
* '''Valid record creation''': Confirms records persist when all requirements are met&lt;br /&gt;
&lt;br /&gt;
==== Scopes ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;''': Returns future due dates ordered by due_at, excludes past dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;''': Returns past due dates ordered by due_at, excludes future dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;''': Filters by round number, handles non-existent rounds&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt;''': Filters by deadline type name, handles non-existent types&lt;br /&gt;
&lt;br /&gt;
==== Instance Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#overdue?&amp;lt;/code&amp;gt;''': Tests past dates (true), future dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#upcoming?&amp;lt;/code&amp;gt;''': Tests future dates (true), past dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#set&amp;lt;/code&amp;gt;''': Verifies updating deadline_type_id, parent_id, and round with persistence and error handling&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#copy&amp;lt;/code&amp;gt;''': Tests duplication to new assignment, attribute preservation, error handling for non-existent targets&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#deadline_type_name&amp;lt;/code&amp;gt;''': Returns associated deadline type name, handles nil cases&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#last_deadline?&amp;lt;/code&amp;gt;''': Tests detection of final deadline, handles multiple deadlines and single deadline scenarios&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#&amp;lt;=&amp;gt;&amp;lt;/code&amp;gt;''': Tests comparison by due_at time, handles non-DueDate objects&lt;br /&gt;
&lt;br /&gt;
==== Class Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.sort_due_dates&amp;lt;/code&amp;gt;''': Sorts by due_at ascending, handles empty arrays and single elements&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.any_future_due_dates?&amp;lt;/code&amp;gt;''': Detects future dates in collections, handles empty arrays and mixed date collections&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.copy&amp;lt;/code&amp;gt;''': Copies all due dates between assignments, preserves attributes, handles empty sources and non-existent records&lt;br /&gt;
&lt;br /&gt;
==== Callbacks ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;before_save :set_default_round&amp;lt;/code&amp;gt;''': Sets round to 1 when not specified, preserves explicit values, handles nil&lt;br /&gt;
&lt;br /&gt;
=== Test Examples ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Validation with multiple assertions&lt;br /&gt;
describe 'validations' do&lt;br /&gt;
  context 'when parent is missing' do&lt;br /&gt;
    let(:due_date) do&lt;br /&gt;
      DueDate.new(&lt;br /&gt;
        parent: nil,&lt;br /&gt;
        due_at: 2.days.from_now,&lt;br /&gt;
        deadline_type: submission_type,&lt;br /&gt;
        submission_allowed_id: ok_right.id,&lt;br /&gt;
        review_allowed_id: ok_right.id&lt;br /&gt;
      )&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'is invalid' do&lt;br /&gt;
      expect(due_date).to be_invalid&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'has error on parent field' do&lt;br /&gt;
      due_date.valid?&lt;br /&gt;
      expect(due_date.errors[:parent]).to include('must exist')&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when round validation' do&lt;br /&gt;
    context 'with round value of 0' do&lt;br /&gt;
      let(:due_date) do&lt;br /&gt;
        DueDate.new(&lt;br /&gt;
          parent: assignment,&lt;br /&gt;
          due_at: 2.days.from_now,&lt;br /&gt;
          deadline_type: submission_type,&lt;br /&gt;
          submission_allowed_id: ok_right.id,&lt;br /&gt;
          review_allowed_id: ok_right.id,&lt;br /&gt;
          round: 0&lt;br /&gt;
        )&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'is invalid' do&lt;br /&gt;
        expect(due_date).to be_invalid&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'has error on round field' do&lt;br /&gt;
        due_date.valid?&lt;br /&gt;
        expect(due_date.errors[:round]).to be_present&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'with nil round' do&lt;br /&gt;
      let(:due_date) do&lt;br /&gt;
        DueDate.create(&lt;br /&gt;
          parent: assignment,&lt;br /&gt;
          due_at: 2.days.from_now,&lt;br /&gt;
          deadline_type: submission_type,&lt;br /&gt;
          submission_allowed_id: ok_right.id,&lt;br /&gt;
          review_allowed_id: ok_right.id,&lt;br /&gt;
          round: nil&lt;br /&gt;
        )&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'is valid' do&lt;br /&gt;
        expect(due_date).to be_valid&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'sets default round to 1' do&lt;br /&gt;
        expect(due_date.round).to eq(1)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
# Scope with edge cases&lt;br /&gt;
describe '.upcoming' do&lt;br /&gt;
  it 'returns only future due dates' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).to contain_exactly(upcoming_due_date1, upcoming_due_date2)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'orders results by due_at ascending' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).to eq([upcoming_due_date1, upcoming_due_date2])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'excludes past due dates' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).not_to include(past_due_date)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when all due dates are in the past' do&lt;br /&gt;
    before do&lt;br /&gt;
      DueDate.destroy_all&lt;br /&gt;
      DueDate.create(&lt;br /&gt;
        parent: assignment,&lt;br /&gt;
        due_at: 3.days.ago,&lt;br /&gt;
        deadline_type: submission_type,&lt;br /&gt;
        submission_allowed_id: ok_right.id,&lt;br /&gt;
        review_allowed_id: ok_right.id&lt;br /&gt;
      )&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns empty collection' do&lt;br /&gt;
      expect(DueDate.upcoming).to be_empty&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
# Instance method with error handling&lt;br /&gt;
describe '#copy' do&lt;br /&gt;
  let(:original) do&lt;br /&gt;
    DueDate.create(&lt;br /&gt;
      parent: assignment,&lt;br /&gt;
      due_at: 2.days.from_now,&lt;br /&gt;
      deadline_type: submission_type,&lt;br /&gt;
      submission_allowed_id: ok_right.id,&lt;br /&gt;
      review_allowed_id: late_right.id,&lt;br /&gt;
      round: 1&lt;br /&gt;
    )&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  let(:copied) { original.copy(assignment2.id) }&lt;br /&gt;
&lt;br /&gt;
  it 'creates a new persisted record' do&lt;br /&gt;
    expect(copied).to be_persisted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'has different id from original' do&lt;br /&gt;
    expect(copied.id).not_to eq(original.id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'preserves all attributes' do&lt;br /&gt;
    expect(copied.due_at).to eq(original.due_at)&lt;br /&gt;
    expect(copied.deadline_type_id).to eq(original.deadline_type_id)&lt;br /&gt;
    expect(copied.round).to eq(original.round)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when copying to non-existent assignment' do&lt;br /&gt;
    it 'raises error' do&lt;br /&gt;
      expect do&lt;br /&gt;
        original.copy(99999)&lt;br /&gt;
      end.to raise_error(ActiveRecord::RecordNotFound)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Test Metrics ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDate model''': 60+ examples covering all methods and edge cases&lt;br /&gt;
* '''Context depth''': 2-3 levels for complex scenarios&lt;br /&gt;
* '''Code coverage''': 100% coverage of DueDate model methods&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
=== Demo Link ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add deadline notification system using the new permission framework&lt;br /&gt;
* Implement deadline templates for common assignment patterns&lt;br /&gt;
* Add API endpoints for mobile app integration&lt;br /&gt;
* Create admin dashboard for monitoring deadline compliance across courses&lt;br /&gt;
* Add automated deadline extension workflows based on configurable rules&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167190</id>
		<title>CSC/ECE 517 Fall 2025 - E2566. Finish DueDates</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167190"/>
		<updated>2025-12-02T03:53:53Z</updated>

		<summary type="html">&lt;p&gt;Skim253: /* Tasks Identified */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
This project aims to complete the implementation of the DueDate system in Expertiza. The current implementation consists mostly of class methods and lacks proper definition of different deadline types and permission checking functionality. This redesign will create a more robust, readable, and maintainable due date system.&lt;br /&gt;
&lt;br /&gt;
=== Existing Components ===&lt;br /&gt;
* &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model with polymorphic parent association (Assignment/SignUpTopic)&lt;br /&gt;
* &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; subclasses&lt;br /&gt;
* Basic CRUD operations (within &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AssignmentForm&amp;lt;/code&amp;gt;) and sorting functionality (&amp;lt;code&amp;gt;deadline_sort&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Database schema with &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt; field&lt;br /&gt;
&lt;br /&gt;
=== Current Behavior ===&lt;br /&gt;
&lt;br /&gt;
====Admin's View====&lt;br /&gt;
[[File:Duedates.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* When editing an assignment, due dates can be modified under the Due Dates tab.&lt;br /&gt;
* Each row contains the following columns: &amp;lt;code&amp;gt;Date &amp;amp; Time&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Use Date Updater&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Submission Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Teammate Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Meta-Review Allowed&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Reminder&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:Assignments-CurrentStage.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* In the Assignments table, the ''DueDate'' of each assignment is displayed as Current Stage and Stage Deadline.&lt;br /&gt;
&lt;br /&gt;
====Student's View====&lt;br /&gt;
[[File:Student-assignments.png|600px]]&lt;br /&gt;
* Students can view the due date information under &amp;lt;code&amp;gt;Current State&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Stage Deadline&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
* The actions available to students are determined by the current DueDate status.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-not-over.png|600px]]&lt;br /&gt;
* For example, during the submission period, students can access the &amp;lt;code&amp;gt;Submit a hyperlink&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Submit a file&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-over.png|600px]]&lt;br /&gt;
* Once the due date has passed, students cannot submit either a hyperlink or a file.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Currently, there are some glaring issues with how due_dates was created in the first place, including redundancy and lack of modularity.&lt;br /&gt;
&lt;br /&gt;
Modeling &amp;amp; Structural Issues&lt;br /&gt;
# No dedicated &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to define different kinds of deadlines. The existing &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; class only serves as an association for &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; models, and is never referenced in the current &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; implementation.&lt;br /&gt;
# Duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table, which may lead to potential bugs.&lt;br /&gt;
# Incomplete deadline type definitions - &amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt; need to be added.&lt;br /&gt;
# No clear separation of concerns - data persistence, business logic, and permission logic are mixed in a single model.&lt;br /&gt;
&lt;br /&gt;
Permission &amp;amp; Behavior Issues&lt;br /&gt;
# Overuse of class methods making the code difficult to maintain.&lt;br /&gt;
# Missing permission checking logic for user actions.&lt;br /&gt;
&lt;br /&gt;
=== Tasks Identified ===&lt;br /&gt;
&lt;br /&gt;
Modeling &amp;amp; Structural Issues&lt;br /&gt;
# Refactor the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model to separate persistence, business logic, and permission logic into distinct layers.&lt;br /&gt;
# Implement reusable mixins (e.g., &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;) to handle permission evaluation and deadline-related operations.&lt;br /&gt;
# Introduce instance-level methods (e.g., &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;) to replace class-level utilities and improve testability.&lt;br /&gt;
&lt;br /&gt;
Permission &amp;amp; Behavior Issues&lt;br /&gt;
# Integrate role-based and time-based permission checks within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; to ensure consistent access control.&lt;br /&gt;
# Redesign the &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.&lt;br /&gt;
# Add missing deadline types (&amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt;) and remove duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries for data integrity.&lt;br /&gt;
&lt;br /&gt;
== Proposed Solution ==&lt;br /&gt;
&lt;br /&gt;
[[File:Duedate-diagram.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== DeadlineType Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Although the current codebase includes a &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model, it only serves as a passive lookup table for associations with &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt;.  &lt;br /&gt;
In practice, most parts of the system still rely on hard-coded numeric IDs or manual lookups such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Example of current usage&lt;br /&gt;
deadline_type_id = DeadlineType.find_by(name: 'review').id&lt;br /&gt;
if due_date.deadline_type_id == deadline_type_id&lt;br /&gt;
  # Perform review-related logic&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of using the model as an active domain object, &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; directly compares IDs or string literals.  &lt;br /&gt;
This leads to several structural issues:&lt;br /&gt;
&lt;br /&gt;
* Inconsistent references (some use IDs, others strings)&lt;br /&gt;
* Tight coupling between &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
* Lack of helper semantics (e.g., &amp;lt;code&amp;gt;is_review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;is_submission?&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Data duplication and integrity risks (two &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; rows)&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; will serve as a source of truth, defining all valid deadline types and providing meaningful interfaces.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE deadline_types (&lt;br /&gt;
  id BIGINT PRIMARY KEY,&lt;br /&gt;
  name VARCHAR(255) NOT NULL UNIQUE,&lt;br /&gt;
  description TEXT NOT NULL,&lt;br /&gt;
  created_at TIMESTAMP,&lt;br /&gt;
  updated_at TIMESTAMP&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Deadline Type Definitions ====&lt;br /&gt;
* &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;: submission - Student work submission deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt;: review - Peer review deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;3&amp;lt;/code&amp;gt;: teammate_review - Team member evaluation deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;5&amp;lt;/code&amp;gt;: metareview - Meta-review deadlines (kept for backward compatibility)  &lt;br /&gt;
* &amp;lt;code&amp;gt;6&amp;lt;/code&amp;gt;: drop_topic - Topic drop deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;7&amp;lt;/code&amp;gt;: signup - Course/assignment signup deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;8&amp;lt;/code&amp;gt;: team_formation - Team formation deadlines (clean up duplicate ID 10)  &lt;br /&gt;
* &amp;lt;code&amp;gt;11&amp;lt;/code&amp;gt;: quiz - Quiz completion deadlines&lt;br /&gt;
&lt;br /&gt;
==== DeadlineType Model Implementation ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineType &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
  validates :description, presence: true&lt;br /&gt;
&lt;br /&gt;
  has_many :due_dates, foreign_key: :deadline_type_id, dependent: :restrict_with_exception&lt;br /&gt;
&lt;br /&gt;
  # Semantic helper methods for deadline type identification&lt;br /&gt;
  def submission?&lt;br /&gt;
    name == 'submission'&lt;br /&gt;
  end&lt;br /&gt;
  # same code for review, teammate_review, quiz, team_formation, signup, drop_topic   &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DeadlineRight Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model represents the permission level for a specific action at a given deadline.&lt;br /&gt;
It defines three states: &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt; (action allowed), &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt; (action allowed with penalty), and &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt; (action denied).&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE deadline_rights (&lt;br /&gt;
  id BIGINT PRIMARY KEY,&lt;br /&gt;
  name VARCHAR(255) NOT NULL UNIQUE,&lt;br /&gt;
  created_at TIMESTAMP,&lt;br /&gt;
  updated_at TIMESTAMP&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== DeadlineRight Model ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineRight &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
&lt;br /&gt;
  # Permission checking methods&lt;br /&gt;
  def allows_action?&lt;br /&gt;
    %w[OK Late].include?(name)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def denies_action?&lt;br /&gt;
    name == 'No'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def allows_with_penalty?&lt;br /&gt;
    name == 'Late'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def allows_without_penalty?&lt;br /&gt;
    name == 'OK'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Display methods&lt;br /&gt;
  def to_s&lt;br /&gt;
    name&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def permission_level&lt;br /&gt;
    case name&lt;br /&gt;
    when 'No'   then 0&lt;br /&gt;
    when 'Late' then 1&lt;br /&gt;
    when 'OK'   then 2&lt;br /&gt;
    else -1&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== DueDate Model Refactoring ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model currently mixes persistence logic, deadline calculation, and permission checking, violating SRP.  &lt;br /&gt;
Most of its logic is implemented as class methods, which makes testing and extension difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Current design&lt;br /&gt;
def self.copy(old_assignment_id, new_assignment_id)&lt;br /&gt;
  duedates = where(parent_id: old_assignment_id)&lt;br /&gt;
  duedates.each do |orig_due_date|&lt;br /&gt;
    new_due_date = orig_due_date.dup&lt;br /&gt;
    new_due_date.parent_id = new_assignment_id&lt;br /&gt;
    new_due_date.save&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Improved Design ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
&lt;br /&gt;
  belongs_to :parent, polymorphic: true&lt;br /&gt;
  belongs_to :deadline_type, foreign_key: :deadline_type_id&lt;br /&gt;
&lt;br /&gt;
  validates :due_at, presence: true&lt;br /&gt;
  validates :deadline_type_id, presence: true&lt;br /&gt;
  validates :parent, presence: true&lt;br /&gt;
  validates :round, numericality: { greater_than: 0 }, allow_nil: true&lt;br /&gt;
&lt;br /&gt;
  # Scopes for common queries&lt;br /&gt;
  scope :upcoming, -&amp;gt; { where('due_at &amp;gt; ?', Time.current).order(:due_at) }&lt;br /&gt;
  scope :overdue, -&amp;gt; { where('due_at &amp;lt; ?', Time.current).order(:due_at) }&lt;br /&gt;
  scope :for_round, -&amp;gt;(round_num) { where(round: round_num) }&lt;br /&gt;
  scope :for_deadline_type, -&amp;gt;(type_name) { joins(:deadline_type).where(deadline_types: { name: type_name }) }&lt;br /&gt;
&lt;br /&gt;
  # Instance methods replace class methods&lt;br /&gt;
  def copy(to_assignment_id)&lt;br /&gt;
    to_assignment = Assignment.find(to_assignment_id)&lt;br /&gt;
    new_due_date = dup&lt;br /&gt;
    new_due_date.parent = to_assignment&lt;br /&gt;
    new_due_date.save!&lt;br /&gt;
    new_due_date&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def set(deadline_type_id, parent_id, round)&lt;br /&gt;
    self.deadline_type_id = deadline_type_id&lt;br /&gt;
    self.parent_id = parent_id&lt;br /&gt;
    self.round = round&lt;br /&gt;
    save!&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if this deadline has passed&lt;br /&gt;
  def overdue?&lt;br /&gt;
    due_at &amp;lt; Time.current&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if this deadline is upcoming&lt;br /&gt;
  def upcoming?&lt;br /&gt;
    due_at &amp;gt; Time.current&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the deadline type name&lt;br /&gt;
  def deadline_type_name&lt;br /&gt;
    deadline_type&amp;amp;.name&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Comparison method for sorting&lt;br /&gt;
  def &amp;lt;=&amp;gt;(other)&lt;br /&gt;
    return nil unless other.is_a?(DueDate)&lt;br /&gt;
    due_at &amp;lt;=&amp;gt; other.due_at&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Class methods for collection operations&lt;br /&gt;
  class &amp;lt;&amp;lt; self&lt;br /&gt;
    def sort_due_dates(due_dates)&lt;br /&gt;
      due_dates.sort_by(&amp;amp;:due_at)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    def any_future_due_dates?(due_dates)&lt;br /&gt;
      due_dates.any?(&amp;amp;:upcoming?)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    def copy(from_assignment_id, to_assignment_id)&lt;br /&gt;
      from_assignment = Assignment.find(from_assignment_id)&lt;br /&gt;
      to_assignment = Assignment.find(to_assignment_id)&lt;br /&gt;
&lt;br /&gt;
      from_assignment.due_dates.each do |due_date|&lt;br /&gt;
        new_due_date = due_date.dup&lt;br /&gt;
        new_due_date.parent = to_assignment&lt;br /&gt;
        new_due_date.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  before_save :set_default_round&lt;br /&gt;
&lt;br /&gt;
  def set_default_round&lt;br /&gt;
    self.round ||= 1&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Permission-Checking Methods ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Currently, permission checks depend only on user roles and ignore the actual deadline.  &lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Role-based permissions (participant_helpers.rb)&lt;br /&gt;
def participant_permissions(authorization)&lt;br /&gt;
  case authorization&lt;br /&gt;
  when 'reader'   then { can_submit: false, can_review: true, can_take_quiz: true }&lt;br /&gt;
  when 'reviewer' then { can_submit: false, can_review: true, can_take_quiz: false }&lt;br /&gt;
  when 'submitter' then { can_submit: true, can_review: false, can_take_quiz: false }&lt;br /&gt;
  else { can_submit: true, can_review: true, can_take_quiz: true }&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And deadline-based checks exist separately in the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def submission_allowed(topic_id = nil)&lt;br /&gt;
  check_condition('submission_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
def can_review(topic_id = nil)&lt;br /&gt;
  check_condition('review_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These two layers (role-based and time-based) never interact, leading to inconsistencies (e.g., a user with &amp;lt;code&amp;gt;can_submit=true&amp;lt;/code&amp;gt; after deadline).&lt;br /&gt;
&lt;br /&gt;
==== Proposed Changes ====&lt;br /&gt;
&lt;br /&gt;
The new &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module integrates role-based and deadline-based checks:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDatePermissions&lt;br /&gt;
  # Permission checking methods that combine deadline-based and role-based logic&lt;br /&gt;
  #&lt;br /&gt;
  # These methods must check both:&lt;br /&gt;
  # 1. Whether the action is permitted by the current deadline (submission_allowed_id, review_allowed_id, etc.)&lt;br /&gt;
  # 2. Whether the participant has the necessary permissions (can_submit, can_review, can_take_quiz fields)&lt;br /&gt;
&lt;br /&gt;
  # Check if submission is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_submit?(participant = nil)&lt;br /&gt;
    return false unless submission_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_submit&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: submission_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if review is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_review?(participant = nil)&lt;br /&gt;
    return false unless review_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_review&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: review_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if taking a quiz is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_take_quiz?(participant = nil)&lt;br /&gt;
    return false unless quiz_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_take_quiz&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: quiz_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if teammate review is allowed&lt;br /&gt;
  def can_review_teammate?(participant = nil)&lt;br /&gt;
    return false unless teammate_review_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_review&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: teammate_review_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Generic permission checker&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    permission_field = &amp;quot;#{activity}_allowed_id&amp;quot;&lt;br /&gt;
    return false unless respond_to?(permission_field)&lt;br /&gt;
&lt;br /&gt;
    allowed_id = public_send(permission_field)&lt;br /&gt;
    return false unless allowed_id&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get permission status for an action (OK, Late, No)&lt;br /&gt;
  def permission_status_for(action)&lt;br /&gt;
    permission_field = &amp;quot;#{action}_allowed_id&amp;quot;&lt;br /&gt;
    return 'No' unless respond_to?(permission_field)&lt;br /&gt;
&lt;br /&gt;
    allowed_id = public_send(permission_field)&lt;br /&gt;
    return 'No' unless allowed_id&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name || 'No'&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This module is included in the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 5. DueDateActions Mixin ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module provides deadline-related operations for models that have due dates (Assignment, SignUpTopic).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDateActions&lt;br /&gt;
  # Generic activity permission checker that determines if an activity is permissible&lt;br /&gt;
  # based on the current deadline state for this parent object&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    current_deadline = next_due_date()&lt;br /&gt;
    return false unless current_deadline&lt;br /&gt;
&lt;br /&gt;
    current_deadline.activity_permissible?(activity)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Syntactic sugar methods for common activities&lt;br /&gt;
  def submission_permissible?&lt;br /&gt;
    activity_permissible?(:submission)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def review_permissible?&lt;br /&gt;
    activity_permissible?(:review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def teammate_review_permissible?&lt;br /&gt;
    activity_permissible?(:teammate_review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def quiz_permissible?&lt;br /&gt;
    activity_permissible?(:quiz)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def team_formation_permissible?&lt;br /&gt;
    activity_permissible?(:team_formation)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def signup_permissible?&lt;br /&gt;
    activity_permissible?(:signup)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def drop_topic_permissible?&lt;br /&gt;
    activity_permissible?(:drop_topic)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the next due date for this assignment&lt;br /&gt;
  def next_due_date(topic_id = nil)&lt;br /&gt;
    # If a topic is specified and this assignment has topic-specific deadlines,&lt;br /&gt;
    # look for topic due dates first&lt;br /&gt;
    if topic_id &amp;amp;&amp;amp; has_topic_specific_deadlines?&lt;br /&gt;
      topic_deadline = due_dates.where(parent_id: topic_id, parent_type: 'SignUpTopic')&lt;br /&gt;
                               .upcoming&lt;br /&gt;
                               .first&lt;br /&gt;
      return topic_deadline if topic_deadline&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Fall back to assignment-level due dates&lt;br /&gt;
    due_dates.upcoming.first&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the current stage name for display purposes&lt;br /&gt;
  def current_stage&lt;br /&gt;
    deadline = next_due_date()&lt;br /&gt;
    return 'finished' unless deadline&lt;br /&gt;
&lt;br /&gt;
    deadline.deadline_type&amp;amp;.name || 'unknown'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if assignment has topic-specific deadlines&lt;br /&gt;
  def has_topic_specific_deadlines?&lt;br /&gt;
    staggered_deadline || due_dates.where(parent_type: 'SignUpTopic').exists?&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Copy due dates to a new parent object&lt;br /&gt;
  def copy_due_dates_to(new_parent)&lt;br /&gt;
    due_dates.find_each do |due_date|&lt;br /&gt;
      new_due_date = due_date.dup&lt;br /&gt;
      new_due_date.parent = new_parent&lt;br /&gt;
      new_due_date.save!&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This module is included in &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class Assignment &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
class SignUpTopic &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Implementation Summary ==&lt;br /&gt;
&lt;br /&gt;
=== Models Created/Modified ===&lt;br /&gt;
&lt;br /&gt;
* '''DeadlineType''' - Canonical deadline type definitions with semantic helper methods&lt;br /&gt;
* '''DeadlineRight''' - Permission state model (OK/Late/No) with comparison methods&lt;br /&gt;
* '''DueDate''' - Refactored with instance methods, scopes, and permission checking&lt;br /&gt;
* '''TopicDueDate''' - STI subclass for topic-specific deadlines&lt;br /&gt;
* '''Assignment''' - Includes DueDateActions mixin&lt;br /&gt;
* '''SignUpTopic''' - Includes DueDateActions mixin&lt;br /&gt;
&lt;br /&gt;
=== Modules Created ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions''' - Permission checking combining deadline and role constraints&lt;br /&gt;
* '''DueDateActions''' - Deadline-related operations for parent models (Assignment, SignUpTopic)&lt;br /&gt;
&lt;br /&gt;
=== Key Improvements ===&lt;br /&gt;
&lt;br /&gt;
# '''Separation of Concerns''': Permission logic separated into dedicated modules&lt;br /&gt;
# '''Instance Methods''': Replaced class methods with testable instance methods&lt;br /&gt;
# '''Polymorphic Design''': Single DueDate model serves both Assignment and SignUpTopic&lt;br /&gt;
# '''Unified Permissions''': Role-based and time-based checks combined in one interface&lt;br /&gt;
# '''Data Integrity''': Removed duplicate deadline types, enforced foreign key constraints&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
The test suite includes comprehensive RSpec tests for the ''DueDate'' model and its associated models (''DeadlineType'', ''DeadlineRight'').&lt;br /&gt;
These tests verify model validations, scopes, instance methods, class methods, and callbacks with extensive coverage of edge cases and error conditions.&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
The test suite follows RSpec best practices with:&lt;br /&gt;
* '''Nested contexts''' for different scenarios (e.g., &amp;quot;when parent is missing&amp;quot;, &amp;quot;when round is 0&amp;quot;)&lt;br /&gt;
* '''Multiple assertions per context''' to verify behavior from different angles&lt;br /&gt;
* '''Edge case coverage''' including empty collections, nil values, and boundary conditions&lt;br /&gt;
* '''Error case testing''' for invalid data and non-existent records&lt;br /&gt;
* '''Clear descriptive names''' using &amp;quot;context 'when...'&amp;quot; and &amp;quot;it 'does something'&amp;quot; patterns&lt;br /&gt;
&lt;br /&gt;
=== Current Coverage ===&lt;br /&gt;
&lt;br /&gt;
==== Associations ====&lt;br /&gt;
* Tests verify &amp;lt;code&amp;gt;belongs_to :parent&amp;lt;/code&amp;gt; (polymorphic) and &amp;lt;code&amp;gt;belongs_to :deadline_type&amp;lt;/code&amp;gt; relationships&lt;br /&gt;
&lt;br /&gt;
==== Validations ====&lt;br /&gt;
* '''Required field validation''': Tests for parent, due_at, and deadline_type_id presence&lt;br /&gt;
* '''Round validation''': Tests for positive values, zero rejection, negative rejection, and nil handling&lt;br /&gt;
* '''Error messages''': Verifies specific error messages are added to appropriate fields&lt;br /&gt;
* '''Valid record creation''': Confirms records persist when all requirements are met&lt;br /&gt;
&lt;br /&gt;
==== Scopes ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;''': Returns future due dates ordered by due_at, excludes past dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;''': Returns past due dates ordered by due_at, excludes future dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;''': Filters by round number, handles non-existent rounds&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt;''': Filters by deadline type name, handles non-existent types&lt;br /&gt;
&lt;br /&gt;
==== Instance Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#overdue?&amp;lt;/code&amp;gt;''': Tests past dates (true), future dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#upcoming?&amp;lt;/code&amp;gt;''': Tests future dates (true), past dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#set&amp;lt;/code&amp;gt;''': Verifies updating deadline_type_id, parent_id, and round with persistence and error handling&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#copy&amp;lt;/code&amp;gt;''': Tests duplication to new assignment, attribute preservation, error handling for non-existent targets&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#deadline_type_name&amp;lt;/code&amp;gt;''': Returns associated deadline type name, handles nil cases&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#last_deadline?&amp;lt;/code&amp;gt;''': Tests detection of final deadline, handles multiple deadlines and single deadline scenarios&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#&amp;lt;=&amp;gt;&amp;lt;/code&amp;gt;''': Tests comparison by due_at time, handles non-DueDate objects&lt;br /&gt;
&lt;br /&gt;
==== Class Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.sort_due_dates&amp;lt;/code&amp;gt;''': Sorts by due_at ascending, handles empty arrays and single elements&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.any_future_due_dates?&amp;lt;/code&amp;gt;''': Detects future dates in collections, handles empty arrays and mixed date collections&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.copy&amp;lt;/code&amp;gt;''': Copies all due dates between assignments, preserves attributes, handles empty sources and non-existent records&lt;br /&gt;
&lt;br /&gt;
==== Callbacks ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;before_save :set_default_round&amp;lt;/code&amp;gt;''': Sets round to 1 when not specified, preserves explicit values, handles nil&lt;br /&gt;
&lt;br /&gt;
=== Test Examples ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Validation with multiple assertions&lt;br /&gt;
describe 'validations' do&lt;br /&gt;
  context 'when parent is missing' do&lt;br /&gt;
    let(:due_date) do&lt;br /&gt;
      DueDate.new(&lt;br /&gt;
        parent: nil,&lt;br /&gt;
        due_at: 2.days.from_now,&lt;br /&gt;
        deadline_type: submission_type,&lt;br /&gt;
        submission_allowed_id: ok_right.id,&lt;br /&gt;
        review_allowed_id: ok_right.id&lt;br /&gt;
      )&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'is invalid' do&lt;br /&gt;
      expect(due_date).to be_invalid&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'has error on parent field' do&lt;br /&gt;
      due_date.valid?&lt;br /&gt;
      expect(due_date.errors[:parent]).to include('must exist')&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when round validation' do&lt;br /&gt;
    context 'with round value of 0' do&lt;br /&gt;
      let(:due_date) do&lt;br /&gt;
        DueDate.new(&lt;br /&gt;
          parent: assignment,&lt;br /&gt;
          due_at: 2.days.from_now,&lt;br /&gt;
          deadline_type: submission_type,&lt;br /&gt;
          submission_allowed_id: ok_right.id,&lt;br /&gt;
          review_allowed_id: ok_right.id,&lt;br /&gt;
          round: 0&lt;br /&gt;
        )&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'is invalid' do&lt;br /&gt;
        expect(due_date).to be_invalid&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'has error on round field' do&lt;br /&gt;
        due_date.valid?&lt;br /&gt;
        expect(due_date.errors[:round]).to be_present&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'with nil round' do&lt;br /&gt;
      let(:due_date) do&lt;br /&gt;
        DueDate.create(&lt;br /&gt;
          parent: assignment,&lt;br /&gt;
          due_at: 2.days.from_now,&lt;br /&gt;
          deadline_type: submission_type,&lt;br /&gt;
          submission_allowed_id: ok_right.id,&lt;br /&gt;
          review_allowed_id: ok_right.id,&lt;br /&gt;
          round: nil&lt;br /&gt;
        )&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'is valid' do&lt;br /&gt;
        expect(due_date).to be_valid&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'sets default round to 1' do&lt;br /&gt;
        expect(due_date.round).to eq(1)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
# Scope with edge cases&lt;br /&gt;
describe '.upcoming' do&lt;br /&gt;
  it 'returns only future due dates' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).to contain_exactly(upcoming_due_date1, upcoming_due_date2)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'orders results by due_at ascending' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).to eq([upcoming_due_date1, upcoming_due_date2])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'excludes past due dates' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).not_to include(past_due_date)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when all due dates are in the past' do&lt;br /&gt;
    before do&lt;br /&gt;
      DueDate.destroy_all&lt;br /&gt;
      DueDate.create(&lt;br /&gt;
        parent: assignment,&lt;br /&gt;
        due_at: 3.days.ago,&lt;br /&gt;
        deadline_type: submission_type,&lt;br /&gt;
        submission_allowed_id: ok_right.id,&lt;br /&gt;
        review_allowed_id: ok_right.id&lt;br /&gt;
      )&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns empty collection' do&lt;br /&gt;
      expect(DueDate.upcoming).to be_empty&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
# Instance method with error handling&lt;br /&gt;
describe '#copy' do&lt;br /&gt;
  let(:original) do&lt;br /&gt;
    DueDate.create(&lt;br /&gt;
      parent: assignment,&lt;br /&gt;
      due_at: 2.days.from_now,&lt;br /&gt;
      deadline_type: submission_type,&lt;br /&gt;
      submission_allowed_id: ok_right.id,&lt;br /&gt;
      review_allowed_id: late_right.id,&lt;br /&gt;
      round: 1&lt;br /&gt;
    )&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  let(:copied) { original.copy(assignment2.id) }&lt;br /&gt;
&lt;br /&gt;
  it 'creates a new persisted record' do&lt;br /&gt;
    expect(copied).to be_persisted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'has different id from original' do&lt;br /&gt;
    expect(copied.id).not_to eq(original.id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'preserves all attributes' do&lt;br /&gt;
    expect(copied.due_at).to eq(original.due_at)&lt;br /&gt;
    expect(copied.deadline_type_id).to eq(original.deadline_type_id)&lt;br /&gt;
    expect(copied.round).to eq(original.round)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when copying to non-existent assignment' do&lt;br /&gt;
    it 'raises error' do&lt;br /&gt;
      expect do&lt;br /&gt;
        original.copy(99999)&lt;br /&gt;
      end.to raise_error(ActiveRecord::RecordNotFound)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Test Metrics ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDate model''': 60+ examples covering all methods and edge cases&lt;br /&gt;
* '''Context depth''': 2-3 levels for complex scenarios&lt;br /&gt;
* '''Code coverage''': 100% coverage of DueDate model methods&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
=== Demo Link ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add deadline notification system using the new permission framework&lt;br /&gt;
* Implement deadline templates for common assignment patterns&lt;br /&gt;
* Add API endpoints for mobile app integration&lt;br /&gt;
* Create admin dashboard for monitoring deadline compliance across courses&lt;br /&gt;
* Add automated deadline extension workflows based on configurable rules&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167189</id>
		<title>CSC/ECE 517 Fall 2025 - E2566. Finish DueDates</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167189"/>
		<updated>2025-12-02T03:52:01Z</updated>

		<summary type="html">&lt;p&gt;Skim253: /* Motivation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
This project aims to complete the implementation of the DueDate system in Expertiza. The current implementation consists mostly of class methods and lacks proper definition of different deadline types and permission checking functionality. This redesign will create a more robust, readable, and maintainable due date system.&lt;br /&gt;
&lt;br /&gt;
=== Existing Components ===&lt;br /&gt;
* &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model with polymorphic parent association (Assignment/SignUpTopic)&lt;br /&gt;
* &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; subclasses&lt;br /&gt;
* Basic CRUD operations (within &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AssignmentForm&amp;lt;/code&amp;gt;) and sorting functionality (&amp;lt;code&amp;gt;deadline_sort&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Database schema with &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt; field&lt;br /&gt;
&lt;br /&gt;
=== Current Behavior ===&lt;br /&gt;
&lt;br /&gt;
====Admin's View====&lt;br /&gt;
[[File:Duedates.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* When editing an assignment, due dates can be modified under the Due Dates tab.&lt;br /&gt;
* Each row contains the following columns: &amp;lt;code&amp;gt;Date &amp;amp; Time&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Use Date Updater&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Submission Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Teammate Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Meta-Review Allowed&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Reminder&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:Assignments-CurrentStage.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* In the Assignments table, the ''DueDate'' of each assignment is displayed as Current Stage and Stage Deadline.&lt;br /&gt;
&lt;br /&gt;
====Student's View====&lt;br /&gt;
[[File:Student-assignments.png|600px]]&lt;br /&gt;
* Students can view the due date information under &amp;lt;code&amp;gt;Current State&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Stage Deadline&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
* The actions available to students are determined by the current DueDate status.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-not-over.png|600px]]&lt;br /&gt;
* For example, during the submission period, students can access the &amp;lt;code&amp;gt;Submit a hyperlink&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Submit a file&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-over.png|600px]]&lt;br /&gt;
* Once the due date has passed, students cannot submit either a hyperlink or a file.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Currently, there are some glaring issues with how due_dates was created in the first place, including redundancy and lack of modularity.&lt;br /&gt;
&lt;br /&gt;
Modeling &amp;amp; Structural Issues&lt;br /&gt;
# No dedicated &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to define different kinds of deadlines. The existing &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; class only serves as an association for &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; models, and is never referenced in the current &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; implementation.&lt;br /&gt;
# Duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table, which may lead to potential bugs.&lt;br /&gt;
# Incomplete deadline type definitions - &amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt; need to be added.&lt;br /&gt;
# No clear separation of concerns - data persistence, business logic, and permission logic are mixed in a single model.&lt;br /&gt;
&lt;br /&gt;
Permission &amp;amp; Behavior Issues&lt;br /&gt;
# Overuse of class methods making the code difficult to maintain.&lt;br /&gt;
# Missing permission checking logic for user actions.&lt;br /&gt;
&lt;br /&gt;
=== Tasks Identified ===&lt;br /&gt;
&lt;br /&gt;
# Redesign the &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.&lt;br /&gt;
# Refactor the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model to separate persistence, business logic, and permission logic into distinct layers.&lt;br /&gt;
# Introduce instance-level methods (e.g., &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;) to replace class-level utilities and improve testability.&lt;br /&gt;
# Integrate role-based and time-based permission checks within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; to ensure consistent access control.&lt;br /&gt;
# Add missing deadline types (&amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt;) and remove duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries for data integrity.&lt;br /&gt;
# Implement reusable mixins (e.g., &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;) to handle permission evaluation and deadline-related operations.&lt;br /&gt;
&lt;br /&gt;
== Proposed Solution ==&lt;br /&gt;
&lt;br /&gt;
[[File:Duedate-diagram.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== DeadlineType Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Although the current codebase includes a &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model, it only serves as a passive lookup table for associations with &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt;.  &lt;br /&gt;
In practice, most parts of the system still rely on hard-coded numeric IDs or manual lookups such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Example of current usage&lt;br /&gt;
deadline_type_id = DeadlineType.find_by(name: 'review').id&lt;br /&gt;
if due_date.deadline_type_id == deadline_type_id&lt;br /&gt;
  # Perform review-related logic&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of using the model as an active domain object, &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; directly compares IDs or string literals.  &lt;br /&gt;
This leads to several structural issues:&lt;br /&gt;
&lt;br /&gt;
* Inconsistent references (some use IDs, others strings)&lt;br /&gt;
* Tight coupling between &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
* Lack of helper semantics (e.g., &amp;lt;code&amp;gt;is_review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;is_submission?&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Data duplication and integrity risks (two &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; rows)&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; will serve as a source of truth, defining all valid deadline types and providing meaningful interfaces.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE deadline_types (&lt;br /&gt;
  id BIGINT PRIMARY KEY,&lt;br /&gt;
  name VARCHAR(255) NOT NULL UNIQUE,&lt;br /&gt;
  description TEXT NOT NULL,&lt;br /&gt;
  created_at TIMESTAMP,&lt;br /&gt;
  updated_at TIMESTAMP&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Deadline Type Definitions ====&lt;br /&gt;
* &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;: submission - Student work submission deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt;: review - Peer review deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;3&amp;lt;/code&amp;gt;: teammate_review - Team member evaluation deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;5&amp;lt;/code&amp;gt;: metareview - Meta-review deadlines (kept for backward compatibility)  &lt;br /&gt;
* &amp;lt;code&amp;gt;6&amp;lt;/code&amp;gt;: drop_topic - Topic drop deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;7&amp;lt;/code&amp;gt;: signup - Course/assignment signup deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;8&amp;lt;/code&amp;gt;: team_formation - Team formation deadlines (clean up duplicate ID 10)  &lt;br /&gt;
* &amp;lt;code&amp;gt;11&amp;lt;/code&amp;gt;: quiz - Quiz completion deadlines&lt;br /&gt;
&lt;br /&gt;
==== DeadlineType Model Implementation ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineType &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
  validates :description, presence: true&lt;br /&gt;
&lt;br /&gt;
  has_many :due_dates, foreign_key: :deadline_type_id, dependent: :restrict_with_exception&lt;br /&gt;
&lt;br /&gt;
  # Semantic helper methods for deadline type identification&lt;br /&gt;
  def submission?&lt;br /&gt;
    name == 'submission'&lt;br /&gt;
  end&lt;br /&gt;
  # same code for review, teammate_review, quiz, team_formation, signup, drop_topic   &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DeadlineRight Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model represents the permission level for a specific action at a given deadline.&lt;br /&gt;
It defines three states: &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt; (action allowed), &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt; (action allowed with penalty), and &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt; (action denied).&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE deadline_rights (&lt;br /&gt;
  id BIGINT PRIMARY KEY,&lt;br /&gt;
  name VARCHAR(255) NOT NULL UNIQUE,&lt;br /&gt;
  created_at TIMESTAMP,&lt;br /&gt;
  updated_at TIMESTAMP&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== DeadlineRight Model ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineRight &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
&lt;br /&gt;
  # Permission checking methods&lt;br /&gt;
  def allows_action?&lt;br /&gt;
    %w[OK Late].include?(name)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def denies_action?&lt;br /&gt;
    name == 'No'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def allows_with_penalty?&lt;br /&gt;
    name == 'Late'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def allows_without_penalty?&lt;br /&gt;
    name == 'OK'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Display methods&lt;br /&gt;
  def to_s&lt;br /&gt;
    name&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def permission_level&lt;br /&gt;
    case name&lt;br /&gt;
    when 'No'   then 0&lt;br /&gt;
    when 'Late' then 1&lt;br /&gt;
    when 'OK'   then 2&lt;br /&gt;
    else -1&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== DueDate Model Refactoring ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model currently mixes persistence logic, deadline calculation, and permission checking, violating SRP.  &lt;br /&gt;
Most of its logic is implemented as class methods, which makes testing and extension difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Current design&lt;br /&gt;
def self.copy(old_assignment_id, new_assignment_id)&lt;br /&gt;
  duedates = where(parent_id: old_assignment_id)&lt;br /&gt;
  duedates.each do |orig_due_date|&lt;br /&gt;
    new_due_date = orig_due_date.dup&lt;br /&gt;
    new_due_date.parent_id = new_assignment_id&lt;br /&gt;
    new_due_date.save&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Improved Design ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
&lt;br /&gt;
  belongs_to :parent, polymorphic: true&lt;br /&gt;
  belongs_to :deadline_type, foreign_key: :deadline_type_id&lt;br /&gt;
&lt;br /&gt;
  validates :due_at, presence: true&lt;br /&gt;
  validates :deadline_type_id, presence: true&lt;br /&gt;
  validates :parent, presence: true&lt;br /&gt;
  validates :round, numericality: { greater_than: 0 }, allow_nil: true&lt;br /&gt;
&lt;br /&gt;
  # Scopes for common queries&lt;br /&gt;
  scope :upcoming, -&amp;gt; { where('due_at &amp;gt; ?', Time.current).order(:due_at) }&lt;br /&gt;
  scope :overdue, -&amp;gt; { where('due_at &amp;lt; ?', Time.current).order(:due_at) }&lt;br /&gt;
  scope :for_round, -&amp;gt;(round_num) { where(round: round_num) }&lt;br /&gt;
  scope :for_deadline_type, -&amp;gt;(type_name) { joins(:deadline_type).where(deadline_types: { name: type_name }) }&lt;br /&gt;
&lt;br /&gt;
  # Instance methods replace class methods&lt;br /&gt;
  def copy(to_assignment_id)&lt;br /&gt;
    to_assignment = Assignment.find(to_assignment_id)&lt;br /&gt;
    new_due_date = dup&lt;br /&gt;
    new_due_date.parent = to_assignment&lt;br /&gt;
    new_due_date.save!&lt;br /&gt;
    new_due_date&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def set(deadline_type_id, parent_id, round)&lt;br /&gt;
    self.deadline_type_id = deadline_type_id&lt;br /&gt;
    self.parent_id = parent_id&lt;br /&gt;
    self.round = round&lt;br /&gt;
    save!&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if this deadline has passed&lt;br /&gt;
  def overdue?&lt;br /&gt;
    due_at &amp;lt; Time.current&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if this deadline is upcoming&lt;br /&gt;
  def upcoming?&lt;br /&gt;
    due_at &amp;gt; Time.current&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the deadline type name&lt;br /&gt;
  def deadline_type_name&lt;br /&gt;
    deadline_type&amp;amp;.name&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Comparison method for sorting&lt;br /&gt;
  def &amp;lt;=&amp;gt;(other)&lt;br /&gt;
    return nil unless other.is_a?(DueDate)&lt;br /&gt;
    due_at &amp;lt;=&amp;gt; other.due_at&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Class methods for collection operations&lt;br /&gt;
  class &amp;lt;&amp;lt; self&lt;br /&gt;
    def sort_due_dates(due_dates)&lt;br /&gt;
      due_dates.sort_by(&amp;amp;:due_at)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    def any_future_due_dates?(due_dates)&lt;br /&gt;
      due_dates.any?(&amp;amp;:upcoming?)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    def copy(from_assignment_id, to_assignment_id)&lt;br /&gt;
      from_assignment = Assignment.find(from_assignment_id)&lt;br /&gt;
      to_assignment = Assignment.find(to_assignment_id)&lt;br /&gt;
&lt;br /&gt;
      from_assignment.due_dates.each do |due_date|&lt;br /&gt;
        new_due_date = due_date.dup&lt;br /&gt;
        new_due_date.parent = to_assignment&lt;br /&gt;
        new_due_date.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  before_save :set_default_round&lt;br /&gt;
&lt;br /&gt;
  def set_default_round&lt;br /&gt;
    self.round ||= 1&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Permission-Checking Methods ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Currently, permission checks depend only on user roles and ignore the actual deadline.  &lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Role-based permissions (participant_helpers.rb)&lt;br /&gt;
def participant_permissions(authorization)&lt;br /&gt;
  case authorization&lt;br /&gt;
  when 'reader'   then { can_submit: false, can_review: true, can_take_quiz: true }&lt;br /&gt;
  when 'reviewer' then { can_submit: false, can_review: true, can_take_quiz: false }&lt;br /&gt;
  when 'submitter' then { can_submit: true, can_review: false, can_take_quiz: false }&lt;br /&gt;
  else { can_submit: true, can_review: true, can_take_quiz: true }&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And deadline-based checks exist separately in the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def submission_allowed(topic_id = nil)&lt;br /&gt;
  check_condition('submission_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
def can_review(topic_id = nil)&lt;br /&gt;
  check_condition('review_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These two layers (role-based and time-based) never interact, leading to inconsistencies (e.g., a user with &amp;lt;code&amp;gt;can_submit=true&amp;lt;/code&amp;gt; after deadline).&lt;br /&gt;
&lt;br /&gt;
==== Proposed Changes ====&lt;br /&gt;
&lt;br /&gt;
The new &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module integrates role-based and deadline-based checks:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDatePermissions&lt;br /&gt;
  # Permission checking methods that combine deadline-based and role-based logic&lt;br /&gt;
  #&lt;br /&gt;
  # These methods must check both:&lt;br /&gt;
  # 1. Whether the action is permitted by the current deadline (submission_allowed_id, review_allowed_id, etc.)&lt;br /&gt;
  # 2. Whether the participant has the necessary permissions (can_submit, can_review, can_take_quiz fields)&lt;br /&gt;
&lt;br /&gt;
  # Check if submission is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_submit?(participant = nil)&lt;br /&gt;
    return false unless submission_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_submit&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: submission_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if review is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_review?(participant = nil)&lt;br /&gt;
    return false unless review_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_review&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: review_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if taking a quiz is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_take_quiz?(participant = nil)&lt;br /&gt;
    return false unless quiz_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_take_quiz&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: quiz_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if teammate review is allowed&lt;br /&gt;
  def can_review_teammate?(participant = nil)&lt;br /&gt;
    return false unless teammate_review_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_review&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: teammate_review_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Generic permission checker&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    permission_field = &amp;quot;#{activity}_allowed_id&amp;quot;&lt;br /&gt;
    return false unless respond_to?(permission_field)&lt;br /&gt;
&lt;br /&gt;
    allowed_id = public_send(permission_field)&lt;br /&gt;
    return false unless allowed_id&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get permission status for an action (OK, Late, No)&lt;br /&gt;
  def permission_status_for(action)&lt;br /&gt;
    permission_field = &amp;quot;#{action}_allowed_id&amp;quot;&lt;br /&gt;
    return 'No' unless respond_to?(permission_field)&lt;br /&gt;
&lt;br /&gt;
    allowed_id = public_send(permission_field)&lt;br /&gt;
    return 'No' unless allowed_id&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name || 'No'&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This module is included in the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 5. DueDateActions Mixin ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module provides deadline-related operations for models that have due dates (Assignment, SignUpTopic).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDateActions&lt;br /&gt;
  # Generic activity permission checker that determines if an activity is permissible&lt;br /&gt;
  # based on the current deadline state for this parent object&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    current_deadline = next_due_date()&lt;br /&gt;
    return false unless current_deadline&lt;br /&gt;
&lt;br /&gt;
    current_deadline.activity_permissible?(activity)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Syntactic sugar methods for common activities&lt;br /&gt;
  def submission_permissible?&lt;br /&gt;
    activity_permissible?(:submission)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def review_permissible?&lt;br /&gt;
    activity_permissible?(:review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def teammate_review_permissible?&lt;br /&gt;
    activity_permissible?(:teammate_review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def quiz_permissible?&lt;br /&gt;
    activity_permissible?(:quiz)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def team_formation_permissible?&lt;br /&gt;
    activity_permissible?(:team_formation)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def signup_permissible?&lt;br /&gt;
    activity_permissible?(:signup)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def drop_topic_permissible?&lt;br /&gt;
    activity_permissible?(:drop_topic)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the next due date for this assignment&lt;br /&gt;
  def next_due_date(topic_id = nil)&lt;br /&gt;
    # If a topic is specified and this assignment has topic-specific deadlines,&lt;br /&gt;
    # look for topic due dates first&lt;br /&gt;
    if topic_id &amp;amp;&amp;amp; has_topic_specific_deadlines?&lt;br /&gt;
      topic_deadline = due_dates.where(parent_id: topic_id, parent_type: 'SignUpTopic')&lt;br /&gt;
                               .upcoming&lt;br /&gt;
                               .first&lt;br /&gt;
      return topic_deadline if topic_deadline&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Fall back to assignment-level due dates&lt;br /&gt;
    due_dates.upcoming.first&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the current stage name for display purposes&lt;br /&gt;
  def current_stage&lt;br /&gt;
    deadline = next_due_date()&lt;br /&gt;
    return 'finished' unless deadline&lt;br /&gt;
&lt;br /&gt;
    deadline.deadline_type&amp;amp;.name || 'unknown'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if assignment has topic-specific deadlines&lt;br /&gt;
  def has_topic_specific_deadlines?&lt;br /&gt;
    staggered_deadline || due_dates.where(parent_type: 'SignUpTopic').exists?&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Copy due dates to a new parent object&lt;br /&gt;
  def copy_due_dates_to(new_parent)&lt;br /&gt;
    due_dates.find_each do |due_date|&lt;br /&gt;
      new_due_date = due_date.dup&lt;br /&gt;
      new_due_date.parent = new_parent&lt;br /&gt;
      new_due_date.save!&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This module is included in &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class Assignment &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
class SignUpTopic &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Implementation Summary ==&lt;br /&gt;
&lt;br /&gt;
=== Models Created/Modified ===&lt;br /&gt;
&lt;br /&gt;
* '''DeadlineType''' - Canonical deadline type definitions with semantic helper methods&lt;br /&gt;
* '''DeadlineRight''' - Permission state model (OK/Late/No) with comparison methods&lt;br /&gt;
* '''DueDate''' - Refactored with instance methods, scopes, and permission checking&lt;br /&gt;
* '''TopicDueDate''' - STI subclass for topic-specific deadlines&lt;br /&gt;
* '''Assignment''' - Includes DueDateActions mixin&lt;br /&gt;
* '''SignUpTopic''' - Includes DueDateActions mixin&lt;br /&gt;
&lt;br /&gt;
=== Modules Created ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions''' - Permission checking combining deadline and role constraints&lt;br /&gt;
* '''DueDateActions''' - Deadline-related operations for parent models (Assignment, SignUpTopic)&lt;br /&gt;
&lt;br /&gt;
=== Key Improvements ===&lt;br /&gt;
&lt;br /&gt;
# '''Separation of Concerns''': Permission logic separated into dedicated modules&lt;br /&gt;
# '''Instance Methods''': Replaced class methods with testable instance methods&lt;br /&gt;
# '''Polymorphic Design''': Single DueDate model serves both Assignment and SignUpTopic&lt;br /&gt;
# '''Unified Permissions''': Role-based and time-based checks combined in one interface&lt;br /&gt;
# '''Data Integrity''': Removed duplicate deadline types, enforced foreign key constraints&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
The test suite includes comprehensive RSpec tests for the ''DueDate'' model and its associated models (''DeadlineType'', ''DeadlineRight'').&lt;br /&gt;
These tests verify model validations, scopes, instance methods, class methods, and callbacks with extensive coverage of edge cases and error conditions.&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
The test suite follows RSpec best practices with:&lt;br /&gt;
* '''Nested contexts''' for different scenarios (e.g., &amp;quot;when parent is missing&amp;quot;, &amp;quot;when round is 0&amp;quot;)&lt;br /&gt;
* '''Multiple assertions per context''' to verify behavior from different angles&lt;br /&gt;
* '''Edge case coverage''' including empty collections, nil values, and boundary conditions&lt;br /&gt;
* '''Error case testing''' for invalid data and non-existent records&lt;br /&gt;
* '''Clear descriptive names''' using &amp;quot;context 'when...'&amp;quot; and &amp;quot;it 'does something'&amp;quot; patterns&lt;br /&gt;
&lt;br /&gt;
=== Current Coverage ===&lt;br /&gt;
&lt;br /&gt;
==== Associations ====&lt;br /&gt;
* Tests verify &amp;lt;code&amp;gt;belongs_to :parent&amp;lt;/code&amp;gt; (polymorphic) and &amp;lt;code&amp;gt;belongs_to :deadline_type&amp;lt;/code&amp;gt; relationships&lt;br /&gt;
&lt;br /&gt;
==== Validations ====&lt;br /&gt;
* '''Required field validation''': Tests for parent, due_at, and deadline_type_id presence&lt;br /&gt;
* '''Round validation''': Tests for positive values, zero rejection, negative rejection, and nil handling&lt;br /&gt;
* '''Error messages''': Verifies specific error messages are added to appropriate fields&lt;br /&gt;
* '''Valid record creation''': Confirms records persist when all requirements are met&lt;br /&gt;
&lt;br /&gt;
==== Scopes ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;''': Returns future due dates ordered by due_at, excludes past dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;''': Returns past due dates ordered by due_at, excludes future dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;''': Filters by round number, handles non-existent rounds&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt;''': Filters by deadline type name, handles non-existent types&lt;br /&gt;
&lt;br /&gt;
==== Instance Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#overdue?&amp;lt;/code&amp;gt;''': Tests past dates (true), future dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#upcoming?&amp;lt;/code&amp;gt;''': Tests future dates (true), past dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#set&amp;lt;/code&amp;gt;''': Verifies updating deadline_type_id, parent_id, and round with persistence and error handling&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#copy&amp;lt;/code&amp;gt;''': Tests duplication to new assignment, attribute preservation, error handling for non-existent targets&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#deadline_type_name&amp;lt;/code&amp;gt;''': Returns associated deadline type name, handles nil cases&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#last_deadline?&amp;lt;/code&amp;gt;''': Tests detection of final deadline, handles multiple deadlines and single deadline scenarios&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#&amp;lt;=&amp;gt;&amp;lt;/code&amp;gt;''': Tests comparison by due_at time, handles non-DueDate objects&lt;br /&gt;
&lt;br /&gt;
==== Class Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.sort_due_dates&amp;lt;/code&amp;gt;''': Sorts by due_at ascending, handles empty arrays and single elements&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.any_future_due_dates?&amp;lt;/code&amp;gt;''': Detects future dates in collections, handles empty arrays and mixed date collections&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.copy&amp;lt;/code&amp;gt;''': Copies all due dates between assignments, preserves attributes, handles empty sources and non-existent records&lt;br /&gt;
&lt;br /&gt;
==== Callbacks ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;before_save :set_default_round&amp;lt;/code&amp;gt;''': Sets round to 1 when not specified, preserves explicit values, handles nil&lt;br /&gt;
&lt;br /&gt;
=== Test Examples ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Validation with multiple assertions&lt;br /&gt;
describe 'validations' do&lt;br /&gt;
  context 'when parent is missing' do&lt;br /&gt;
    let(:due_date) do&lt;br /&gt;
      DueDate.new(&lt;br /&gt;
        parent: nil,&lt;br /&gt;
        due_at: 2.days.from_now,&lt;br /&gt;
        deadline_type: submission_type,&lt;br /&gt;
        submission_allowed_id: ok_right.id,&lt;br /&gt;
        review_allowed_id: ok_right.id&lt;br /&gt;
      )&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'is invalid' do&lt;br /&gt;
      expect(due_date).to be_invalid&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'has error on parent field' do&lt;br /&gt;
      due_date.valid?&lt;br /&gt;
      expect(due_date.errors[:parent]).to include('must exist')&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when round validation' do&lt;br /&gt;
    context 'with round value of 0' do&lt;br /&gt;
      let(:due_date) do&lt;br /&gt;
        DueDate.new(&lt;br /&gt;
          parent: assignment,&lt;br /&gt;
          due_at: 2.days.from_now,&lt;br /&gt;
          deadline_type: submission_type,&lt;br /&gt;
          submission_allowed_id: ok_right.id,&lt;br /&gt;
          review_allowed_id: ok_right.id,&lt;br /&gt;
          round: 0&lt;br /&gt;
        )&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'is invalid' do&lt;br /&gt;
        expect(due_date).to be_invalid&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'has error on round field' do&lt;br /&gt;
        due_date.valid?&lt;br /&gt;
        expect(due_date.errors[:round]).to be_present&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'with nil round' do&lt;br /&gt;
      let(:due_date) do&lt;br /&gt;
        DueDate.create(&lt;br /&gt;
          parent: assignment,&lt;br /&gt;
          due_at: 2.days.from_now,&lt;br /&gt;
          deadline_type: submission_type,&lt;br /&gt;
          submission_allowed_id: ok_right.id,&lt;br /&gt;
          review_allowed_id: ok_right.id,&lt;br /&gt;
          round: nil&lt;br /&gt;
        )&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'is valid' do&lt;br /&gt;
        expect(due_date).to be_valid&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'sets default round to 1' do&lt;br /&gt;
        expect(due_date.round).to eq(1)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
# Scope with edge cases&lt;br /&gt;
describe '.upcoming' do&lt;br /&gt;
  it 'returns only future due dates' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).to contain_exactly(upcoming_due_date1, upcoming_due_date2)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'orders results by due_at ascending' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).to eq([upcoming_due_date1, upcoming_due_date2])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'excludes past due dates' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).not_to include(past_due_date)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when all due dates are in the past' do&lt;br /&gt;
    before do&lt;br /&gt;
      DueDate.destroy_all&lt;br /&gt;
      DueDate.create(&lt;br /&gt;
        parent: assignment,&lt;br /&gt;
        due_at: 3.days.ago,&lt;br /&gt;
        deadline_type: submission_type,&lt;br /&gt;
        submission_allowed_id: ok_right.id,&lt;br /&gt;
        review_allowed_id: ok_right.id&lt;br /&gt;
      )&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns empty collection' do&lt;br /&gt;
      expect(DueDate.upcoming).to be_empty&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
# Instance method with error handling&lt;br /&gt;
describe '#copy' do&lt;br /&gt;
  let(:original) do&lt;br /&gt;
    DueDate.create(&lt;br /&gt;
      parent: assignment,&lt;br /&gt;
      due_at: 2.days.from_now,&lt;br /&gt;
      deadline_type: submission_type,&lt;br /&gt;
      submission_allowed_id: ok_right.id,&lt;br /&gt;
      review_allowed_id: late_right.id,&lt;br /&gt;
      round: 1&lt;br /&gt;
    )&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  let(:copied) { original.copy(assignment2.id) }&lt;br /&gt;
&lt;br /&gt;
  it 'creates a new persisted record' do&lt;br /&gt;
    expect(copied).to be_persisted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'has different id from original' do&lt;br /&gt;
    expect(copied.id).not_to eq(original.id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'preserves all attributes' do&lt;br /&gt;
    expect(copied.due_at).to eq(original.due_at)&lt;br /&gt;
    expect(copied.deadline_type_id).to eq(original.deadline_type_id)&lt;br /&gt;
    expect(copied.round).to eq(original.round)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when copying to non-existent assignment' do&lt;br /&gt;
    it 'raises error' do&lt;br /&gt;
      expect do&lt;br /&gt;
        original.copy(99999)&lt;br /&gt;
      end.to raise_error(ActiveRecord::RecordNotFound)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Test Metrics ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDate model''': 60+ examples covering all methods and edge cases&lt;br /&gt;
* '''Context depth''': 2-3 levels for complex scenarios&lt;br /&gt;
* '''Code coverage''': 100% coverage of DueDate model methods&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
=== Demo Link ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add deadline notification system using the new permission framework&lt;br /&gt;
* Implement deadline templates for common assignment patterns&lt;br /&gt;
* Add API endpoints for mobile app integration&lt;br /&gt;
* Create admin dashboard for monitoring deadline compliance across courses&lt;br /&gt;
* Add automated deadline extension workflows based on configurable rules&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167182</id>
		<title>CSC/ECE 517 Fall 2025 - E2566. Finish DueDates</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167182"/>
		<updated>2025-12-02T00:27:39Z</updated>

		<summary type="html">&lt;p&gt;Skim253: /* Tasks Identified */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
This project aims to complete the implementation of the DueDate system in Expertiza. The current implementation consists mostly of class methods and lacks proper definition of different deadline types and permission checking functionality. This redesign will create a more robust, readable, and maintainable due date system.&lt;br /&gt;
&lt;br /&gt;
=== Existing Components ===&lt;br /&gt;
* &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model with polymorphic parent association (Assignment/SignUpTopic)&lt;br /&gt;
* &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; subclasses&lt;br /&gt;
* Basic CRUD operations (within &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AssignmentForm&amp;lt;/code&amp;gt;) and sorting functionality (&amp;lt;code&amp;gt;deadline_sort&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Database schema with &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt; field&lt;br /&gt;
&lt;br /&gt;
=== Current Behavior ===&lt;br /&gt;
&lt;br /&gt;
====Admin's View====&lt;br /&gt;
[[File:Duedates.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* When editing an assignment, due dates can be modified under the Due Dates tab.&lt;br /&gt;
* Each row contains the following columns: &amp;lt;code&amp;gt;Date &amp;amp; Time&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Use Date Updater&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Submission Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Teammate Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Meta-Review Allowed&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Reminder&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:Assignments-CurrentStage.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* In the Assignments table, the ''DueDate'' of each assignment is displayed as Current Stage and Stage Deadline.&lt;br /&gt;
&lt;br /&gt;
====Student's View====&lt;br /&gt;
[[File:Student-assignments.png|600px]]&lt;br /&gt;
* Students can view the due date information under &amp;lt;code&amp;gt;Current State&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Stage Deadline&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
* The actions available to students are determined by the current DueDate status.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-not-over.png|600px]]&lt;br /&gt;
* For example, during the submission period, students can access the &amp;lt;code&amp;gt;Submit a hyperlink&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Submit a file&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-over.png|600px]]&lt;br /&gt;
* Once the due date has passed, students cannot submit either a hyperlink or a file.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Currently, there are some glaring issues with how due_dates was created in the first place, including redundancy and lack of modularity.&lt;br /&gt;
&lt;br /&gt;
# No dedicated &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to define different kinds of deadlines. The existing &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; class only serves as an association for &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; models, and is never referenced in the current &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; implementation.&lt;br /&gt;
# Overuse of class methods making the code difficult to maintain.&lt;br /&gt;
# Missing permission checking logic for user actions.&lt;br /&gt;
# Incomplete deadline type definitions — &amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt; need to be added.&lt;br /&gt;
# Duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table, which may lead to potential bugs.&lt;br /&gt;
# No clear separation of concerns — data persistence, business logic, and permission logic are mixed in a single model.&lt;br /&gt;
&lt;br /&gt;
=== Tasks Identified ===&lt;br /&gt;
&lt;br /&gt;
# Redesign the &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.&lt;br /&gt;
# Refactor the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model to separate persistence, business logic, and permission logic into distinct layers.&lt;br /&gt;
# Introduce instance-level methods (e.g., &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;) to replace class-level utilities and improve testability.&lt;br /&gt;
# Integrate role-based and time-based permission checks within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; to ensure consistent access control.&lt;br /&gt;
# Add missing deadline types (&amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt;) and remove duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries for data integrity.&lt;br /&gt;
# Implement reusable mixins (e.g., &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;) to handle permission evaluation and deadline-related operations.&lt;br /&gt;
&lt;br /&gt;
== Proposed Solution ==&lt;br /&gt;
&lt;br /&gt;
[[File:Duedate-diagram.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== DeadlineType Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Although the current codebase includes a &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model, it only serves as a passive lookup table for associations with &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt;.  &lt;br /&gt;
In practice, most parts of the system still rely on hard-coded numeric IDs or manual lookups such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Example of current usage&lt;br /&gt;
deadline_type_id = DeadlineType.find_by(name: 'review').id&lt;br /&gt;
if due_date.deadline_type_id == deadline_type_id&lt;br /&gt;
  # Perform review-related logic&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of using the model as an active domain object, &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; directly compares IDs or string literals.  &lt;br /&gt;
This leads to several structural issues:&lt;br /&gt;
&lt;br /&gt;
* Inconsistent references (some use IDs, others strings)&lt;br /&gt;
* Tight coupling between &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
* Lack of helper semantics (e.g., &amp;lt;code&amp;gt;is_review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;is_submission?&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Data duplication and integrity risks (two &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; rows)&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; will serve as a source of truth, defining all valid deadline types and providing meaningful interfaces.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE deadline_types (&lt;br /&gt;
  id BIGINT PRIMARY KEY,&lt;br /&gt;
  name VARCHAR(255) NOT NULL UNIQUE,&lt;br /&gt;
  description TEXT NOT NULL,&lt;br /&gt;
  created_at TIMESTAMP,&lt;br /&gt;
  updated_at TIMESTAMP&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Deadline Type Definitions ====&lt;br /&gt;
* &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;: submission - Student work submission deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt;: review - Peer review deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;3&amp;lt;/code&amp;gt;: teammate_review - Team member evaluation deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;5&amp;lt;/code&amp;gt;: metareview - Meta-review deadlines (kept for backward compatibility)  &lt;br /&gt;
* &amp;lt;code&amp;gt;6&amp;lt;/code&amp;gt;: drop_topic - Topic drop deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;7&amp;lt;/code&amp;gt;: signup - Course/assignment signup deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;8&amp;lt;/code&amp;gt;: team_formation - Team formation deadlines (clean up duplicate ID 10)  &lt;br /&gt;
* &amp;lt;code&amp;gt;11&amp;lt;/code&amp;gt;: quiz - Quiz completion deadlines&lt;br /&gt;
&lt;br /&gt;
==== DeadlineType Model Implementation ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineType &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
  validates :description, presence: true&lt;br /&gt;
&lt;br /&gt;
  has_many :due_dates, foreign_key: :deadline_type_id, dependent: :restrict_with_exception&lt;br /&gt;
&lt;br /&gt;
  # Semantic helper methods for deadline type identification&lt;br /&gt;
  def submission?&lt;br /&gt;
    name == 'submission'&lt;br /&gt;
  end&lt;br /&gt;
  # same code for review, teammate_review, quiz, team_formation, signup, drop_topic   &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DeadlineRight Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model represents the permission level for a specific action at a given deadline.&lt;br /&gt;
It defines three states: &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt; (action allowed), &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt; (action allowed with penalty), and &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt; (action denied).&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE deadline_rights (&lt;br /&gt;
  id BIGINT PRIMARY KEY,&lt;br /&gt;
  name VARCHAR(255) NOT NULL UNIQUE,&lt;br /&gt;
  created_at TIMESTAMP,&lt;br /&gt;
  updated_at TIMESTAMP&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== DeadlineRight Model ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineRight &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
&lt;br /&gt;
  # Permission checking methods&lt;br /&gt;
  def allows_action?&lt;br /&gt;
    %w[OK Late].include?(name)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def denies_action?&lt;br /&gt;
    name == 'No'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def allows_with_penalty?&lt;br /&gt;
    name == 'Late'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def allows_without_penalty?&lt;br /&gt;
    name == 'OK'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Display methods&lt;br /&gt;
  def to_s&lt;br /&gt;
    name&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def permission_level&lt;br /&gt;
    case name&lt;br /&gt;
    when 'No'   then 0&lt;br /&gt;
    when 'Late' then 1&lt;br /&gt;
    when 'OK'   then 2&lt;br /&gt;
    else -1&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== DueDate Model Refactoring ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model currently mixes persistence logic, deadline calculation, and permission checking, violating SRP.  &lt;br /&gt;
Most of its logic is implemented as class methods, which makes testing and extension difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Current design&lt;br /&gt;
def self.copy(old_assignment_id, new_assignment_id)&lt;br /&gt;
  duedates = where(parent_id: old_assignment_id)&lt;br /&gt;
  duedates.each do |orig_due_date|&lt;br /&gt;
    new_due_date = orig_due_date.dup&lt;br /&gt;
    new_due_date.parent_id = new_assignment_id&lt;br /&gt;
    new_due_date.save&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Improved Design ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
&lt;br /&gt;
  belongs_to :parent, polymorphic: true&lt;br /&gt;
  belongs_to :deadline_type, foreign_key: :deadline_type_id&lt;br /&gt;
&lt;br /&gt;
  validates :due_at, presence: true&lt;br /&gt;
  validates :deadline_type_id, presence: true&lt;br /&gt;
  validates :parent, presence: true&lt;br /&gt;
  validates :round, numericality: { greater_than: 0 }, allow_nil: true&lt;br /&gt;
&lt;br /&gt;
  # Scopes for common queries&lt;br /&gt;
  scope :upcoming, -&amp;gt; { where('due_at &amp;gt; ?', Time.current).order(:due_at) }&lt;br /&gt;
  scope :overdue, -&amp;gt; { where('due_at &amp;lt; ?', Time.current).order(:due_at) }&lt;br /&gt;
  scope :for_round, -&amp;gt;(round_num) { where(round: round_num) }&lt;br /&gt;
  scope :for_deadline_type, -&amp;gt;(type_name) { joins(:deadline_type).where(deadline_types: { name: type_name }) }&lt;br /&gt;
&lt;br /&gt;
  # Instance methods replace class methods&lt;br /&gt;
  def copy(to_assignment_id)&lt;br /&gt;
    to_assignment = Assignment.find(to_assignment_id)&lt;br /&gt;
    new_due_date = dup&lt;br /&gt;
    new_due_date.parent = to_assignment&lt;br /&gt;
    new_due_date.save!&lt;br /&gt;
    new_due_date&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def set(deadline_type_id, parent_id, round)&lt;br /&gt;
    self.deadline_type_id = deadline_type_id&lt;br /&gt;
    self.parent_id = parent_id&lt;br /&gt;
    self.round = round&lt;br /&gt;
    save!&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if this deadline has passed&lt;br /&gt;
  def overdue?&lt;br /&gt;
    due_at &amp;lt; Time.current&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if this deadline is upcoming&lt;br /&gt;
  def upcoming?&lt;br /&gt;
    due_at &amp;gt; Time.current&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the deadline type name&lt;br /&gt;
  def deadline_type_name&lt;br /&gt;
    deadline_type&amp;amp;.name&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Comparison method for sorting&lt;br /&gt;
  def &amp;lt;=&amp;gt;(other)&lt;br /&gt;
    return nil unless other.is_a?(DueDate)&lt;br /&gt;
    due_at &amp;lt;=&amp;gt; other.due_at&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Class methods for collection operations&lt;br /&gt;
  class &amp;lt;&amp;lt; self&lt;br /&gt;
    def sort_due_dates(due_dates)&lt;br /&gt;
      due_dates.sort_by(&amp;amp;:due_at)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    def any_future_due_dates?(due_dates)&lt;br /&gt;
      due_dates.any?(&amp;amp;:upcoming?)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    def copy(from_assignment_id, to_assignment_id)&lt;br /&gt;
      from_assignment = Assignment.find(from_assignment_id)&lt;br /&gt;
      to_assignment = Assignment.find(to_assignment_id)&lt;br /&gt;
&lt;br /&gt;
      from_assignment.due_dates.each do |due_date|&lt;br /&gt;
        new_due_date = due_date.dup&lt;br /&gt;
        new_due_date.parent = to_assignment&lt;br /&gt;
        new_due_date.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  before_save :set_default_round&lt;br /&gt;
&lt;br /&gt;
  def set_default_round&lt;br /&gt;
    self.round ||= 1&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Permission-Checking Methods ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Currently, permission checks depend only on user roles and ignore the actual deadline.  &lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Role-based permissions (participant_helpers.rb)&lt;br /&gt;
def participant_permissions(authorization)&lt;br /&gt;
  case authorization&lt;br /&gt;
  when 'reader'   then { can_submit: false, can_review: true, can_take_quiz: true }&lt;br /&gt;
  when 'reviewer' then { can_submit: false, can_review: true, can_take_quiz: false }&lt;br /&gt;
  when 'submitter' then { can_submit: true, can_review: false, can_take_quiz: false }&lt;br /&gt;
  else { can_submit: true, can_review: true, can_take_quiz: true }&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And deadline-based checks exist separately in the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def submission_allowed(topic_id = nil)&lt;br /&gt;
  check_condition('submission_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
def can_review(topic_id = nil)&lt;br /&gt;
  check_condition('review_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These two layers (role-based and time-based) never interact, leading to inconsistencies (e.g., a user with &amp;lt;code&amp;gt;can_submit=true&amp;lt;/code&amp;gt; after deadline).&lt;br /&gt;
&lt;br /&gt;
==== Proposed Changes ====&lt;br /&gt;
&lt;br /&gt;
The new &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module integrates role-based and deadline-based checks:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDatePermissions&lt;br /&gt;
  # Permission checking methods that combine deadline-based and role-based logic&lt;br /&gt;
  #&lt;br /&gt;
  # These methods must check both:&lt;br /&gt;
  # 1. Whether the action is permitted by the current deadline (submission_allowed_id, review_allowed_id, etc.)&lt;br /&gt;
  # 2. Whether the participant has the necessary permissions (can_submit, can_review, can_take_quiz fields)&lt;br /&gt;
&lt;br /&gt;
  # Check if submission is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_submit?(participant = nil)&lt;br /&gt;
    return false unless submission_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_submit&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: submission_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if review is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_review?(participant = nil)&lt;br /&gt;
    return false unless review_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_review&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: review_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if taking a quiz is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_take_quiz?(participant = nil)&lt;br /&gt;
    return false unless quiz_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_take_quiz&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: quiz_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if teammate review is allowed&lt;br /&gt;
  def can_review_teammate?(participant = nil)&lt;br /&gt;
    return false unless teammate_review_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_review&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: teammate_review_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Generic permission checker&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    permission_field = &amp;quot;#{activity}_allowed_id&amp;quot;&lt;br /&gt;
    return false unless respond_to?(permission_field)&lt;br /&gt;
&lt;br /&gt;
    allowed_id = public_send(permission_field)&lt;br /&gt;
    return false unless allowed_id&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get permission status for an action (OK, Late, No)&lt;br /&gt;
  def permission_status_for(action)&lt;br /&gt;
    permission_field = &amp;quot;#{action}_allowed_id&amp;quot;&lt;br /&gt;
    return 'No' unless respond_to?(permission_field)&lt;br /&gt;
&lt;br /&gt;
    allowed_id = public_send(permission_field)&lt;br /&gt;
    return 'No' unless allowed_id&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name || 'No'&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This module is included in the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 5. DueDateActions Mixin ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module provides deadline-related operations for models that have due dates (Assignment, SignUpTopic).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDateActions&lt;br /&gt;
  # Generic activity permission checker that determines if an activity is permissible&lt;br /&gt;
  # based on the current deadline state for this parent object&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    current_deadline = next_due_date()&lt;br /&gt;
    return false unless current_deadline&lt;br /&gt;
&lt;br /&gt;
    current_deadline.activity_permissible?(activity)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Syntactic sugar methods for common activities&lt;br /&gt;
  def submission_permissible?&lt;br /&gt;
    activity_permissible?(:submission)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def review_permissible?&lt;br /&gt;
    activity_permissible?(:review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def teammate_review_permissible?&lt;br /&gt;
    activity_permissible?(:teammate_review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def quiz_permissible?&lt;br /&gt;
    activity_permissible?(:quiz)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def team_formation_permissible?&lt;br /&gt;
    activity_permissible?(:team_formation)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def signup_permissible?&lt;br /&gt;
    activity_permissible?(:signup)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def drop_topic_permissible?&lt;br /&gt;
    activity_permissible?(:drop_topic)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the next due date for this assignment&lt;br /&gt;
  def next_due_date(topic_id = nil)&lt;br /&gt;
    # If a topic is specified and this assignment has topic-specific deadlines,&lt;br /&gt;
    # look for topic due dates first&lt;br /&gt;
    if topic_id &amp;amp;&amp;amp; has_topic_specific_deadlines?&lt;br /&gt;
      topic_deadline = due_dates.where(parent_id: topic_id, parent_type: 'SignUpTopic')&lt;br /&gt;
                               .upcoming&lt;br /&gt;
                               .first&lt;br /&gt;
      return topic_deadline if topic_deadline&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Fall back to assignment-level due dates&lt;br /&gt;
    due_dates.upcoming.first&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the current stage name for display purposes&lt;br /&gt;
  def current_stage&lt;br /&gt;
    deadline = next_due_date()&lt;br /&gt;
    return 'finished' unless deadline&lt;br /&gt;
&lt;br /&gt;
    deadline.deadline_type&amp;amp;.name || 'unknown'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if assignment has topic-specific deadlines&lt;br /&gt;
  def has_topic_specific_deadlines?&lt;br /&gt;
    staggered_deadline || due_dates.where(parent_type: 'SignUpTopic').exists?&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Copy due dates to a new parent object&lt;br /&gt;
  def copy_due_dates_to(new_parent)&lt;br /&gt;
    due_dates.find_each do |due_date|&lt;br /&gt;
      new_due_date = due_date.dup&lt;br /&gt;
      new_due_date.parent = new_parent&lt;br /&gt;
      new_due_date.save!&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This module is included in &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class Assignment &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
class SignUpTopic &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Implementation Summary ==&lt;br /&gt;
&lt;br /&gt;
=== Models Created/Modified ===&lt;br /&gt;
&lt;br /&gt;
* '''DeadlineType''' - Canonical deadline type definitions with semantic helper methods&lt;br /&gt;
* '''DeadlineRight''' - Permission state model (OK/Late/No) with comparison methods&lt;br /&gt;
* '''DueDate''' - Refactored with instance methods, scopes, and permission checking&lt;br /&gt;
* '''TopicDueDate''' - STI subclass for topic-specific deadlines&lt;br /&gt;
* '''Assignment''' - Includes DueDateActions mixin&lt;br /&gt;
* '''SignUpTopic''' - Includes DueDateActions mixin&lt;br /&gt;
&lt;br /&gt;
=== Modules Created ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions''' - Permission checking combining deadline and role constraints&lt;br /&gt;
* '''DueDateActions''' - Deadline-related operations for parent models (Assignment, SignUpTopic)&lt;br /&gt;
&lt;br /&gt;
=== Key Improvements ===&lt;br /&gt;
&lt;br /&gt;
# '''Separation of Concerns''': Permission logic separated into dedicated modules&lt;br /&gt;
# '''Instance Methods''': Replaced class methods with testable instance methods&lt;br /&gt;
# '''Polymorphic Design''': Single DueDate model serves both Assignment and SignUpTopic&lt;br /&gt;
# '''Unified Permissions''': Role-based and time-based checks combined in one interface&lt;br /&gt;
# '''Data Integrity''': Removed duplicate deadline types, enforced foreign key constraints&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
The test suite includes comprehensive RSpec tests for the ''DueDate'' model and its associated models (''DeadlineType'', ''DeadlineRight'').&lt;br /&gt;
These tests verify model validations, scopes, instance methods, class methods, and callbacks with extensive coverage of edge cases and error conditions.&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
The test suite follows RSpec best practices with:&lt;br /&gt;
* '''Nested contexts''' for different scenarios (e.g., &amp;quot;when parent is missing&amp;quot;, &amp;quot;when round is 0&amp;quot;)&lt;br /&gt;
* '''Multiple assertions per context''' to verify behavior from different angles&lt;br /&gt;
* '''Edge case coverage''' including empty collections, nil values, and boundary conditions&lt;br /&gt;
* '''Error case testing''' for invalid data and non-existent records&lt;br /&gt;
* '''Clear descriptive names''' using &amp;quot;context 'when...'&amp;quot; and &amp;quot;it 'does something'&amp;quot; patterns&lt;br /&gt;
&lt;br /&gt;
=== Current Coverage ===&lt;br /&gt;
&lt;br /&gt;
==== Associations ====&lt;br /&gt;
* Tests verify &amp;lt;code&amp;gt;belongs_to :parent&amp;lt;/code&amp;gt; (polymorphic) and &amp;lt;code&amp;gt;belongs_to :deadline_type&amp;lt;/code&amp;gt; relationships&lt;br /&gt;
&lt;br /&gt;
==== Validations ====&lt;br /&gt;
* '''Required field validation''': Tests for parent, due_at, and deadline_type_id presence&lt;br /&gt;
* '''Round validation''': Tests for positive values, zero rejection, negative rejection, and nil handling&lt;br /&gt;
* '''Error messages''': Verifies specific error messages are added to appropriate fields&lt;br /&gt;
* '''Valid record creation''': Confirms records persist when all requirements are met&lt;br /&gt;
&lt;br /&gt;
==== Scopes ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;''': Returns future due dates ordered by due_at, excludes past dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;''': Returns past due dates ordered by due_at, excludes future dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;''': Filters by round number, handles non-existent rounds&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt;''': Filters by deadline type name, handles non-existent types&lt;br /&gt;
&lt;br /&gt;
==== Instance Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#overdue?&amp;lt;/code&amp;gt;''': Tests past dates (true), future dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#upcoming?&amp;lt;/code&amp;gt;''': Tests future dates (true), past dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#set&amp;lt;/code&amp;gt;''': Verifies updating deadline_type_id, parent_id, and round with persistence and error handling&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#copy&amp;lt;/code&amp;gt;''': Tests duplication to new assignment, attribute preservation, error handling for non-existent targets&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#deadline_type_name&amp;lt;/code&amp;gt;''': Returns associated deadline type name, handles nil cases&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#last_deadline?&amp;lt;/code&amp;gt;''': Tests detection of final deadline, handles multiple deadlines and single deadline scenarios&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#&amp;lt;=&amp;gt;&amp;lt;/code&amp;gt;''': Tests comparison by due_at time, handles non-DueDate objects&lt;br /&gt;
&lt;br /&gt;
==== Class Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.sort_due_dates&amp;lt;/code&amp;gt;''': Sorts by due_at ascending, handles empty arrays and single elements&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.any_future_due_dates?&amp;lt;/code&amp;gt;''': Detects future dates in collections, handles empty arrays and mixed date collections&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.copy&amp;lt;/code&amp;gt;''': Copies all due dates between assignments, preserves attributes, handles empty sources and non-existent records&lt;br /&gt;
&lt;br /&gt;
==== Callbacks ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;before_save :set_default_round&amp;lt;/code&amp;gt;''': Sets round to 1 when not specified, preserves explicit values, handles nil&lt;br /&gt;
&lt;br /&gt;
=== Test Examples ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Validation with multiple assertions&lt;br /&gt;
describe 'validations' do&lt;br /&gt;
  context 'when parent is missing' do&lt;br /&gt;
    let(:due_date) do&lt;br /&gt;
      DueDate.new(&lt;br /&gt;
        parent: nil,&lt;br /&gt;
        due_at: 2.days.from_now,&lt;br /&gt;
        deadline_type: submission_type,&lt;br /&gt;
        submission_allowed_id: ok_right.id,&lt;br /&gt;
        review_allowed_id: ok_right.id&lt;br /&gt;
      )&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'is invalid' do&lt;br /&gt;
      expect(due_date).to be_invalid&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'has error on parent field' do&lt;br /&gt;
      due_date.valid?&lt;br /&gt;
      expect(due_date.errors[:parent]).to include('must exist')&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when round validation' do&lt;br /&gt;
    context 'with round value of 0' do&lt;br /&gt;
      let(:due_date) do&lt;br /&gt;
        DueDate.new(&lt;br /&gt;
          parent: assignment,&lt;br /&gt;
          due_at: 2.days.from_now,&lt;br /&gt;
          deadline_type: submission_type,&lt;br /&gt;
          submission_allowed_id: ok_right.id,&lt;br /&gt;
          review_allowed_id: ok_right.id,&lt;br /&gt;
          round: 0&lt;br /&gt;
        )&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'is invalid' do&lt;br /&gt;
        expect(due_date).to be_invalid&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'has error on round field' do&lt;br /&gt;
        due_date.valid?&lt;br /&gt;
        expect(due_date.errors[:round]).to be_present&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'with nil round' do&lt;br /&gt;
      let(:due_date) do&lt;br /&gt;
        DueDate.create(&lt;br /&gt;
          parent: assignment,&lt;br /&gt;
          due_at: 2.days.from_now,&lt;br /&gt;
          deadline_type: submission_type,&lt;br /&gt;
          submission_allowed_id: ok_right.id,&lt;br /&gt;
          review_allowed_id: ok_right.id,&lt;br /&gt;
          round: nil&lt;br /&gt;
        )&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'is valid' do&lt;br /&gt;
        expect(due_date).to be_valid&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'sets default round to 1' do&lt;br /&gt;
        expect(due_date.round).to eq(1)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
# Scope with edge cases&lt;br /&gt;
describe '.upcoming' do&lt;br /&gt;
  it 'returns only future due dates' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).to contain_exactly(upcoming_due_date1, upcoming_due_date2)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'orders results by due_at ascending' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).to eq([upcoming_due_date1, upcoming_due_date2])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'excludes past due dates' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).not_to include(past_due_date)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when all due dates are in the past' do&lt;br /&gt;
    before do&lt;br /&gt;
      DueDate.destroy_all&lt;br /&gt;
      DueDate.create(&lt;br /&gt;
        parent: assignment,&lt;br /&gt;
        due_at: 3.days.ago,&lt;br /&gt;
        deadline_type: submission_type,&lt;br /&gt;
        submission_allowed_id: ok_right.id,&lt;br /&gt;
        review_allowed_id: ok_right.id&lt;br /&gt;
      )&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns empty collection' do&lt;br /&gt;
      expect(DueDate.upcoming).to be_empty&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
# Instance method with error handling&lt;br /&gt;
describe '#copy' do&lt;br /&gt;
  let(:original) do&lt;br /&gt;
    DueDate.create(&lt;br /&gt;
      parent: assignment,&lt;br /&gt;
      due_at: 2.days.from_now,&lt;br /&gt;
      deadline_type: submission_type,&lt;br /&gt;
      submission_allowed_id: ok_right.id,&lt;br /&gt;
      review_allowed_id: late_right.id,&lt;br /&gt;
      round: 1&lt;br /&gt;
    )&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  let(:copied) { original.copy(assignment2.id) }&lt;br /&gt;
&lt;br /&gt;
  it 'creates a new persisted record' do&lt;br /&gt;
    expect(copied).to be_persisted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'has different id from original' do&lt;br /&gt;
    expect(copied.id).not_to eq(original.id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'preserves all attributes' do&lt;br /&gt;
    expect(copied.due_at).to eq(original.due_at)&lt;br /&gt;
    expect(copied.deadline_type_id).to eq(original.deadline_type_id)&lt;br /&gt;
    expect(copied.round).to eq(original.round)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when copying to non-existent assignment' do&lt;br /&gt;
    it 'raises error' do&lt;br /&gt;
      expect do&lt;br /&gt;
        original.copy(99999)&lt;br /&gt;
      end.to raise_error(ActiveRecord::RecordNotFound)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Test Metrics ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDate model''': 60+ examples covering all methods and edge cases&lt;br /&gt;
* '''Context depth''': 2-3 levels for complex scenarios&lt;br /&gt;
* '''Code coverage''': 100% coverage of DueDate model methods&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
=== Demo Link ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add deadline notification system using the new permission framework&lt;br /&gt;
* Implement deadline templates for common assignment patterns&lt;br /&gt;
* Add API endpoints for mobile app integration&lt;br /&gt;
* Create admin dashboard for monitoring deadline compliance across courses&lt;br /&gt;
* Add automated deadline extension workflows based on configurable rules&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167181</id>
		<title>CSC/ECE 517 Fall 2025 - E2566. Finish DueDates</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167181"/>
		<updated>2025-12-02T00:27:13Z</updated>

		<summary type="html">&lt;p&gt;Skim253: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
This project aims to complete the implementation of the DueDate system in Expertiza. The current implementation consists mostly of class methods and lacks proper definition of different deadline types and permission checking functionality. This redesign will create a more robust, readable, and maintainable due date system.&lt;br /&gt;
&lt;br /&gt;
=== Existing Components ===&lt;br /&gt;
* &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model with polymorphic parent association (Assignment/SignUpTopic)&lt;br /&gt;
* &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; subclasses&lt;br /&gt;
* Basic CRUD operations (within &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AssignmentForm&amp;lt;/code&amp;gt;) and sorting functionality (&amp;lt;code&amp;gt;deadline_sort&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Database schema with &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt; field&lt;br /&gt;
&lt;br /&gt;
=== Current Behavior ===&lt;br /&gt;
&lt;br /&gt;
====Admin's View====&lt;br /&gt;
[[File:Duedates.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* When editing an assignment, due dates can be modified under the Due Dates tab.&lt;br /&gt;
* Each row contains the following columns: &amp;lt;code&amp;gt;Date &amp;amp; Time&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Use Date Updater&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Submission Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Teammate Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Meta-Review Allowed&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Reminder&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:Assignments-CurrentStage.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* In the Assignments table, the ''DueDate'' of each assignment is displayed as Current Stage and Stage Deadline.&lt;br /&gt;
&lt;br /&gt;
====Student's View====&lt;br /&gt;
[[File:Student-assignments.png|600px]]&lt;br /&gt;
* Students can view the due date information under &amp;lt;code&amp;gt;Current State&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Stage Deadline&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
* The actions available to students are determined by the current DueDate status.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-not-over.png|600px]]&lt;br /&gt;
* For example, during the submission period, students can access the &amp;lt;code&amp;gt;Submit a hyperlink&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Submit a file&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-over.png|600px]]&lt;br /&gt;
* Once the due date has passed, students cannot submit either a hyperlink or a file.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Currently, there are some glaring issues with how due_dates was created in the first place, including redundancy and lack of modularity.&lt;br /&gt;
&lt;br /&gt;
# No dedicated &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to define different kinds of deadlines. The existing &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; class only serves as an association for &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; models, and is never referenced in the current &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; implementation.&lt;br /&gt;
# Overuse of class methods making the code difficult to maintain.&lt;br /&gt;
# Missing permission checking logic for user actions.&lt;br /&gt;
# Incomplete deadline type definitions — &amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt; need to be added.&lt;br /&gt;
# Duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table, which may lead to potential bugs.&lt;br /&gt;
# No clear separation of concerns — data persistence, business logic, and permission logic are mixed in a single model.&lt;br /&gt;
&lt;br /&gt;
=== Tasks Identified ===&lt;br /&gt;
&lt;br /&gt;
# Redesign the &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.&lt;br /&gt;
# Refactor the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model to separate persistence, business logic, and permission logic into distinct layers.&lt;br /&gt;
# Introduce instance-level methods (e.g., &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;) to replace class-level utilities and improve testability.&lt;br /&gt;
# Integrate role-based and time-based permission checks within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; to ensure consistent access control.&lt;br /&gt;
# Add missing deadline types (&amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt;) and remove duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries for data integrity.&lt;br /&gt;
# Implement reusable mixins (e.g., &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;) to handle permission evaluation and deadline-related operations.&lt;br /&gt;
# Update controllers and APIs to use the new mixin-based approach for consistent permission checks and deadline queries.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Proposed Solution ==&lt;br /&gt;
&lt;br /&gt;
[[File:Duedate-diagram.png|600px]]&lt;br /&gt;
&lt;br /&gt;
=== DeadlineType Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Although the current codebase includes a &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model, it only serves as a passive lookup table for associations with &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt;.  &lt;br /&gt;
In practice, most parts of the system still rely on hard-coded numeric IDs or manual lookups such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Example of current usage&lt;br /&gt;
deadline_type_id = DeadlineType.find_by(name: 'review').id&lt;br /&gt;
if due_date.deadline_type_id == deadline_type_id&lt;br /&gt;
  # Perform review-related logic&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of using the model as an active domain object, &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; directly compares IDs or string literals.  &lt;br /&gt;
This leads to several structural issues:&lt;br /&gt;
&lt;br /&gt;
* Inconsistent references (some use IDs, others strings)&lt;br /&gt;
* Tight coupling between &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
* Lack of helper semantics (e.g., &amp;lt;code&amp;gt;is_review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;is_submission?&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Data duplication and integrity risks (two &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; rows)&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; will serve as a source of truth, defining all valid deadline types and providing meaningful interfaces.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE deadline_types (&lt;br /&gt;
  id BIGINT PRIMARY KEY,&lt;br /&gt;
  name VARCHAR(255) NOT NULL UNIQUE,&lt;br /&gt;
  description TEXT NOT NULL,&lt;br /&gt;
  created_at TIMESTAMP,&lt;br /&gt;
  updated_at TIMESTAMP&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Deadline Type Definitions ====&lt;br /&gt;
* &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;: submission - Student work submission deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt;: review - Peer review deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;3&amp;lt;/code&amp;gt;: teammate_review - Team member evaluation deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;5&amp;lt;/code&amp;gt;: metareview - Meta-review deadlines (kept for backward compatibility)  &lt;br /&gt;
* &amp;lt;code&amp;gt;6&amp;lt;/code&amp;gt;: drop_topic - Topic drop deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;7&amp;lt;/code&amp;gt;: signup - Course/assignment signup deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;8&amp;lt;/code&amp;gt;: team_formation - Team formation deadlines (clean up duplicate ID 10)  &lt;br /&gt;
* &amp;lt;code&amp;gt;11&amp;lt;/code&amp;gt;: quiz - Quiz completion deadlines&lt;br /&gt;
&lt;br /&gt;
==== DeadlineType Model Implementation ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineType &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
  validates :description, presence: true&lt;br /&gt;
&lt;br /&gt;
  has_many :due_dates, foreign_key: :deadline_type_id, dependent: :restrict_with_exception&lt;br /&gt;
&lt;br /&gt;
  # Semantic helper methods for deadline type identification&lt;br /&gt;
  def submission?&lt;br /&gt;
    name == 'submission'&lt;br /&gt;
  end&lt;br /&gt;
  # same code for review, teammate_review, quiz, team_formation, signup, drop_topic   &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DeadlineRight Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model represents the permission level for a specific action at a given deadline.&lt;br /&gt;
It defines three states: &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt; (action allowed), &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt; (action allowed with penalty), and &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt; (action denied).&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE deadline_rights (&lt;br /&gt;
  id BIGINT PRIMARY KEY,&lt;br /&gt;
  name VARCHAR(255) NOT NULL UNIQUE,&lt;br /&gt;
  created_at TIMESTAMP,&lt;br /&gt;
  updated_at TIMESTAMP&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== DeadlineRight Model ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineRight &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
&lt;br /&gt;
  # Permission checking methods&lt;br /&gt;
  def allows_action?&lt;br /&gt;
    %w[OK Late].include?(name)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def denies_action?&lt;br /&gt;
    name == 'No'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def allows_with_penalty?&lt;br /&gt;
    name == 'Late'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def allows_without_penalty?&lt;br /&gt;
    name == 'OK'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Display methods&lt;br /&gt;
  def to_s&lt;br /&gt;
    name&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def permission_level&lt;br /&gt;
    case name&lt;br /&gt;
    when 'No'   then 0&lt;br /&gt;
    when 'Late' then 1&lt;br /&gt;
    when 'OK'   then 2&lt;br /&gt;
    else -1&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== DueDate Model Refactoring ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model currently mixes persistence logic, deadline calculation, and permission checking, violating SRP.  &lt;br /&gt;
Most of its logic is implemented as class methods, which makes testing and extension difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Current design&lt;br /&gt;
def self.copy(old_assignment_id, new_assignment_id)&lt;br /&gt;
  duedates = where(parent_id: old_assignment_id)&lt;br /&gt;
  duedates.each do |orig_due_date|&lt;br /&gt;
    new_due_date = orig_due_date.dup&lt;br /&gt;
    new_due_date.parent_id = new_assignment_id&lt;br /&gt;
    new_due_date.save&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Improved Design ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
&lt;br /&gt;
  belongs_to :parent, polymorphic: true&lt;br /&gt;
  belongs_to :deadline_type, foreign_key: :deadline_type_id&lt;br /&gt;
&lt;br /&gt;
  validates :due_at, presence: true&lt;br /&gt;
  validates :deadline_type_id, presence: true&lt;br /&gt;
  validates :parent, presence: true&lt;br /&gt;
  validates :round, numericality: { greater_than: 0 }, allow_nil: true&lt;br /&gt;
&lt;br /&gt;
  # Scopes for common queries&lt;br /&gt;
  scope :upcoming, -&amp;gt; { where('due_at &amp;gt; ?', Time.current).order(:due_at) }&lt;br /&gt;
  scope :overdue, -&amp;gt; { where('due_at &amp;lt; ?', Time.current).order(:due_at) }&lt;br /&gt;
  scope :for_round, -&amp;gt;(round_num) { where(round: round_num) }&lt;br /&gt;
  scope :for_deadline_type, -&amp;gt;(type_name) { joins(:deadline_type).where(deadline_types: { name: type_name }) }&lt;br /&gt;
&lt;br /&gt;
  # Instance methods replace class methods&lt;br /&gt;
  def copy(to_assignment_id)&lt;br /&gt;
    to_assignment = Assignment.find(to_assignment_id)&lt;br /&gt;
    new_due_date = dup&lt;br /&gt;
    new_due_date.parent = to_assignment&lt;br /&gt;
    new_due_date.save!&lt;br /&gt;
    new_due_date&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def set(deadline_type_id, parent_id, round)&lt;br /&gt;
    self.deadline_type_id = deadline_type_id&lt;br /&gt;
    self.parent_id = parent_id&lt;br /&gt;
    self.round = round&lt;br /&gt;
    save!&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if this deadline has passed&lt;br /&gt;
  def overdue?&lt;br /&gt;
    due_at &amp;lt; Time.current&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if this deadline is upcoming&lt;br /&gt;
  def upcoming?&lt;br /&gt;
    due_at &amp;gt; Time.current&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the deadline type name&lt;br /&gt;
  def deadline_type_name&lt;br /&gt;
    deadline_type&amp;amp;.name&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Comparison method for sorting&lt;br /&gt;
  def &amp;lt;=&amp;gt;(other)&lt;br /&gt;
    return nil unless other.is_a?(DueDate)&lt;br /&gt;
    due_at &amp;lt;=&amp;gt; other.due_at&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Class methods for collection operations&lt;br /&gt;
  class &amp;lt;&amp;lt; self&lt;br /&gt;
    def sort_due_dates(due_dates)&lt;br /&gt;
      due_dates.sort_by(&amp;amp;:due_at)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    def any_future_due_dates?(due_dates)&lt;br /&gt;
      due_dates.any?(&amp;amp;:upcoming?)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    def copy(from_assignment_id, to_assignment_id)&lt;br /&gt;
      from_assignment = Assignment.find(from_assignment_id)&lt;br /&gt;
      to_assignment = Assignment.find(to_assignment_id)&lt;br /&gt;
&lt;br /&gt;
      from_assignment.due_dates.each do |due_date|&lt;br /&gt;
        new_due_date = due_date.dup&lt;br /&gt;
        new_due_date.parent = to_assignment&lt;br /&gt;
        new_due_date.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  before_save :set_default_round&lt;br /&gt;
&lt;br /&gt;
  def set_default_round&lt;br /&gt;
    self.round ||= 1&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Permission-Checking Methods ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Currently, permission checks depend only on user roles and ignore the actual deadline.  &lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Role-based permissions (participant_helpers.rb)&lt;br /&gt;
def participant_permissions(authorization)&lt;br /&gt;
  case authorization&lt;br /&gt;
  when 'reader'   then { can_submit: false, can_review: true, can_take_quiz: true }&lt;br /&gt;
  when 'reviewer' then { can_submit: false, can_review: true, can_take_quiz: false }&lt;br /&gt;
  when 'submitter' then { can_submit: true, can_review: false, can_take_quiz: false }&lt;br /&gt;
  else { can_submit: true, can_review: true, can_take_quiz: true }&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And deadline-based checks exist separately in the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def submission_allowed(topic_id = nil)&lt;br /&gt;
  check_condition('submission_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
def can_review(topic_id = nil)&lt;br /&gt;
  check_condition('review_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These two layers (role-based and time-based) never interact, leading to inconsistencies (e.g., a user with &amp;lt;code&amp;gt;can_submit=true&amp;lt;/code&amp;gt; after deadline).&lt;br /&gt;
&lt;br /&gt;
==== Proposed Changes ====&lt;br /&gt;
&lt;br /&gt;
The new &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module integrates role-based and deadline-based checks:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDatePermissions&lt;br /&gt;
  # Permission checking methods that combine deadline-based and role-based logic&lt;br /&gt;
  #&lt;br /&gt;
  # These methods must check both:&lt;br /&gt;
  # 1. Whether the action is permitted by the current deadline (submission_allowed_id, review_allowed_id, etc.)&lt;br /&gt;
  # 2. Whether the participant has the necessary permissions (can_submit, can_review, can_take_quiz fields)&lt;br /&gt;
&lt;br /&gt;
  # Check if submission is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_submit?(participant = nil)&lt;br /&gt;
    return false unless submission_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_submit&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: submission_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if review is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_review?(participant = nil)&lt;br /&gt;
    return false unless review_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_review&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: review_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if taking a quiz is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_take_quiz?(participant = nil)&lt;br /&gt;
    return false unless quiz_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_take_quiz&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: quiz_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if teammate review is allowed&lt;br /&gt;
  def can_review_teammate?(participant = nil)&lt;br /&gt;
    return false unless teammate_review_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_review&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: teammate_review_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Generic permission checker&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    permission_field = &amp;quot;#{activity}_allowed_id&amp;quot;&lt;br /&gt;
    return false unless respond_to?(permission_field)&lt;br /&gt;
&lt;br /&gt;
    allowed_id = public_send(permission_field)&lt;br /&gt;
    return false unless allowed_id&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get permission status for an action (OK, Late, No)&lt;br /&gt;
  def permission_status_for(action)&lt;br /&gt;
    permission_field = &amp;quot;#{action}_allowed_id&amp;quot;&lt;br /&gt;
    return 'No' unless respond_to?(permission_field)&lt;br /&gt;
&lt;br /&gt;
    allowed_id = public_send(permission_field)&lt;br /&gt;
    return 'No' unless allowed_id&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name || 'No'&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This module is included in the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 5. DueDateActions Mixin ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module provides deadline-related operations for models that have due dates (Assignment, SignUpTopic).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDateActions&lt;br /&gt;
  # Generic activity permission checker that determines if an activity is permissible&lt;br /&gt;
  # based on the current deadline state for this parent object&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    current_deadline = next_due_date()&lt;br /&gt;
    return false unless current_deadline&lt;br /&gt;
&lt;br /&gt;
    current_deadline.activity_permissible?(activity)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Syntactic sugar methods for common activities&lt;br /&gt;
  def submission_permissible?&lt;br /&gt;
    activity_permissible?(:submission)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def review_permissible?&lt;br /&gt;
    activity_permissible?(:review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def teammate_review_permissible?&lt;br /&gt;
    activity_permissible?(:teammate_review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def quiz_permissible?&lt;br /&gt;
    activity_permissible?(:quiz)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def team_formation_permissible?&lt;br /&gt;
    activity_permissible?(:team_formation)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def signup_permissible?&lt;br /&gt;
    activity_permissible?(:signup)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def drop_topic_permissible?&lt;br /&gt;
    activity_permissible?(:drop_topic)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the next due date for this assignment&lt;br /&gt;
  def next_due_date(topic_id = nil)&lt;br /&gt;
    # If a topic is specified and this assignment has topic-specific deadlines,&lt;br /&gt;
    # look for topic due dates first&lt;br /&gt;
    if topic_id &amp;amp;&amp;amp; has_topic_specific_deadlines?&lt;br /&gt;
      topic_deadline = due_dates.where(parent_id: topic_id, parent_type: 'SignUpTopic')&lt;br /&gt;
                               .upcoming&lt;br /&gt;
                               .first&lt;br /&gt;
      return topic_deadline if topic_deadline&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Fall back to assignment-level due dates&lt;br /&gt;
    due_dates.upcoming.first&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the current stage name for display purposes&lt;br /&gt;
  def current_stage&lt;br /&gt;
    deadline = next_due_date()&lt;br /&gt;
    return 'finished' unless deadline&lt;br /&gt;
&lt;br /&gt;
    deadline.deadline_type&amp;amp;.name || 'unknown'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if assignment has topic-specific deadlines&lt;br /&gt;
  def has_topic_specific_deadlines?&lt;br /&gt;
    staggered_deadline || due_dates.where(parent_type: 'SignUpTopic').exists?&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Copy due dates to a new parent object&lt;br /&gt;
  def copy_due_dates_to(new_parent)&lt;br /&gt;
    due_dates.find_each do |due_date|&lt;br /&gt;
      new_due_date = due_date.dup&lt;br /&gt;
      new_due_date.parent = new_parent&lt;br /&gt;
      new_due_date.save!&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This module is included in &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class Assignment &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
class SignUpTopic &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Implementation Summary ==&lt;br /&gt;
&lt;br /&gt;
=== Models Created/Modified ===&lt;br /&gt;
&lt;br /&gt;
* '''DeadlineType''' - Canonical deadline type definitions with semantic helper methods&lt;br /&gt;
* '''DeadlineRight''' - Permission state model (OK/Late/No) with comparison methods&lt;br /&gt;
* '''DueDate''' - Refactored with instance methods, scopes, and permission checking&lt;br /&gt;
* '''TopicDueDate''' - STI subclass for topic-specific deadlines&lt;br /&gt;
* '''Assignment''' - Includes DueDateActions mixin&lt;br /&gt;
* '''SignUpTopic''' - Includes DueDateActions mixin&lt;br /&gt;
&lt;br /&gt;
=== Modules Created ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions''' - Permission checking combining deadline and role constraints&lt;br /&gt;
* '''DueDateActions''' - Deadline-related operations for parent models (Assignment, SignUpTopic)&lt;br /&gt;
&lt;br /&gt;
=== Key Improvements ===&lt;br /&gt;
&lt;br /&gt;
# '''Separation of Concerns''': Permission logic separated into dedicated modules&lt;br /&gt;
# '''Instance Methods''': Replaced class methods with testable instance methods&lt;br /&gt;
# '''Polymorphic Design''': Single DueDate model serves both Assignment and SignUpTopic&lt;br /&gt;
# '''Unified Permissions''': Role-based and time-based checks combined in one interface&lt;br /&gt;
# '''Data Integrity''': Removed duplicate deadline types, enforced foreign key constraints&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
The test suite includes comprehensive RSpec tests for the ''DueDate'' model and its associated models (''DeadlineType'', ''DeadlineRight'').&lt;br /&gt;
These tests verify model validations, scopes, instance methods, class methods, and callbacks with extensive coverage of edge cases and error conditions.&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
The test suite follows RSpec best practices with:&lt;br /&gt;
* '''Nested contexts''' for different scenarios (e.g., &amp;quot;when parent is missing&amp;quot;, &amp;quot;when round is 0&amp;quot;)&lt;br /&gt;
* '''Multiple assertions per context''' to verify behavior from different angles&lt;br /&gt;
* '''Edge case coverage''' including empty collections, nil values, and boundary conditions&lt;br /&gt;
* '''Error case testing''' for invalid data and non-existent records&lt;br /&gt;
* '''Clear descriptive names''' using &amp;quot;context 'when...'&amp;quot; and &amp;quot;it 'does something'&amp;quot; patterns&lt;br /&gt;
&lt;br /&gt;
=== Current Coverage ===&lt;br /&gt;
&lt;br /&gt;
==== Associations ====&lt;br /&gt;
* Tests verify &amp;lt;code&amp;gt;belongs_to :parent&amp;lt;/code&amp;gt; (polymorphic) and &amp;lt;code&amp;gt;belongs_to :deadline_type&amp;lt;/code&amp;gt; relationships&lt;br /&gt;
&lt;br /&gt;
==== Validations ====&lt;br /&gt;
* '''Required field validation''': Tests for parent, due_at, and deadline_type_id presence&lt;br /&gt;
* '''Round validation''': Tests for positive values, zero rejection, negative rejection, and nil handling&lt;br /&gt;
* '''Error messages''': Verifies specific error messages are added to appropriate fields&lt;br /&gt;
* '''Valid record creation''': Confirms records persist when all requirements are met&lt;br /&gt;
&lt;br /&gt;
==== Scopes ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;''': Returns future due dates ordered by due_at, excludes past dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;''': Returns past due dates ordered by due_at, excludes future dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;''': Filters by round number, handles non-existent rounds&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt;''': Filters by deadline type name, handles non-existent types&lt;br /&gt;
&lt;br /&gt;
==== Instance Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#overdue?&amp;lt;/code&amp;gt;''': Tests past dates (true), future dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#upcoming?&amp;lt;/code&amp;gt;''': Tests future dates (true), past dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#set&amp;lt;/code&amp;gt;''': Verifies updating deadline_type_id, parent_id, and round with persistence and error handling&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#copy&amp;lt;/code&amp;gt;''': Tests duplication to new assignment, attribute preservation, error handling for non-existent targets&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#deadline_type_name&amp;lt;/code&amp;gt;''': Returns associated deadline type name, handles nil cases&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#last_deadline?&amp;lt;/code&amp;gt;''': Tests detection of final deadline, handles multiple deadlines and single deadline scenarios&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#&amp;lt;=&amp;gt;&amp;lt;/code&amp;gt;''': Tests comparison by due_at time, handles non-DueDate objects&lt;br /&gt;
&lt;br /&gt;
==== Class Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.sort_due_dates&amp;lt;/code&amp;gt;''': Sorts by due_at ascending, handles empty arrays and single elements&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.any_future_due_dates?&amp;lt;/code&amp;gt;''': Detects future dates in collections, handles empty arrays and mixed date collections&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.copy&amp;lt;/code&amp;gt;''': Copies all due dates between assignments, preserves attributes, handles empty sources and non-existent records&lt;br /&gt;
&lt;br /&gt;
==== Callbacks ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;before_save :set_default_round&amp;lt;/code&amp;gt;''': Sets round to 1 when not specified, preserves explicit values, handles nil&lt;br /&gt;
&lt;br /&gt;
=== Test Examples ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Validation with multiple assertions&lt;br /&gt;
describe 'validations' do&lt;br /&gt;
  context 'when parent is missing' do&lt;br /&gt;
    let(:due_date) do&lt;br /&gt;
      DueDate.new(&lt;br /&gt;
        parent: nil,&lt;br /&gt;
        due_at: 2.days.from_now,&lt;br /&gt;
        deadline_type: submission_type,&lt;br /&gt;
        submission_allowed_id: ok_right.id,&lt;br /&gt;
        review_allowed_id: ok_right.id&lt;br /&gt;
      )&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'is invalid' do&lt;br /&gt;
      expect(due_date).to be_invalid&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'has error on parent field' do&lt;br /&gt;
      due_date.valid?&lt;br /&gt;
      expect(due_date.errors[:parent]).to include('must exist')&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when round validation' do&lt;br /&gt;
    context 'with round value of 0' do&lt;br /&gt;
      let(:due_date) do&lt;br /&gt;
        DueDate.new(&lt;br /&gt;
          parent: assignment,&lt;br /&gt;
          due_at: 2.days.from_now,&lt;br /&gt;
          deadline_type: submission_type,&lt;br /&gt;
          submission_allowed_id: ok_right.id,&lt;br /&gt;
          review_allowed_id: ok_right.id,&lt;br /&gt;
          round: 0&lt;br /&gt;
        )&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'is invalid' do&lt;br /&gt;
        expect(due_date).to be_invalid&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'has error on round field' do&lt;br /&gt;
        due_date.valid?&lt;br /&gt;
        expect(due_date.errors[:round]).to be_present&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'with nil round' do&lt;br /&gt;
      let(:due_date) do&lt;br /&gt;
        DueDate.create(&lt;br /&gt;
          parent: assignment,&lt;br /&gt;
          due_at: 2.days.from_now,&lt;br /&gt;
          deadline_type: submission_type,&lt;br /&gt;
          submission_allowed_id: ok_right.id,&lt;br /&gt;
          review_allowed_id: ok_right.id,&lt;br /&gt;
          round: nil&lt;br /&gt;
        )&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'is valid' do&lt;br /&gt;
        expect(due_date).to be_valid&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'sets default round to 1' do&lt;br /&gt;
        expect(due_date.round).to eq(1)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
# Scope with edge cases&lt;br /&gt;
describe '.upcoming' do&lt;br /&gt;
  it 'returns only future due dates' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).to contain_exactly(upcoming_due_date1, upcoming_due_date2)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'orders results by due_at ascending' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).to eq([upcoming_due_date1, upcoming_due_date2])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'excludes past due dates' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).not_to include(past_due_date)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when all due dates are in the past' do&lt;br /&gt;
    before do&lt;br /&gt;
      DueDate.destroy_all&lt;br /&gt;
      DueDate.create(&lt;br /&gt;
        parent: assignment,&lt;br /&gt;
        due_at: 3.days.ago,&lt;br /&gt;
        deadline_type: submission_type,&lt;br /&gt;
        submission_allowed_id: ok_right.id,&lt;br /&gt;
        review_allowed_id: ok_right.id&lt;br /&gt;
      )&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns empty collection' do&lt;br /&gt;
      expect(DueDate.upcoming).to be_empty&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
# Instance method with error handling&lt;br /&gt;
describe '#copy' do&lt;br /&gt;
  let(:original) do&lt;br /&gt;
    DueDate.create(&lt;br /&gt;
      parent: assignment,&lt;br /&gt;
      due_at: 2.days.from_now,&lt;br /&gt;
      deadline_type: submission_type,&lt;br /&gt;
      submission_allowed_id: ok_right.id,&lt;br /&gt;
      review_allowed_id: late_right.id,&lt;br /&gt;
      round: 1&lt;br /&gt;
    )&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  let(:copied) { original.copy(assignment2.id) }&lt;br /&gt;
&lt;br /&gt;
  it 'creates a new persisted record' do&lt;br /&gt;
    expect(copied).to be_persisted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'has different id from original' do&lt;br /&gt;
    expect(copied.id).not_to eq(original.id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'preserves all attributes' do&lt;br /&gt;
    expect(copied.due_at).to eq(original.due_at)&lt;br /&gt;
    expect(copied.deadline_type_id).to eq(original.deadline_type_id)&lt;br /&gt;
    expect(copied.round).to eq(original.round)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when copying to non-existent assignment' do&lt;br /&gt;
    it 'raises error' do&lt;br /&gt;
      expect do&lt;br /&gt;
        original.copy(99999)&lt;br /&gt;
      end.to raise_error(ActiveRecord::RecordNotFound)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Test Metrics ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDate model''': 60+ examples covering all methods and edge cases&lt;br /&gt;
* '''Context depth''': 2-3 levels for complex scenarios&lt;br /&gt;
* '''Code coverage''': 100% coverage of DueDate model methods&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
=== Demo Link ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add deadline notification system using the new permission framework&lt;br /&gt;
* Implement deadline templates for common assignment patterns&lt;br /&gt;
* Add API endpoints for mobile app integration&lt;br /&gt;
* Create admin dashboard for monitoring deadline compliance across courses&lt;br /&gt;
* Add automated deadline extension workflows based on configurable rules&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=File:Duedate-diagram.png&amp;diff=167180</id>
		<title>File:Duedate-diagram.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=File:Duedate-diagram.png&amp;diff=167180"/>
		<updated>2025-12-02T00:26:12Z</updated>

		<summary type="html">&lt;p&gt;Skim253: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167179</id>
		<title>CSC/ECE 517 Fall 2025 - E2566. Finish DueDates</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167179"/>
		<updated>2025-12-02T00:16:22Z</updated>

		<summary type="html">&lt;p&gt;Skim253: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
This project aims to complete the implementation of the DueDate system in Expertiza. The current implementation consists mostly of class methods and lacks proper definition of different deadline types and permission checking functionality. This redesign will create a more robust, readable, and maintainable due date system.&lt;br /&gt;
&lt;br /&gt;
=== Existing Components ===&lt;br /&gt;
* &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model with polymorphic parent association (Assignment/SignUpTopic)&lt;br /&gt;
* &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; subclasses&lt;br /&gt;
* Basic CRUD operations (within &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AssignmentForm&amp;lt;/code&amp;gt;) and sorting functionality (&amp;lt;code&amp;gt;deadline_sort&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Database schema with &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt; field&lt;br /&gt;
&lt;br /&gt;
=== Current Behavior ===&lt;br /&gt;
&lt;br /&gt;
====Admin's View====&lt;br /&gt;
[[File:Duedates.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* When editing an assignment, due dates can be modified under the Due Dates tab.&lt;br /&gt;
* Each row contains the following columns: &amp;lt;code&amp;gt;Date &amp;amp; Time&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Use Date Updater&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Submission Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Teammate Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Meta-Review Allowed&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Reminder&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:Assignments-CurrentStage.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* In the Assignments table, the ''DueDate'' of each assignment is displayed as Current Stage and Stage Deadline.&lt;br /&gt;
&lt;br /&gt;
====Student's View====&lt;br /&gt;
[[File:Student-assignments.png|600px]]&lt;br /&gt;
* Students can view the due date information under &amp;lt;code&amp;gt;Current State&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Stage Deadline&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
* The actions available to students are determined by the current DueDate status.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-not-over.png|600px]]&lt;br /&gt;
* For example, during the submission period, students can access the &amp;lt;code&amp;gt;Submit a hyperlink&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Submit a file&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-over.png|600px]]&lt;br /&gt;
* Once the due date has passed, students cannot submit either a hyperlink or a file.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Currently, there are some glaring issues with how due_dates was created in the first place, including redundancy and lack of modularity.&lt;br /&gt;
&lt;br /&gt;
# No dedicated &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to define different kinds of deadlines. The existing &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; class only serves as an association for &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; models, and is never referenced in the current &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; implementation.&lt;br /&gt;
# Overuse of class methods making the code difficult to maintain.&lt;br /&gt;
# Missing permission checking logic for user actions.&lt;br /&gt;
# Incomplete deadline type definitions — &amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt; need to be added.&lt;br /&gt;
# Duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table, which may lead to potential bugs.&lt;br /&gt;
# No clear separation of concerns — data persistence, business logic, and permission logic are mixed in a single model.&lt;br /&gt;
&lt;br /&gt;
=== Tasks Identified ===&lt;br /&gt;
&lt;br /&gt;
# Redesign the &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.&lt;br /&gt;
# Refactor the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model to separate persistence, business logic, and permission logic into distinct layers.&lt;br /&gt;
# Introduce instance-level methods (e.g., &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;) to replace class-level utilities and improve testability.&lt;br /&gt;
# Integrate role-based and time-based permission checks within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; to ensure consistent access control.&lt;br /&gt;
# Add missing deadline types (&amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt;) and remove duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries for data integrity.&lt;br /&gt;
# Implement reusable mixins (e.g., &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;) to handle permission evaluation and deadline-related operations.&lt;br /&gt;
# Update controllers and APIs to use the new mixin-based approach for consistent permission checks and deadline queries.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Proposed Solution ==&lt;br /&gt;
&lt;br /&gt;
=== DeadlineType Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Although the current codebase includes a &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model, it only serves as a passive lookup table for associations with &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt;.  &lt;br /&gt;
In practice, most parts of the system still rely on hard-coded numeric IDs or manual lookups such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Example of current usage&lt;br /&gt;
deadline_type_id = DeadlineType.find_by(name: 'review').id&lt;br /&gt;
if due_date.deadline_type_id == deadline_type_id&lt;br /&gt;
  # Perform review-related logic&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of using the model as an active domain object, &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; directly compares IDs or string literals.  &lt;br /&gt;
This leads to several structural issues:&lt;br /&gt;
&lt;br /&gt;
* Inconsistent references (some use IDs, others strings)&lt;br /&gt;
* Tight coupling between &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
* Lack of helper semantics (e.g., &amp;lt;code&amp;gt;is_review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;is_submission?&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Data duplication and integrity risks (two &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; rows)&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; will serve as a source of truth, defining all valid deadline types and providing meaningful interfaces.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE deadline_types (&lt;br /&gt;
  id BIGINT PRIMARY KEY,&lt;br /&gt;
  name VARCHAR(255) NOT NULL UNIQUE,&lt;br /&gt;
  description TEXT NOT NULL,&lt;br /&gt;
  created_at TIMESTAMP,&lt;br /&gt;
  updated_at TIMESTAMP&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Deadline Type Definitions ====&lt;br /&gt;
* &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;: submission - Student work submission deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt;: review - Peer review deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;3&amp;lt;/code&amp;gt;: teammate_review - Team member evaluation deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;5&amp;lt;/code&amp;gt;: metareview - Meta-review deadlines (kept for backward compatibility)  &lt;br /&gt;
* &amp;lt;code&amp;gt;6&amp;lt;/code&amp;gt;: drop_topic - Topic drop deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;7&amp;lt;/code&amp;gt;: signup - Course/assignment signup deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;8&amp;lt;/code&amp;gt;: team_formation - Team formation deadlines (clean up duplicate ID 10)  &lt;br /&gt;
* &amp;lt;code&amp;gt;11&amp;lt;/code&amp;gt;: quiz - Quiz completion deadlines&lt;br /&gt;
&lt;br /&gt;
==== DeadlineType Model Implementation ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineType &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
  validates :description, presence: true&lt;br /&gt;
&lt;br /&gt;
  has_many :due_dates, foreign_key: :deadline_type_id, dependent: :restrict_with_exception&lt;br /&gt;
&lt;br /&gt;
  # Semantic helper methods for deadline type identification&lt;br /&gt;
  def submission?&lt;br /&gt;
    name == 'submission'&lt;br /&gt;
  end&lt;br /&gt;
  # same code for review, teammate_review, quiz, team_formation, signup, drop_topic   &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DeadlineRight Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model represents the permission level for a specific action at a given deadline.&lt;br /&gt;
It defines three states: &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt; (action allowed), &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt; (action allowed with penalty), and &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt; (action denied).&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE deadline_rights (&lt;br /&gt;
  id BIGINT PRIMARY KEY,&lt;br /&gt;
  name VARCHAR(255) NOT NULL UNIQUE,&lt;br /&gt;
  created_at TIMESTAMP,&lt;br /&gt;
  updated_at TIMESTAMP&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== DeadlineRight Model ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineRight &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
&lt;br /&gt;
  # Permission checking methods&lt;br /&gt;
  def allows_action?&lt;br /&gt;
    %w[OK Late].include?(name)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def denies_action?&lt;br /&gt;
    name == 'No'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def allows_with_penalty?&lt;br /&gt;
    name == 'Late'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def allows_without_penalty?&lt;br /&gt;
    name == 'OK'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Display methods&lt;br /&gt;
  def to_s&lt;br /&gt;
    name&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def permission_level&lt;br /&gt;
    case name&lt;br /&gt;
    when 'No'   then 0&lt;br /&gt;
    when 'Late' then 1&lt;br /&gt;
    when 'OK'   then 2&lt;br /&gt;
    else -1&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== DueDate Model Refactoring ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model currently mixes persistence logic, deadline calculation, and permission checking, violating SRP.  &lt;br /&gt;
Most of its logic is implemented as class methods, which makes testing and extension difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Current design&lt;br /&gt;
def self.copy(old_assignment_id, new_assignment_id)&lt;br /&gt;
  duedates = where(parent_id: old_assignment_id)&lt;br /&gt;
  duedates.each do |orig_due_date|&lt;br /&gt;
    new_due_date = orig_due_date.dup&lt;br /&gt;
    new_due_date.parent_id = new_assignment_id&lt;br /&gt;
    new_due_date.save&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Improved Design ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
&lt;br /&gt;
  belongs_to :parent, polymorphic: true&lt;br /&gt;
  belongs_to :deadline_type, foreign_key: :deadline_type_id&lt;br /&gt;
&lt;br /&gt;
  validates :due_at, presence: true&lt;br /&gt;
  validates :deadline_type_id, presence: true&lt;br /&gt;
  validates :parent, presence: true&lt;br /&gt;
  validates :round, numericality: { greater_than: 0 }, allow_nil: true&lt;br /&gt;
&lt;br /&gt;
  # Scopes for common queries&lt;br /&gt;
  scope :upcoming, -&amp;gt; { where('due_at &amp;gt; ?', Time.current).order(:due_at) }&lt;br /&gt;
  scope :overdue, -&amp;gt; { where('due_at &amp;lt; ?', Time.current).order(:due_at) }&lt;br /&gt;
  scope :for_round, -&amp;gt;(round_num) { where(round: round_num) }&lt;br /&gt;
  scope :for_deadline_type, -&amp;gt;(type_name) { joins(:deadline_type).where(deadline_types: { name: type_name }) }&lt;br /&gt;
&lt;br /&gt;
  # Instance methods replace class methods&lt;br /&gt;
  def copy(to_assignment_id)&lt;br /&gt;
    to_assignment = Assignment.find(to_assignment_id)&lt;br /&gt;
    new_due_date = dup&lt;br /&gt;
    new_due_date.parent = to_assignment&lt;br /&gt;
    new_due_date.save!&lt;br /&gt;
    new_due_date&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def set(deadline_type_id, parent_id, round)&lt;br /&gt;
    self.deadline_type_id = deadline_type_id&lt;br /&gt;
    self.parent_id = parent_id&lt;br /&gt;
    self.round = round&lt;br /&gt;
    save!&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if this deadline has passed&lt;br /&gt;
  def overdue?&lt;br /&gt;
    due_at &amp;lt; Time.current&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if this deadline is upcoming&lt;br /&gt;
  def upcoming?&lt;br /&gt;
    due_at &amp;gt; Time.current&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the deadline type name&lt;br /&gt;
  def deadline_type_name&lt;br /&gt;
    deadline_type&amp;amp;.name&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Comparison method for sorting&lt;br /&gt;
  def &amp;lt;=&amp;gt;(other)&lt;br /&gt;
    return nil unless other.is_a?(DueDate)&lt;br /&gt;
    due_at &amp;lt;=&amp;gt; other.due_at&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Class methods for collection operations&lt;br /&gt;
  class &amp;lt;&amp;lt; self&lt;br /&gt;
    def sort_due_dates(due_dates)&lt;br /&gt;
      due_dates.sort_by(&amp;amp;:due_at)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    def any_future_due_dates?(due_dates)&lt;br /&gt;
      due_dates.any?(&amp;amp;:upcoming?)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    def copy(from_assignment_id, to_assignment_id)&lt;br /&gt;
      from_assignment = Assignment.find(from_assignment_id)&lt;br /&gt;
      to_assignment = Assignment.find(to_assignment_id)&lt;br /&gt;
&lt;br /&gt;
      from_assignment.due_dates.each do |due_date|&lt;br /&gt;
        new_due_date = due_date.dup&lt;br /&gt;
        new_due_date.parent = to_assignment&lt;br /&gt;
        new_due_date.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  before_save :set_default_round&lt;br /&gt;
&lt;br /&gt;
  def set_default_round&lt;br /&gt;
    self.round ||= 1&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Permission-Checking Methods ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Currently, permission checks depend only on user roles and ignore the actual deadline.  &lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Role-based permissions (participant_helpers.rb)&lt;br /&gt;
def participant_permissions(authorization)&lt;br /&gt;
  case authorization&lt;br /&gt;
  when 'reader'   then { can_submit: false, can_review: true, can_take_quiz: true }&lt;br /&gt;
  when 'reviewer' then { can_submit: false, can_review: true, can_take_quiz: false }&lt;br /&gt;
  when 'submitter' then { can_submit: true, can_review: false, can_take_quiz: false }&lt;br /&gt;
  else { can_submit: true, can_review: true, can_take_quiz: true }&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And deadline-based checks exist separately in the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def submission_allowed(topic_id = nil)&lt;br /&gt;
  check_condition('submission_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
def can_review(topic_id = nil)&lt;br /&gt;
  check_condition('review_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These two layers (role-based and time-based) never interact, leading to inconsistencies (e.g., a user with &amp;lt;code&amp;gt;can_submit=true&amp;lt;/code&amp;gt; after deadline).&lt;br /&gt;
&lt;br /&gt;
==== Proposed Changes ====&lt;br /&gt;
&lt;br /&gt;
The new &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module integrates role-based and deadline-based checks:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDatePermissions&lt;br /&gt;
  # Permission checking methods that combine deadline-based and role-based logic&lt;br /&gt;
  #&lt;br /&gt;
  # These methods must check both:&lt;br /&gt;
  # 1. Whether the action is permitted by the current deadline (submission_allowed_id, review_allowed_id, etc.)&lt;br /&gt;
  # 2. Whether the participant has the necessary permissions (can_submit, can_review, can_take_quiz fields)&lt;br /&gt;
&lt;br /&gt;
  # Check if submission is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_submit?(participant = nil)&lt;br /&gt;
    return false unless submission_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_submit&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: submission_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if review is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_review?(participant = nil)&lt;br /&gt;
    return false unless review_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_review&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: review_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if taking a quiz is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_take_quiz?(participant = nil)&lt;br /&gt;
    return false unless quiz_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_take_quiz&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: quiz_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if teammate review is allowed&lt;br /&gt;
  def can_review_teammate?(participant = nil)&lt;br /&gt;
    return false unless teammate_review_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_review&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: teammate_review_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Generic permission checker&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    permission_field = &amp;quot;#{activity}_allowed_id&amp;quot;&lt;br /&gt;
    return false unless respond_to?(permission_field)&lt;br /&gt;
&lt;br /&gt;
    allowed_id = public_send(permission_field)&lt;br /&gt;
    return false unless allowed_id&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get permission status for an action (OK, Late, No)&lt;br /&gt;
  def permission_status_for(action)&lt;br /&gt;
    permission_field = &amp;quot;#{action}_allowed_id&amp;quot;&lt;br /&gt;
    return 'No' unless respond_to?(permission_field)&lt;br /&gt;
&lt;br /&gt;
    allowed_id = public_send(permission_field)&lt;br /&gt;
    return 'No' unless allowed_id&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name || 'No'&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This module is included in the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 5. DueDateActions Mixin ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module provides deadline-related operations for models that have due dates (Assignment, SignUpTopic).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDateActions&lt;br /&gt;
  # Generic activity permission checker that determines if an activity is permissible&lt;br /&gt;
  # based on the current deadline state for this parent object&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    current_deadline = next_due_date()&lt;br /&gt;
    return false unless current_deadline&lt;br /&gt;
&lt;br /&gt;
    current_deadline.activity_permissible?(activity)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Syntactic sugar methods for common activities&lt;br /&gt;
  def submission_permissible?&lt;br /&gt;
    activity_permissible?(:submission)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def review_permissible?&lt;br /&gt;
    activity_permissible?(:review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def teammate_review_permissible?&lt;br /&gt;
    activity_permissible?(:teammate_review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def quiz_permissible?&lt;br /&gt;
    activity_permissible?(:quiz)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def team_formation_permissible?&lt;br /&gt;
    activity_permissible?(:team_formation)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def signup_permissible?&lt;br /&gt;
    activity_permissible?(:signup)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def drop_topic_permissible?&lt;br /&gt;
    activity_permissible?(:drop_topic)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the next due date for this assignment&lt;br /&gt;
  def next_due_date(topic_id = nil)&lt;br /&gt;
    # If a topic is specified and this assignment has topic-specific deadlines,&lt;br /&gt;
    # look for topic due dates first&lt;br /&gt;
    if topic_id &amp;amp;&amp;amp; has_topic_specific_deadlines?&lt;br /&gt;
      topic_deadline = due_dates.where(parent_id: topic_id, parent_type: 'SignUpTopic')&lt;br /&gt;
                               .upcoming&lt;br /&gt;
                               .first&lt;br /&gt;
      return topic_deadline if topic_deadline&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Fall back to assignment-level due dates&lt;br /&gt;
    due_dates.upcoming.first&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the current stage name for display purposes&lt;br /&gt;
  def current_stage&lt;br /&gt;
    deadline = next_due_date()&lt;br /&gt;
    return 'finished' unless deadline&lt;br /&gt;
&lt;br /&gt;
    deadline.deadline_type&amp;amp;.name || 'unknown'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if assignment has topic-specific deadlines&lt;br /&gt;
  def has_topic_specific_deadlines?&lt;br /&gt;
    staggered_deadline || due_dates.where(parent_type: 'SignUpTopic').exists?&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Copy due dates to a new parent object&lt;br /&gt;
  def copy_due_dates_to(new_parent)&lt;br /&gt;
    due_dates.find_each do |due_date|&lt;br /&gt;
      new_due_date = due_date.dup&lt;br /&gt;
      new_due_date.parent = new_parent&lt;br /&gt;
      new_due_date.save!&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This module is included in &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class Assignment &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
class SignUpTopic &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Implementation Summary ==&lt;br /&gt;
&lt;br /&gt;
=== Models Created/Modified ===&lt;br /&gt;
&lt;br /&gt;
* '''DeadlineType''' - Canonical deadline type definitions with semantic helper methods&lt;br /&gt;
* '''DeadlineRight''' - Permission state model (OK/Late/No) with comparison methods&lt;br /&gt;
* '''DueDate''' - Refactored with instance methods, scopes, and permission checking&lt;br /&gt;
* '''TopicDueDate''' - STI subclass for topic-specific deadlines&lt;br /&gt;
* '''Assignment''' - Includes DueDateActions mixin&lt;br /&gt;
* '''SignUpTopic''' - Includes DueDateActions mixin&lt;br /&gt;
&lt;br /&gt;
=== Modules Created ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions''' - Permission checking combining deadline and role constraints&lt;br /&gt;
* '''DueDateActions''' - Deadline-related operations for parent models (Assignment, SignUpTopic)&lt;br /&gt;
&lt;br /&gt;
=== Key Improvements ===&lt;br /&gt;
&lt;br /&gt;
# '''Separation of Concerns''': Permission logic separated into dedicated modules&lt;br /&gt;
# '''Instance Methods''': Replaced class methods with testable instance methods&lt;br /&gt;
# '''Polymorphic Design''': Single DueDate model serves both Assignment and SignUpTopic&lt;br /&gt;
# '''Unified Permissions''': Role-based and time-based checks combined in one interface&lt;br /&gt;
# '''Data Integrity''': Removed duplicate deadline types, enforced foreign key constraints&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
The test suite includes comprehensive RSpec tests for the ''DueDate'' model and its associated models (''DeadlineType'', ''DeadlineRight'').&lt;br /&gt;
These tests verify model validations, scopes, instance methods, class methods, and callbacks with extensive coverage of edge cases and error conditions.&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
The test suite follows RSpec best practices with:&lt;br /&gt;
* '''Nested contexts''' for different scenarios (e.g., &amp;quot;when parent is missing&amp;quot;, &amp;quot;when round is 0&amp;quot;)&lt;br /&gt;
* '''Multiple assertions per context''' to verify behavior from different angles&lt;br /&gt;
* '''Edge case coverage''' including empty collections, nil values, and boundary conditions&lt;br /&gt;
* '''Error case testing''' for invalid data and non-existent records&lt;br /&gt;
* '''Clear descriptive names''' using &amp;quot;context 'when...'&amp;quot; and &amp;quot;it 'does something'&amp;quot; patterns&lt;br /&gt;
&lt;br /&gt;
=== Current Coverage ===&lt;br /&gt;
&lt;br /&gt;
==== Associations ====&lt;br /&gt;
* Tests verify &amp;lt;code&amp;gt;belongs_to :parent&amp;lt;/code&amp;gt; (polymorphic) and &amp;lt;code&amp;gt;belongs_to :deadline_type&amp;lt;/code&amp;gt; relationships&lt;br /&gt;
&lt;br /&gt;
==== Validations ====&lt;br /&gt;
* '''Required field validation''': Tests for parent, due_at, and deadline_type_id presence&lt;br /&gt;
* '''Round validation''': Tests for positive values, zero rejection, negative rejection, and nil handling&lt;br /&gt;
* '''Error messages''': Verifies specific error messages are added to appropriate fields&lt;br /&gt;
* '''Valid record creation''': Confirms records persist when all requirements are met&lt;br /&gt;
&lt;br /&gt;
==== Scopes ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;''': Returns future due dates ordered by due_at, excludes past dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;''': Returns past due dates ordered by due_at, excludes future dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;''': Filters by round number, handles non-existent rounds&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt;''': Filters by deadline type name, handles non-existent types&lt;br /&gt;
&lt;br /&gt;
==== Instance Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#overdue?&amp;lt;/code&amp;gt;''': Tests past dates (true), future dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#upcoming?&amp;lt;/code&amp;gt;''': Tests future dates (true), past dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#set&amp;lt;/code&amp;gt;''': Verifies updating deadline_type_id, parent_id, and round with persistence and error handling&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#copy&amp;lt;/code&amp;gt;''': Tests duplication to new assignment, attribute preservation, error handling for non-existent targets&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#deadline_type_name&amp;lt;/code&amp;gt;''': Returns associated deadline type name, handles nil cases&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#last_deadline?&amp;lt;/code&amp;gt;''': Tests detection of final deadline, handles multiple deadlines and single deadline scenarios&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#&amp;lt;=&amp;gt;&amp;lt;/code&amp;gt;''': Tests comparison by due_at time, handles non-DueDate objects&lt;br /&gt;
&lt;br /&gt;
==== Class Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.sort_due_dates&amp;lt;/code&amp;gt;''': Sorts by due_at ascending, handles empty arrays and single elements&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.any_future_due_dates?&amp;lt;/code&amp;gt;''': Detects future dates in collections, handles empty arrays and mixed date collections&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.copy&amp;lt;/code&amp;gt;''': Copies all due dates between assignments, preserves attributes, handles empty sources and non-existent records&lt;br /&gt;
&lt;br /&gt;
==== Callbacks ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;before_save :set_default_round&amp;lt;/code&amp;gt;''': Sets round to 1 when not specified, preserves explicit values, handles nil&lt;br /&gt;
&lt;br /&gt;
=== Test Examples ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Validation with multiple assertions&lt;br /&gt;
describe 'validations' do&lt;br /&gt;
  context 'when parent is missing' do&lt;br /&gt;
    let(:due_date) do&lt;br /&gt;
      DueDate.new(&lt;br /&gt;
        parent: nil,&lt;br /&gt;
        due_at: 2.days.from_now,&lt;br /&gt;
        deadline_type: submission_type,&lt;br /&gt;
        submission_allowed_id: ok_right.id,&lt;br /&gt;
        review_allowed_id: ok_right.id&lt;br /&gt;
      )&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'is invalid' do&lt;br /&gt;
      expect(due_date).to be_invalid&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'has error on parent field' do&lt;br /&gt;
      due_date.valid?&lt;br /&gt;
      expect(due_date.errors[:parent]).to include('must exist')&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when round validation' do&lt;br /&gt;
    context 'with round value of 0' do&lt;br /&gt;
      let(:due_date) do&lt;br /&gt;
        DueDate.new(&lt;br /&gt;
          parent: assignment,&lt;br /&gt;
          due_at: 2.days.from_now,&lt;br /&gt;
          deadline_type: submission_type,&lt;br /&gt;
          submission_allowed_id: ok_right.id,&lt;br /&gt;
          review_allowed_id: ok_right.id,&lt;br /&gt;
          round: 0&lt;br /&gt;
        )&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'is invalid' do&lt;br /&gt;
        expect(due_date).to be_invalid&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'has error on round field' do&lt;br /&gt;
        due_date.valid?&lt;br /&gt;
        expect(due_date.errors[:round]).to be_present&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'with nil round' do&lt;br /&gt;
      let(:due_date) do&lt;br /&gt;
        DueDate.create(&lt;br /&gt;
          parent: assignment,&lt;br /&gt;
          due_at: 2.days.from_now,&lt;br /&gt;
          deadline_type: submission_type,&lt;br /&gt;
          submission_allowed_id: ok_right.id,&lt;br /&gt;
          review_allowed_id: ok_right.id,&lt;br /&gt;
          round: nil&lt;br /&gt;
        )&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'is valid' do&lt;br /&gt;
        expect(due_date).to be_valid&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'sets default round to 1' do&lt;br /&gt;
        expect(due_date.round).to eq(1)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
# Scope with edge cases&lt;br /&gt;
describe '.upcoming' do&lt;br /&gt;
  it 'returns only future due dates' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).to contain_exactly(upcoming_due_date1, upcoming_due_date2)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'orders results by due_at ascending' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).to eq([upcoming_due_date1, upcoming_due_date2])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'excludes past due dates' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).not_to include(past_due_date)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when all due dates are in the past' do&lt;br /&gt;
    before do&lt;br /&gt;
      DueDate.destroy_all&lt;br /&gt;
      DueDate.create(&lt;br /&gt;
        parent: assignment,&lt;br /&gt;
        due_at: 3.days.ago,&lt;br /&gt;
        deadline_type: submission_type,&lt;br /&gt;
        submission_allowed_id: ok_right.id,&lt;br /&gt;
        review_allowed_id: ok_right.id&lt;br /&gt;
      )&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns empty collection' do&lt;br /&gt;
      expect(DueDate.upcoming).to be_empty&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
# Instance method with error handling&lt;br /&gt;
describe '#copy' do&lt;br /&gt;
  let(:original) do&lt;br /&gt;
    DueDate.create(&lt;br /&gt;
      parent: assignment,&lt;br /&gt;
      due_at: 2.days.from_now,&lt;br /&gt;
      deadline_type: submission_type,&lt;br /&gt;
      submission_allowed_id: ok_right.id,&lt;br /&gt;
      review_allowed_id: late_right.id,&lt;br /&gt;
      round: 1&lt;br /&gt;
    )&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  let(:copied) { original.copy(assignment2.id) }&lt;br /&gt;
&lt;br /&gt;
  it 'creates a new persisted record' do&lt;br /&gt;
    expect(copied).to be_persisted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'has different id from original' do&lt;br /&gt;
    expect(copied.id).not_to eq(original.id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'preserves all attributes' do&lt;br /&gt;
    expect(copied.due_at).to eq(original.due_at)&lt;br /&gt;
    expect(copied.deadline_type_id).to eq(original.deadline_type_id)&lt;br /&gt;
    expect(copied.round).to eq(original.round)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when copying to non-existent assignment' do&lt;br /&gt;
    it 'raises error' do&lt;br /&gt;
      expect do&lt;br /&gt;
        original.copy(99999)&lt;br /&gt;
      end.to raise_error(ActiveRecord::RecordNotFound)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Test Metrics ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDate model''': 60+ examples covering all methods and edge cases&lt;br /&gt;
* '''Context depth''': 2-3 levels for complex scenarios&lt;br /&gt;
* '''Code coverage''': 100% coverage of DueDate model methods&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
=== Demo Link ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add deadline notification system using the new permission framework&lt;br /&gt;
* Implement deadline templates for common assignment patterns&lt;br /&gt;
* Add API endpoints for mobile app integration&lt;br /&gt;
* Create admin dashboard for monitoring deadline compliance across courses&lt;br /&gt;
* Add automated deadline extension workflows based on configurable rules&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167178</id>
		<title>CSC/ECE 517 Fall 2025 - E2566. Finish DueDates</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167178"/>
		<updated>2025-12-02T00:14:52Z</updated>

		<summary type="html">&lt;p&gt;Skim253: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
This project aims to complete the implementation of the DueDate system in Expertiza. The current implementation consists mostly of class methods and lacks proper definition of different deadline types and permission checking functionality. This redesign will create a more robust, readable, and maintainable due date system.&lt;br /&gt;
&lt;br /&gt;
=== Existing Components ===&lt;br /&gt;
* &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model with polymorphic parent association (Assignment/SignUpTopic)&lt;br /&gt;
* &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; subclasses&lt;br /&gt;
* Basic CRUD operations (within &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AssignmentForm&amp;lt;/code&amp;gt;) and sorting functionality (&amp;lt;code&amp;gt;deadline_sort&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Database schema with &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt; field&lt;br /&gt;
&lt;br /&gt;
=== Current Behavior ===&lt;br /&gt;
&lt;br /&gt;
====Admin's View====&lt;br /&gt;
[[File:Duedates.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* When editing an assignment, due dates can be modified under the Due Dates tab.&lt;br /&gt;
* Each row contains the following columns: &amp;lt;code&amp;gt;Date &amp;amp; Time&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Use Date Updater&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Submission Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Teammate Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Meta-Review Allowed&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Reminder&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:Assignments-CurrentStage.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* In the Assignments table, the ''DueDate'' of each assignment is displayed as Current Stage and Stage Deadline.&lt;br /&gt;
&lt;br /&gt;
====Student's View====&lt;br /&gt;
[[File:Student-assignments.png|600px]]&lt;br /&gt;
* Students can view the due date information under &amp;lt;code&amp;gt;Current State&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Stage Deadline&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
* The actions available to students are determined by the current DueDate status.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-not-over.png|600px]]&lt;br /&gt;
* For example, during the submission period, students can access the &amp;lt;code&amp;gt;Submit a hyperlink&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Submit a file&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-over.png|600px]]&lt;br /&gt;
* Once the due date has passed, students cannot submit either a hyperlink or a file.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Currently, there are some glaring issues with how due_dates was created in the first place, including redundancy and lack of modularity.&lt;br /&gt;
&lt;br /&gt;
# No dedicated &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to define different kinds of deadlines. The existing &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; class only serves as an association for &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; models, and is never referenced in the current &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; implementation.&lt;br /&gt;
# Overuse of class methods making the code difficult to maintain.&lt;br /&gt;
# Missing permission checking logic for user actions.&lt;br /&gt;
# Incomplete deadline type definitions — &amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt; need to be added.&lt;br /&gt;
# Duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table, which may lead to potential bugs.&lt;br /&gt;
# No clear separation of concerns — data persistence, business logic, and permission logic are mixed in a single model.&lt;br /&gt;
&lt;br /&gt;
=== Tasks Identified ===&lt;br /&gt;
&lt;br /&gt;
# Redesign the &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.&lt;br /&gt;
# Refactor the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model to separate persistence, business logic, and permission logic into distinct layers.&lt;br /&gt;
# Introduce instance-level methods (e.g., &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;) to replace class-level utilities and improve testability.&lt;br /&gt;
# Integrate role-based and time-based permission checks within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; to ensure consistent access control.&lt;br /&gt;
# Add missing deadline types (&amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt;) and remove duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries for data integrity.&lt;br /&gt;
# Implement reusable mixins (e.g., &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;) to handle permission evaluation and deadline-related operations.&lt;br /&gt;
# Update controllers and APIs to use the new mixin-based approach for consistent permission checks and deadline queries.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Proposed Solution ==&lt;br /&gt;
&lt;br /&gt;
=== DeadlineType Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Although the current codebase includes a &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model, it only serves as a passive lookup table for associations with &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt;.  &lt;br /&gt;
In practice, most parts of the system still rely on hard-coded numeric IDs or manual lookups such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Example of current usage&lt;br /&gt;
deadline_type_id = DeadlineType.find_by(name: 'review').id&lt;br /&gt;
if due_date.deadline_type_id == deadline_type_id&lt;br /&gt;
  # Perform review-related logic&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of using the model as an active domain object, &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; directly compares IDs or string literals.  &lt;br /&gt;
This leads to several structural issues:&lt;br /&gt;
&lt;br /&gt;
* Inconsistent references (some use IDs, others strings)&lt;br /&gt;
* Tight coupling between &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
* Lack of helper semantics (e.g., &amp;lt;code&amp;gt;is_review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;is_submission?&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Data duplication and integrity risks (two &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; rows)&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; will serve as a source of truth, defining all valid deadline types and providing meaningful interfaces.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE deadline_types (&lt;br /&gt;
  id BIGINT PRIMARY KEY,&lt;br /&gt;
  name VARCHAR(255) NOT NULL UNIQUE,&lt;br /&gt;
  description TEXT NOT NULL,&lt;br /&gt;
  created_at TIMESTAMP,&lt;br /&gt;
  updated_at TIMESTAMP&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Deadline Type Definitions ====&lt;br /&gt;
* &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;: submission - Student work submission deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt;: review - Peer review deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;3&amp;lt;/code&amp;gt;: teammate_review - Team member evaluation deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;5&amp;lt;/code&amp;gt;: metareview - Meta-review deadlines (kept for backward compatibility)  &lt;br /&gt;
* &amp;lt;code&amp;gt;6&amp;lt;/code&amp;gt;: drop_topic - Topic drop deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;7&amp;lt;/code&amp;gt;: signup - Course/assignment signup deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;8&amp;lt;/code&amp;gt;: team_formation - Team formation deadlines (clean up duplicate ID 10)  &lt;br /&gt;
* &amp;lt;code&amp;gt;11&amp;lt;/code&amp;gt;: quiz - Quiz completion deadlines&lt;br /&gt;
&lt;br /&gt;
==== DeadlineType Model Implementation ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineType &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
  validates :description, presence: true&lt;br /&gt;
&lt;br /&gt;
  has_many :due_dates, foreign_key: :deadline_type_id, dependent: :restrict_with_exception&lt;br /&gt;
&lt;br /&gt;
  # Semantic helper methods for deadline type identification&lt;br /&gt;
  def submission?&lt;br /&gt;
    name == 'submission'&lt;br /&gt;
  end&lt;br /&gt;
  # same code for review, teammate_review, quiz, team_formation, signup, drop_topic   &lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== DeadlineRight Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model represents the permission level for a specific action at a given deadline.&lt;br /&gt;
It defines three states: &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt; (action allowed), &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt; (action allowed with penalty), and &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt; (action denied).&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE deadline_rights (&lt;br /&gt;
  id BIGINT PRIMARY KEY,&lt;br /&gt;
  name VARCHAR(255) NOT NULL UNIQUE,&lt;br /&gt;
  created_at TIMESTAMP,&lt;br /&gt;
  updated_at TIMESTAMP&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== DeadlineRight Model ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineRight &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
&lt;br /&gt;
  # Permission checking methods&lt;br /&gt;
  def allows_action?&lt;br /&gt;
    %w[OK Late].include?(name)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def denies_action?&lt;br /&gt;
    name == 'No'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def allows_with_penalty?&lt;br /&gt;
    name == 'Late'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def allows_without_penalty?&lt;br /&gt;
    name == 'OK'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Display methods&lt;br /&gt;
  def to_s&lt;br /&gt;
    name&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def permission_level&lt;br /&gt;
    case name&lt;br /&gt;
    when 'No'   then 0&lt;br /&gt;
    when 'Late' then 1&lt;br /&gt;
    when 'OK'   then 2&lt;br /&gt;
    else -1&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== DueDate Model Refactoring ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model currently mixes persistence logic, deadline calculation, and permission checking, violating SRP.  &lt;br /&gt;
Most of its logic is implemented as class methods, which makes testing and extension difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Current design&lt;br /&gt;
def self.copy(old_assignment_id, new_assignment_id)&lt;br /&gt;
  duedates = where(parent_id: old_assignment_id)&lt;br /&gt;
  duedates.each do |orig_due_date|&lt;br /&gt;
    new_due_date = orig_due_date.dup&lt;br /&gt;
    new_due_date.parent_id = new_assignment_id&lt;br /&gt;
    new_due_date.save&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Improved Design ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
&lt;br /&gt;
  belongs_to :parent, polymorphic: true&lt;br /&gt;
  belongs_to :deadline_type, foreign_key: :deadline_type_id&lt;br /&gt;
&lt;br /&gt;
  validates :due_at, presence: true&lt;br /&gt;
  validates :deadline_type_id, presence: true&lt;br /&gt;
  validates :parent, presence: true&lt;br /&gt;
  validates :round, numericality: { greater_than: 0 }, allow_nil: true&lt;br /&gt;
&lt;br /&gt;
  # Scopes for common queries&lt;br /&gt;
  scope :upcoming, -&amp;gt; { where('due_at &amp;gt; ?', Time.current).order(:due_at) }&lt;br /&gt;
  scope :overdue, -&amp;gt; { where('due_at &amp;lt; ?', Time.current).order(:due_at) }&lt;br /&gt;
  scope :for_round, -&amp;gt;(round_num) { where(round: round_num) }&lt;br /&gt;
  scope :for_deadline_type, -&amp;gt;(type_name) { joins(:deadline_type).where(deadline_types: { name: type_name }) }&lt;br /&gt;
&lt;br /&gt;
  # Instance methods replace class methods&lt;br /&gt;
  def copy(to_assignment_id)&lt;br /&gt;
    to_assignment = Assignment.find(to_assignment_id)&lt;br /&gt;
    new_due_date = dup&lt;br /&gt;
    new_due_date.parent = to_assignment&lt;br /&gt;
    new_due_date.save!&lt;br /&gt;
    new_due_date&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def set(deadline_type_id, parent_id, round)&lt;br /&gt;
    self.deadline_type_id = deadline_type_id&lt;br /&gt;
    self.parent_id = parent_id&lt;br /&gt;
    self.round = round&lt;br /&gt;
    save!&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if this deadline has passed&lt;br /&gt;
  def overdue?&lt;br /&gt;
    due_at &amp;lt; Time.current&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if this deadline is upcoming&lt;br /&gt;
  def upcoming?&lt;br /&gt;
    due_at &amp;gt; Time.current&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the deadline type name&lt;br /&gt;
  def deadline_type_name&lt;br /&gt;
    deadline_type&amp;amp;.name&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Comparison method for sorting&lt;br /&gt;
  def &amp;lt;=&amp;gt;(other)&lt;br /&gt;
    return nil unless other.is_a?(DueDate)&lt;br /&gt;
    due_at &amp;lt;=&amp;gt; other.due_at&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Class methods for collection operations&lt;br /&gt;
  class &amp;lt;&amp;lt; self&lt;br /&gt;
    def sort_due_dates(due_dates)&lt;br /&gt;
      due_dates.sort_by(&amp;amp;:due_at)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    def any_future_due_dates?(due_dates)&lt;br /&gt;
      due_dates.any?(&amp;amp;:upcoming?)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    def copy(from_assignment_id, to_assignment_id)&lt;br /&gt;
      from_assignment = Assignment.find(from_assignment_id)&lt;br /&gt;
      to_assignment = Assignment.find(to_assignment_id)&lt;br /&gt;
&lt;br /&gt;
      from_assignment.due_dates.each do |due_date|&lt;br /&gt;
        new_due_date = due_date.dup&lt;br /&gt;
        new_due_date.parent = to_assignment&lt;br /&gt;
        new_due_date.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  before_save :set_default_round&lt;br /&gt;
&lt;br /&gt;
  def set_default_round&lt;br /&gt;
    self.round ||= 1&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Permission-Checking Methods ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Currently, permission checks depend only on user roles and ignore the actual deadline.  &lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Role-based permissions (participant_helpers.rb)&lt;br /&gt;
def participant_permissions(authorization)&lt;br /&gt;
  case authorization&lt;br /&gt;
  when 'reader'   then { can_submit: false, can_review: true, can_take_quiz: true }&lt;br /&gt;
  when 'reviewer' then { can_submit: false, can_review: true, can_take_quiz: false }&lt;br /&gt;
  when 'submitter' then { can_submit: true, can_review: false, can_take_quiz: false }&lt;br /&gt;
  else { can_submit: true, can_review: true, can_take_quiz: true }&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And deadline-based checks exist separately in the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def submission_allowed(topic_id = nil)&lt;br /&gt;
  check_condition('submission_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
def can_review(topic_id = nil)&lt;br /&gt;
  check_condition('review_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These two layers (role-based and time-based) never interact, leading to inconsistencies (e.g., a user with &amp;lt;code&amp;gt;can_submit=true&amp;lt;/code&amp;gt; after deadline).&lt;br /&gt;
&lt;br /&gt;
==== Proposed Changes ====&lt;br /&gt;
&lt;br /&gt;
The new &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module integrates role-based and deadline-based checks:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDatePermissions&lt;br /&gt;
  # Permission checking methods that combine deadline-based and role-based logic&lt;br /&gt;
  #&lt;br /&gt;
  # These methods must check both:&lt;br /&gt;
  # 1. Whether the action is permitted by the current deadline (submission_allowed_id, review_allowed_id, etc.)&lt;br /&gt;
  # 2. Whether the participant has the necessary permissions (can_submit, can_review, can_take_quiz fields)&lt;br /&gt;
&lt;br /&gt;
  # Check if submission is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_submit?(participant = nil)&lt;br /&gt;
    return false unless submission_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_submit&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: submission_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if review is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_review?(participant = nil)&lt;br /&gt;
    return false unless review_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_review&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: review_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if taking a quiz is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_take_quiz?(participant = nil)&lt;br /&gt;
    return false unless quiz_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_take_quiz&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: quiz_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if teammate review is allowed&lt;br /&gt;
  def can_review_teammate?(participant = nil)&lt;br /&gt;
    return false unless teammate_review_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_review&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: teammate_review_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Generic permission checker&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    permission_field = &amp;quot;#{activity}_allowed_id&amp;quot;&lt;br /&gt;
    return false unless respond_to?(permission_field)&lt;br /&gt;
&lt;br /&gt;
    allowed_id = public_send(permission_field)&lt;br /&gt;
    return false unless allowed_id&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get permission status for an action (OK, Late, No)&lt;br /&gt;
  def permission_status_for(action)&lt;br /&gt;
    permission_field = &amp;quot;#{action}_allowed_id&amp;quot;&lt;br /&gt;
    return 'No' unless respond_to?(permission_field)&lt;br /&gt;
&lt;br /&gt;
    allowed_id = public_send(permission_field)&lt;br /&gt;
    return 'No' unless allowed_id&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name || 'No'&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This module is included in the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 5. DueDateActions Mixin ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module provides deadline-related operations for models that have due dates (Assignment, SignUpTopic).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDateActions&lt;br /&gt;
  # Generic activity permission checker that determines if an activity is permissible&lt;br /&gt;
  # based on the current deadline state for this parent object&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    current_deadline = next_due_date()&lt;br /&gt;
    return false unless current_deadline&lt;br /&gt;
&lt;br /&gt;
    current_deadline.activity_permissible?(activity)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Syntactic sugar methods for common activities&lt;br /&gt;
  def submission_permissible?&lt;br /&gt;
    activity_permissible?(:submission)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def review_permissible?&lt;br /&gt;
    activity_permissible?(:review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def teammate_review_permissible?&lt;br /&gt;
    activity_permissible?(:teammate_review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def quiz_permissible?&lt;br /&gt;
    activity_permissible?(:quiz)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def team_formation_permissible?&lt;br /&gt;
    activity_permissible?(:team_formation)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def signup_permissible?&lt;br /&gt;
    activity_permissible?(:signup)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def drop_topic_permissible?&lt;br /&gt;
    activity_permissible?(:drop_topic)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the next due date for this assignment&lt;br /&gt;
  def next_due_date(topic_id = nil)&lt;br /&gt;
    # If a topic is specified and this assignment has topic-specific deadlines,&lt;br /&gt;
    # look for topic due dates first&lt;br /&gt;
    if topic_id &amp;amp;&amp;amp; has_topic_specific_deadlines?&lt;br /&gt;
      topic_deadline = due_dates.where(parent_id: topic_id, parent_type: 'SignUpTopic')&lt;br /&gt;
                               .upcoming&lt;br /&gt;
                               .first&lt;br /&gt;
      return topic_deadline if topic_deadline&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Fall back to assignment-level due dates&lt;br /&gt;
    due_dates.upcoming.first&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the current stage name for display purposes&lt;br /&gt;
  def current_stage&lt;br /&gt;
    deadline = next_due_date()&lt;br /&gt;
    return 'finished' unless deadline&lt;br /&gt;
&lt;br /&gt;
    deadline.deadline_type&amp;amp;.name || 'unknown'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if assignment has topic-specific deadlines&lt;br /&gt;
  def has_topic_specific_deadlines?&lt;br /&gt;
    staggered_deadline || due_dates.where(parent_type: 'SignUpTopic').exists?&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Copy due dates to a new parent object&lt;br /&gt;
  def copy_due_dates_to(new_parent)&lt;br /&gt;
    due_dates.find_each do |due_date|&lt;br /&gt;
      new_due_date = due_date.dup&lt;br /&gt;
      new_due_date.parent = new_parent&lt;br /&gt;
      new_due_date.save!&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This module is included in &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class Assignment &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
class SignUpTopic &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
The test suite includes comprehensive RSpec tests for the ''DueDate'' model and its associated models (''DeadlineType'', ''DeadlineRight'').&lt;br /&gt;
These tests verify model validations, scopes, instance methods, class methods, and callbacks with extensive coverage of edge cases and error conditions.&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
The test suite follows RSpec best practices with:&lt;br /&gt;
* '''Nested contexts''' for different scenarios (e.g., &amp;quot;when parent is missing&amp;quot;, &amp;quot;when round is 0&amp;quot;)&lt;br /&gt;
* '''Multiple assertions per context''' to verify behavior from different angles&lt;br /&gt;
* '''Edge case coverage''' including empty collections, nil values, and boundary conditions&lt;br /&gt;
* '''Error case testing''' for invalid data and non-existent records&lt;br /&gt;
* '''Clear descriptive names''' using &amp;quot;context 'when...'&amp;quot; and &amp;quot;it 'does something'&amp;quot; patterns&lt;br /&gt;
&lt;br /&gt;
=== Current Coverage ===&lt;br /&gt;
&lt;br /&gt;
==== Associations ====&lt;br /&gt;
* Tests verify &amp;lt;code&amp;gt;belongs_to :parent&amp;lt;/code&amp;gt; (polymorphic) and &amp;lt;code&amp;gt;belongs_to :deadline_type&amp;lt;/code&amp;gt; relationships&lt;br /&gt;
&lt;br /&gt;
==== Validations ====&lt;br /&gt;
* '''Required field validation''': Tests for parent, due_at, and deadline_type_id presence&lt;br /&gt;
* '''Round validation''': Tests for positive values, zero rejection, negative rejection, and nil handling&lt;br /&gt;
* '''Error messages''': Verifies specific error messages are added to appropriate fields&lt;br /&gt;
* '''Valid record creation''': Confirms records persist when all requirements are met&lt;br /&gt;
&lt;br /&gt;
==== Scopes ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;''': Returns future due dates ordered by due_at, excludes past dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;''': Returns past due dates ordered by due_at, excludes future dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;''': Filters by round number, handles non-existent rounds&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt;''': Filters by deadline type name, handles non-existent types&lt;br /&gt;
&lt;br /&gt;
==== Instance Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#overdue?&amp;lt;/code&amp;gt;''': Tests past dates (true), future dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#upcoming?&amp;lt;/code&amp;gt;''': Tests future dates (true), past dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#set&amp;lt;/code&amp;gt;''': Verifies updating deadline_type_id, parent_id, and round with persistence and error handling&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#copy&amp;lt;/code&amp;gt;''': Tests duplication to new assignment, attribute preservation, error handling for non-existent targets&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#deadline_type_name&amp;lt;/code&amp;gt;''': Returns associated deadline type name, handles nil cases&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#last_deadline?&amp;lt;/code&amp;gt;''': Tests detection of final deadline, handles multiple deadlines and single deadline scenarios&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#&amp;lt;=&amp;gt;&amp;lt;/code&amp;gt;''': Tests comparison by due_at time, handles non-DueDate objects&lt;br /&gt;
&lt;br /&gt;
==== Class Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.sort_due_dates&amp;lt;/code&amp;gt;''': Sorts by due_at ascending, handles empty arrays and single elements&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.any_future_due_dates?&amp;lt;/code&amp;gt;''': Detects future dates in collections, handles empty arrays and mixed date collections&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.copy&amp;lt;/code&amp;gt;''': Copies all due dates between assignments, preserves attributes, handles empty sources and non-existent records&lt;br /&gt;
&lt;br /&gt;
==== Callbacks ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;before_save :set_default_round&amp;lt;/code&amp;gt;''': Sets round to 1 when not specified, preserves explicit values, handles nil&lt;br /&gt;
&lt;br /&gt;
=== Test Examples ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Validation with multiple assertions&lt;br /&gt;
describe 'validations' do&lt;br /&gt;
  context 'when parent is missing' do&lt;br /&gt;
    let(:due_date) do&lt;br /&gt;
      DueDate.new(&lt;br /&gt;
        parent: nil,&lt;br /&gt;
        due_at: 2.days.from_now,&lt;br /&gt;
        deadline_type: submission_type,&lt;br /&gt;
        submission_allowed_id: ok_right.id,&lt;br /&gt;
        review_allowed_id: ok_right.id&lt;br /&gt;
      )&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'is invalid' do&lt;br /&gt;
      expect(due_date).to be_invalid&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'has error on parent field' do&lt;br /&gt;
      due_date.valid?&lt;br /&gt;
      expect(due_date.errors[:parent]).to include('must exist')&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when round validation' do&lt;br /&gt;
    context 'with round value of 0' do&lt;br /&gt;
      let(:due_date) do&lt;br /&gt;
        DueDate.new(&lt;br /&gt;
          parent: assignment,&lt;br /&gt;
          due_at: 2.days.from_now,&lt;br /&gt;
          deadline_type: submission_type,&lt;br /&gt;
          submission_allowed_id: ok_right.id,&lt;br /&gt;
          review_allowed_id: ok_right.id,&lt;br /&gt;
          round: 0&lt;br /&gt;
        )&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'is invalid' do&lt;br /&gt;
        expect(due_date).to be_invalid&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'has error on round field' do&lt;br /&gt;
        due_date.valid?&lt;br /&gt;
        expect(due_date.errors[:round]).to be_present&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'with nil round' do&lt;br /&gt;
      let(:due_date) do&lt;br /&gt;
        DueDate.create(&lt;br /&gt;
          parent: assignment,&lt;br /&gt;
          due_at: 2.days.from_now,&lt;br /&gt;
          deadline_type: submission_type,&lt;br /&gt;
          submission_allowed_id: ok_right.id,&lt;br /&gt;
          review_allowed_id: ok_right.id,&lt;br /&gt;
          round: nil&lt;br /&gt;
        )&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'is valid' do&lt;br /&gt;
        expect(due_date).to be_valid&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'sets default round to 1' do&lt;br /&gt;
        expect(due_date.round).to eq(1)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
# Scope with edge cases&lt;br /&gt;
describe '.upcoming' do&lt;br /&gt;
  it 'returns only future due dates' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).to contain_exactly(upcoming_due_date1, upcoming_due_date2)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'orders results by due_at ascending' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).to eq([upcoming_due_date1, upcoming_due_date2])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'excludes past due dates' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).not_to include(past_due_date)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when all due dates are in the past' do&lt;br /&gt;
    before do&lt;br /&gt;
      DueDate.destroy_all&lt;br /&gt;
      DueDate.create(&lt;br /&gt;
        parent: assignment,&lt;br /&gt;
        due_at: 3.days.ago,&lt;br /&gt;
        deadline_type: submission_type,&lt;br /&gt;
        submission_allowed_id: ok_right.id,&lt;br /&gt;
        review_allowed_id: ok_right.id&lt;br /&gt;
      )&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns empty collection' do&lt;br /&gt;
      expect(DueDate.upcoming).to be_empty&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
# Instance method with error handling&lt;br /&gt;
describe '#copy' do&lt;br /&gt;
  let(:original) do&lt;br /&gt;
    DueDate.create(&lt;br /&gt;
      parent: assignment,&lt;br /&gt;
      due_at: 2.days.from_now,&lt;br /&gt;
      deadline_type: submission_type,&lt;br /&gt;
      submission_allowed_id: ok_right.id,&lt;br /&gt;
      review_allowed_id: late_right.id,&lt;br /&gt;
      round: 1&lt;br /&gt;
    )&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  let(:copied) { original.copy(assignment2.id) }&lt;br /&gt;
&lt;br /&gt;
  it 'creates a new persisted record' do&lt;br /&gt;
    expect(copied).to be_persisted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'has different id from original' do&lt;br /&gt;
    expect(copied.id).not_to eq(original.id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'preserves all attributes' do&lt;br /&gt;
    expect(copied.due_at).to eq(original.due_at)&lt;br /&gt;
    expect(copied.deadline_type_id).to eq(original.deadline_type_id)&lt;br /&gt;
    expect(copied.round).to eq(original.round)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when copying to non-existent assignment' do&lt;br /&gt;
    it 'raises error' do&lt;br /&gt;
      expect do&lt;br /&gt;
        original.copy(99999)&lt;br /&gt;
      end.to raise_error(ActiveRecord::RecordNotFound)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Test Metrics ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDate model''': 60+ examples covering all methods and edge cases&lt;br /&gt;
* '''Context depth''': 2-3 levels for complex scenarios&lt;br /&gt;
* '''Code coverage''': 100% coverage of DueDate model methods&lt;br /&gt;
&lt;br /&gt;
== Implementation Summary ==&lt;br /&gt;
&lt;br /&gt;
=== Models Created/Modified ===&lt;br /&gt;
&lt;br /&gt;
* '''DeadlineType''' - Canonical deadline type definitions with semantic helper methods&lt;br /&gt;
* '''DeadlineRight''' - Permission state model (OK/Late/No) with comparison methods&lt;br /&gt;
* '''DueDate''' - Refactored with instance methods, scopes, and permission checking&lt;br /&gt;
* '''TopicDueDate''' - STI subclass for topic-specific deadlines&lt;br /&gt;
* '''Assignment''' - Includes DueDateActions mixin&lt;br /&gt;
* '''SignUpTopic''' - Includes DueDateActions mixin&lt;br /&gt;
&lt;br /&gt;
=== Modules Created ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions''' - Permission checking combining deadline and role constraints&lt;br /&gt;
* '''DueDateActions''' - Deadline-related operations for parent models (Assignment, SignUpTopic)&lt;br /&gt;
&lt;br /&gt;
=== Key Improvements ===&lt;br /&gt;
&lt;br /&gt;
# '''Separation of Concerns''': Permission logic separated into dedicated modules&lt;br /&gt;
# '''Instance Methods''': Replaced class methods with testable instance methods&lt;br /&gt;
# '''Semantic Helpers''': Methods like &amp;lt;code&amp;gt;submission?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;allows_action?&amp;lt;/code&amp;gt; improve code readability&lt;br /&gt;
# '''Polymorphic Design''': Single DueDate model serves both Assignment and SignUpTopic&lt;br /&gt;
# '''Unified Permissions''': Role-based and time-based checks combined in one interface&lt;br /&gt;
# '''Data Integrity''': Removed duplicate deadline types, enforced foreign key constraints&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
=== Demo Link ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add deadline notification system using the new permission framework&lt;br /&gt;
* Implement deadline templates for common assignment patterns&lt;br /&gt;
* Add API endpoints for mobile app integration&lt;br /&gt;
* Create admin dashboard for monitoring deadline compliance across courses&lt;br /&gt;
* Add automated deadline extension workflows based on configurable rules&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167177</id>
		<title>CSC/ECE 517 Fall 2025 - E2566. Finish DueDates</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167177"/>
		<updated>2025-12-02T00:11:51Z</updated>

		<summary type="html">&lt;p&gt;Skim253: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
This project aims to complete the implementation of the DueDate system in Expertiza. The current implementation consists mostly of class methods and lacks proper definition of different deadline types and permission checking functionality. This redesign will create a more robust, readable, and maintainable due date system.&lt;br /&gt;
&lt;br /&gt;
=== Existing Components ===&lt;br /&gt;
* &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model with polymorphic parent association (Assignment/SignUpTopic)&lt;br /&gt;
* &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; subclasses&lt;br /&gt;
* Basic CRUD operations (within &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AssignmentForm&amp;lt;/code&amp;gt;) and sorting functionality (&amp;lt;code&amp;gt;deadline_sort&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Database schema with &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt; field&lt;br /&gt;
&lt;br /&gt;
=== Current Behavior ===&lt;br /&gt;
&lt;br /&gt;
====Admin's View====&lt;br /&gt;
[[File:Duedates.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* When editing an assignment, due dates can be modified under the Due Dates tab.&lt;br /&gt;
* Each row contains the following columns: &amp;lt;code&amp;gt;Date &amp;amp; Time&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Use Date Updater&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Submission Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Teammate Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Meta-Review Allowed&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Reminder&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:Assignments-CurrentStage.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* In the Assignments table, the ''DueDate'' of each assignment is displayed as Current Stage and Stage Deadline.&lt;br /&gt;
&lt;br /&gt;
====Student's View====&lt;br /&gt;
[[File:Student-assignments.png|600px]]&lt;br /&gt;
* Students can view the due date information under &amp;lt;code&amp;gt;Current State&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Stage Deadline&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
* The actions available to students are determined by the current DueDate status.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-not-over.png|600px]]&lt;br /&gt;
* For example, during the submission period, students can access the &amp;lt;code&amp;gt;Submit a hyperlink&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Submit a file&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-over.png|600px]]&lt;br /&gt;
* Once the due date has passed, students cannot submit either a hyperlink or a file.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Currently, there are some glaring issues with how due_dates was created in the first place, including redundancy and lack of modularity.&lt;br /&gt;
&lt;br /&gt;
# No dedicated &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to define different kinds of deadlines. The existing &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; class only serves as an association for &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; models, and is never referenced in the current &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; implementation.&lt;br /&gt;
# Overuse of class methods making the code difficult to maintain.&lt;br /&gt;
# Missing permission checking logic for user actions.&lt;br /&gt;
# Incomplete deadline type definitions — &amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt; need to be added.&lt;br /&gt;
# Duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table, which may lead to potential bugs.&lt;br /&gt;
# No clear separation of concerns — data persistence, business logic, and permission logic are mixed in a single model.&lt;br /&gt;
&lt;br /&gt;
=== Tasks Identified ===&lt;br /&gt;
&lt;br /&gt;
# Redesign the &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.&lt;br /&gt;
# Refactor the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model to separate persistence, business logic, and permission logic into distinct layers.&lt;br /&gt;
# Introduce instance-level methods (e.g., &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;) to replace class-level utilities and improve testability.&lt;br /&gt;
# Integrate role-based and time-based permission checks within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; to ensure consistent access control.&lt;br /&gt;
# Add missing deadline types (&amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt;) and remove duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries for data integrity.&lt;br /&gt;
# Implement reusable mixins (e.g., &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;) to handle permission evaluation and deadline-related operations.&lt;br /&gt;
# Update controllers and APIs to use the new mixin-based approach for consistent permission checks and deadline queries.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Proposed Solution ==&lt;br /&gt;
&lt;br /&gt;
=== 1. DeadlineType Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Although the current codebase includes a &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model, it only serves as a passive lookup table for associations with &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt;.  &lt;br /&gt;
In practice, most parts of the system still rely on hard-coded numeric IDs or manual lookups such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Example of current usage&lt;br /&gt;
deadline_type_id = DeadlineType.find_by(name: 'review').id&lt;br /&gt;
if due_date.deadline_type_id == deadline_type_id&lt;br /&gt;
  # Perform review-related logic&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of using the model as an active domain object, &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; directly compares IDs or string literals.  &lt;br /&gt;
This leads to several structural issues:&lt;br /&gt;
&lt;br /&gt;
* Inconsistent references (some use IDs, others strings)&lt;br /&gt;
* Tight coupling between &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
* Lack of helper semantics (e.g., &amp;lt;code&amp;gt;is_review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;is_submission?&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Data duplication and integrity risks (two &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; rows)&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; will serve as a source of truth, defining all valid deadline types and providing meaningful interfaces.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE deadline_types (&lt;br /&gt;
  id BIGINT PRIMARY KEY,&lt;br /&gt;
  name VARCHAR(255) NOT NULL UNIQUE,&lt;br /&gt;
  description TEXT NOT NULL,&lt;br /&gt;
  created_at TIMESTAMP,&lt;br /&gt;
  updated_at TIMESTAMP&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Deadline Type Definitions ====&lt;br /&gt;
* &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;: submission - Student work submission deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt;: review - Peer review deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;3&amp;lt;/code&amp;gt;: teammate_review - Team member evaluation deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;5&amp;lt;/code&amp;gt;: metareview - Meta-review deadlines (kept for backward compatibility)  &lt;br /&gt;
* &amp;lt;code&amp;gt;6&amp;lt;/code&amp;gt;: drop_topic - Topic drop deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;7&amp;lt;/code&amp;gt;: signup - Course/assignment signup deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;8&amp;lt;/code&amp;gt;: team_formation - Team formation deadlines (clean up duplicate ID 10)  &lt;br /&gt;
* &amp;lt;code&amp;gt;11&amp;lt;/code&amp;gt;: quiz - Quiz completion deadlines&lt;br /&gt;
&lt;br /&gt;
==== DeadlineType Model Implementation ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineType &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
  validates :description, presence: true&lt;br /&gt;
&lt;br /&gt;
  has_many :due_dates, foreign_key: :deadline_type_id, dependent: :restrict_with_exception&lt;br /&gt;
&lt;br /&gt;
  # Semantic helper methods for deadline type identification&lt;br /&gt;
  def submission?&lt;br /&gt;
    name == 'submission'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def review?&lt;br /&gt;
    name == 'review'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def teammate_review?&lt;br /&gt;
    name == 'teammate_review'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def quiz?&lt;br /&gt;
    name == 'quiz'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def team_formation?&lt;br /&gt;
    name == 'team_formation'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def signup?&lt;br /&gt;
    name == 'signup'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def drop_topic?&lt;br /&gt;
    name == 'drop_topic'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Display methods&lt;br /&gt;
  def display_name&lt;br /&gt;
    name.humanize&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def to_s&lt;br /&gt;
    display_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 2. DeadlineRight Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model represents the permission level for a specific action at a given deadline.&lt;br /&gt;
It defines three states: &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt; (action allowed), &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt; (action allowed with penalty), and &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt; (action denied).&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE deadline_rights (&lt;br /&gt;
  id BIGINT PRIMARY KEY,&lt;br /&gt;
  name VARCHAR(255) NOT NULL UNIQUE,&lt;br /&gt;
  created_at TIMESTAMP,&lt;br /&gt;
  updated_at TIMESTAMP&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== DeadlineRight Model ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineRight &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
&lt;br /&gt;
  # Permission checking methods&lt;br /&gt;
  def allows_action?&lt;br /&gt;
    %w[OK Late].include?(name)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def denies_action?&lt;br /&gt;
    name == 'No'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def allows_with_penalty?&lt;br /&gt;
    name == 'Late'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def allows_without_penalty?&lt;br /&gt;
    name == 'OK'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Display methods&lt;br /&gt;
  def to_s&lt;br /&gt;
    name&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def permission_level&lt;br /&gt;
    case name&lt;br /&gt;
    when 'No'   then 0&lt;br /&gt;
    when 'Late' then 1&lt;br /&gt;
    when 'OK'   then 2&lt;br /&gt;
    else -1&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 3. DueDate Model Refactoring ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model currently mixes persistence logic, deadline calculation, and permission checking, violating SRP.  &lt;br /&gt;
Most of its logic is implemented as class methods, which makes testing and extension difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Current design&lt;br /&gt;
def self.copy(old_assignment_id, new_assignment_id)&lt;br /&gt;
  duedates = where(parent_id: old_assignment_id)&lt;br /&gt;
  duedates.each do |orig_due_date|&lt;br /&gt;
    new_due_date = orig_due_date.dup&lt;br /&gt;
    new_due_date.parent_id = new_assignment_id&lt;br /&gt;
    new_due_date.save&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Improved Design ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
&lt;br /&gt;
  belongs_to :parent, polymorphic: true&lt;br /&gt;
  belongs_to :deadline_type, foreign_key: :deadline_type_id&lt;br /&gt;
&lt;br /&gt;
  validates :due_at, presence: true&lt;br /&gt;
  validates :deadline_type_id, presence: true&lt;br /&gt;
  validates :parent, presence: true&lt;br /&gt;
  validates :round, numericality: { greater_than: 0 }, allow_nil: true&lt;br /&gt;
&lt;br /&gt;
  # Scopes for common queries&lt;br /&gt;
  scope :upcoming, -&amp;gt; { where('due_at &amp;gt; ?', Time.current).order(:due_at) }&lt;br /&gt;
  scope :overdue, -&amp;gt; { where('due_at &amp;lt; ?', Time.current).order(:due_at) }&lt;br /&gt;
  scope :for_round, -&amp;gt;(round_num) { where(round: round_num) }&lt;br /&gt;
  scope :for_deadline_type, -&amp;gt;(type_name) { joins(:deadline_type).where(deadline_types: { name: type_name }) }&lt;br /&gt;
&lt;br /&gt;
  # Instance methods replace class methods&lt;br /&gt;
  def copy(to_assignment_id)&lt;br /&gt;
    to_assignment = Assignment.find(to_assignment_id)&lt;br /&gt;
    new_due_date = dup&lt;br /&gt;
    new_due_date.parent = to_assignment&lt;br /&gt;
    new_due_date.save!&lt;br /&gt;
    new_due_date&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def set(deadline_type_id, parent_id, round)&lt;br /&gt;
    self.deadline_type_id = deadline_type_id&lt;br /&gt;
    self.parent_id = parent_id&lt;br /&gt;
    self.round = round&lt;br /&gt;
    save!&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if this deadline has passed&lt;br /&gt;
  def overdue?&lt;br /&gt;
    due_at &amp;lt; Time.current&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if this deadline is upcoming&lt;br /&gt;
  def upcoming?&lt;br /&gt;
    due_at &amp;gt; Time.current&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the deadline type name&lt;br /&gt;
  def deadline_type_name&lt;br /&gt;
    deadline_type&amp;amp;.name&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Comparison method for sorting&lt;br /&gt;
  def &amp;lt;=&amp;gt;(other)&lt;br /&gt;
    return nil unless other.is_a?(DueDate)&lt;br /&gt;
    due_at &amp;lt;=&amp;gt; other.due_at&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Class methods for collection operations&lt;br /&gt;
  class &amp;lt;&amp;lt; self&lt;br /&gt;
    def sort_due_dates(due_dates)&lt;br /&gt;
      due_dates.sort_by(&amp;amp;:due_at)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    def any_future_due_dates?(due_dates)&lt;br /&gt;
      due_dates.any?(&amp;amp;:upcoming?)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    def copy(from_assignment_id, to_assignment_id)&lt;br /&gt;
      from_assignment = Assignment.find(from_assignment_id)&lt;br /&gt;
      to_assignment = Assignment.find(to_assignment_id)&lt;br /&gt;
&lt;br /&gt;
      from_assignment.due_dates.each do |due_date|&lt;br /&gt;
        new_due_date = due_date.dup&lt;br /&gt;
        new_due_date.parent = to_assignment&lt;br /&gt;
        new_due_date.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  before_save :set_default_round&lt;br /&gt;
&lt;br /&gt;
  def set_default_round&lt;br /&gt;
    self.round ||= 1&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 4. Permission-Checking Methods ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Currently, permission checks depend only on user roles and ignore the actual deadline.  &lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Role-based permissions (participant_helpers.rb)&lt;br /&gt;
def participant_permissions(authorization)&lt;br /&gt;
  case authorization&lt;br /&gt;
  when 'reader'   then { can_submit: false, can_review: true, can_take_quiz: true }&lt;br /&gt;
  when 'reviewer' then { can_submit: false, can_review: true, can_take_quiz: false }&lt;br /&gt;
  when 'submitter' then { can_submit: true, can_review: false, can_take_quiz: false }&lt;br /&gt;
  else { can_submit: true, can_review: true, can_take_quiz: true }&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And deadline-based checks exist separately in the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def submission_allowed(topic_id = nil)&lt;br /&gt;
  check_condition('submission_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
def can_review(topic_id = nil)&lt;br /&gt;
  check_condition('review_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These two layers (role-based and time-based) never interact, leading to inconsistencies (e.g., a user with &amp;lt;code&amp;gt;can_submit=true&amp;lt;/code&amp;gt; after deadline).&lt;br /&gt;
&lt;br /&gt;
==== Proposed Changes ====&lt;br /&gt;
&lt;br /&gt;
The new &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module integrates role-based and deadline-based checks:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDatePermissions&lt;br /&gt;
  # Permission checking methods that combine deadline-based and role-based logic&lt;br /&gt;
  #&lt;br /&gt;
  # These methods must check both:&lt;br /&gt;
  # 1. Whether the action is permitted by the current deadline (submission_allowed_id, review_allowed_id, etc.)&lt;br /&gt;
  # 2. Whether the participant has the necessary permissions (can_submit, can_review, can_take_quiz fields)&lt;br /&gt;
&lt;br /&gt;
  # Check if submission is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_submit?(participant = nil)&lt;br /&gt;
    return false unless submission_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_submit&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: submission_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if review is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_review?(participant = nil)&lt;br /&gt;
    return false unless review_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_review&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: review_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if taking a quiz is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_take_quiz?(participant = nil)&lt;br /&gt;
    return false unless quiz_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_take_quiz&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: quiz_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if teammate review is allowed&lt;br /&gt;
  def can_review_teammate?(participant = nil)&lt;br /&gt;
    return false unless teammate_review_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_review&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: teammate_review_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Generic permission checker&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    permission_field = &amp;quot;#{activity}_allowed_id&amp;quot;&lt;br /&gt;
    return false unless respond_to?(permission_field)&lt;br /&gt;
&lt;br /&gt;
    allowed_id = public_send(permission_field)&lt;br /&gt;
    return false unless allowed_id&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get permission status for an action (OK, Late, No)&lt;br /&gt;
  def permission_status_for(action)&lt;br /&gt;
    permission_field = &amp;quot;#{action}_allowed_id&amp;quot;&lt;br /&gt;
    return 'No' unless respond_to?(permission_field)&lt;br /&gt;
&lt;br /&gt;
    allowed_id = public_send(permission_field)&lt;br /&gt;
    return 'No' unless allowed_id&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name || 'No'&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This module is included in the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 5. DueDateActions Mixin ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module provides deadline-related operations for models that have due dates (Assignment, SignUpTopic).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDateActions&lt;br /&gt;
  # Generic activity permission checker that determines if an activity is permissible&lt;br /&gt;
  # based on the current deadline state for this parent object&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    current_deadline = next_due_date()&lt;br /&gt;
    return false unless current_deadline&lt;br /&gt;
&lt;br /&gt;
    current_deadline.activity_permissible?(activity)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Syntactic sugar methods for common activities&lt;br /&gt;
  def submission_permissible?&lt;br /&gt;
    activity_permissible?(:submission)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def review_permissible?&lt;br /&gt;
    activity_permissible?(:review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def teammate_review_permissible?&lt;br /&gt;
    activity_permissible?(:teammate_review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def quiz_permissible?&lt;br /&gt;
    activity_permissible?(:quiz)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def team_formation_permissible?&lt;br /&gt;
    activity_permissible?(:team_formation)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def signup_permissible?&lt;br /&gt;
    activity_permissible?(:signup)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def drop_topic_permissible?&lt;br /&gt;
    activity_permissible?(:drop_topic)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the next due date for this assignment&lt;br /&gt;
  def next_due_date(topic_id = nil)&lt;br /&gt;
    # If a topic is specified and this assignment has topic-specific deadlines,&lt;br /&gt;
    # look for topic due dates first&lt;br /&gt;
    if topic_id &amp;amp;&amp;amp; has_topic_specific_deadlines?&lt;br /&gt;
      topic_deadline = due_dates.where(parent_id: topic_id, parent_type: 'SignUpTopic')&lt;br /&gt;
                               .upcoming&lt;br /&gt;
                               .first&lt;br /&gt;
      return topic_deadline if topic_deadline&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Fall back to assignment-level due dates&lt;br /&gt;
    due_dates.upcoming.first&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the current stage name for display purposes&lt;br /&gt;
  def current_stage&lt;br /&gt;
    deadline = next_due_date()&lt;br /&gt;
    return 'finished' unless deadline&lt;br /&gt;
&lt;br /&gt;
    deadline.deadline_type&amp;amp;.name || 'unknown'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if assignment has topic-specific deadlines&lt;br /&gt;
  def has_topic_specific_deadlines?&lt;br /&gt;
    staggered_deadline || due_dates.where(parent_type: 'SignUpTopic').exists?&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Copy due dates to a new parent object&lt;br /&gt;
  def copy_due_dates_to(new_parent)&lt;br /&gt;
    due_dates.find_each do |due_date|&lt;br /&gt;
      new_due_date = due_date.dup&lt;br /&gt;
      new_due_date.parent = new_parent&lt;br /&gt;
      new_due_date.save!&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This module is included in &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class Assignment &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
class SignUpTopic &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
The test suite includes comprehensive RSpec tests for the ''DueDate'' model and its associated models (''DeadlineType'', ''DeadlineRight'').&lt;br /&gt;
These tests verify model validations, scopes, instance methods, class methods, and callbacks with extensive coverage of edge cases and error conditions.&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
The test suite follows RSpec best practices with:&lt;br /&gt;
* '''Nested contexts''' for different scenarios (e.g., &amp;quot;when parent is missing&amp;quot;, &amp;quot;when round is 0&amp;quot;)&lt;br /&gt;
* '''Multiple assertions per context''' to verify behavior from different angles&lt;br /&gt;
* '''Edge case coverage''' including empty collections, nil values, and boundary conditions&lt;br /&gt;
* '''Error case testing''' for invalid data and non-existent records&lt;br /&gt;
* '''Clear descriptive names''' using &amp;quot;context 'when...'&amp;quot; and &amp;quot;it 'does something'&amp;quot; patterns&lt;br /&gt;
&lt;br /&gt;
=== Current Coverage ===&lt;br /&gt;
&lt;br /&gt;
==== Associations ====&lt;br /&gt;
* Tests verify &amp;lt;code&amp;gt;belongs_to :parent&amp;lt;/code&amp;gt; (polymorphic) and &amp;lt;code&amp;gt;belongs_to :deadline_type&amp;lt;/code&amp;gt; relationships&lt;br /&gt;
&lt;br /&gt;
==== Validations ====&lt;br /&gt;
* '''Required field validation''': Tests for parent, due_at, and deadline_type_id presence&lt;br /&gt;
* '''Round validation''': Tests for positive values, zero rejection, negative rejection, and nil handling&lt;br /&gt;
* '''Error messages''': Verifies specific error messages are added to appropriate fields&lt;br /&gt;
* '''Valid record creation''': Confirms records persist when all requirements are met&lt;br /&gt;
&lt;br /&gt;
==== Scopes ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;''': Returns future due dates ordered by due_at, excludes past dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;''': Returns past due dates ordered by due_at, excludes future dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;''': Filters by round number, handles non-existent rounds&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt;''': Filters by deadline type name, handles non-existent types&lt;br /&gt;
&lt;br /&gt;
==== Instance Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#overdue?&amp;lt;/code&amp;gt;''': Tests past dates (true), future dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#upcoming?&amp;lt;/code&amp;gt;''': Tests future dates (true), past dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#set&amp;lt;/code&amp;gt;''': Verifies updating deadline_type_id, parent_id, and round with persistence and error handling&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#copy&amp;lt;/code&amp;gt;''': Tests duplication to new assignment, attribute preservation, error handling for non-existent targets&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#deadline_type_name&amp;lt;/code&amp;gt;''': Returns associated deadline type name, handles nil cases&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#last_deadline?&amp;lt;/code&amp;gt;''': Tests detection of final deadline, handles multiple deadlines and single deadline scenarios&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#&amp;lt;=&amp;gt;&amp;lt;/code&amp;gt;''': Tests comparison by due_at time, handles non-DueDate objects&lt;br /&gt;
&lt;br /&gt;
==== Class Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.sort_due_dates&amp;lt;/code&amp;gt;''': Sorts by due_at ascending, handles empty arrays and single elements&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.any_future_due_dates?&amp;lt;/code&amp;gt;''': Detects future dates in collections, handles empty arrays and mixed date collections&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.copy&amp;lt;/code&amp;gt;''': Copies all due dates between assignments, preserves attributes, handles empty sources and non-existent records&lt;br /&gt;
&lt;br /&gt;
==== Callbacks ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;before_save :set_default_round&amp;lt;/code&amp;gt;''': Sets round to 1 when not specified, preserves explicit values, handles nil&lt;br /&gt;
&lt;br /&gt;
=== Test Examples ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Validation with multiple assertions&lt;br /&gt;
describe 'validations' do&lt;br /&gt;
  context 'when parent is missing' do&lt;br /&gt;
    let(:due_date) do&lt;br /&gt;
      DueDate.new(&lt;br /&gt;
        parent: nil,&lt;br /&gt;
        due_at: 2.days.from_now,&lt;br /&gt;
        deadline_type: submission_type,&lt;br /&gt;
        submission_allowed_id: ok_right.id,&lt;br /&gt;
        review_allowed_id: ok_right.id&lt;br /&gt;
      )&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'is invalid' do&lt;br /&gt;
      expect(due_date).to be_invalid&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'has error on parent field' do&lt;br /&gt;
      due_date.valid?&lt;br /&gt;
      expect(due_date.errors[:parent]).to include('must exist')&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when round validation' do&lt;br /&gt;
    context 'with round value of 0' do&lt;br /&gt;
      let(:due_date) do&lt;br /&gt;
        DueDate.new(&lt;br /&gt;
          parent: assignment,&lt;br /&gt;
          due_at: 2.days.from_now,&lt;br /&gt;
          deadline_type: submission_type,&lt;br /&gt;
          submission_allowed_id: ok_right.id,&lt;br /&gt;
          review_allowed_id: ok_right.id,&lt;br /&gt;
          round: 0&lt;br /&gt;
        )&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'is invalid' do&lt;br /&gt;
        expect(due_date).to be_invalid&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'has error on round field' do&lt;br /&gt;
        due_date.valid?&lt;br /&gt;
        expect(due_date.errors[:round]).to be_present&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'with nil round' do&lt;br /&gt;
      let(:due_date) do&lt;br /&gt;
        DueDate.create(&lt;br /&gt;
          parent: assignment,&lt;br /&gt;
          due_at: 2.days.from_now,&lt;br /&gt;
          deadline_type: submission_type,&lt;br /&gt;
          submission_allowed_id: ok_right.id,&lt;br /&gt;
          review_allowed_id: ok_right.id,&lt;br /&gt;
          round: nil&lt;br /&gt;
        )&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'is valid' do&lt;br /&gt;
        expect(due_date).to be_valid&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'sets default round to 1' do&lt;br /&gt;
        expect(due_date.round).to eq(1)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
# Scope with edge cases&lt;br /&gt;
describe '.upcoming' do&lt;br /&gt;
  it 'returns only future due dates' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).to contain_exactly(upcoming_due_date1, upcoming_due_date2)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'orders results by due_at ascending' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).to eq([upcoming_due_date1, upcoming_due_date2])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'excludes past due dates' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).not_to include(past_due_date)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when all due dates are in the past' do&lt;br /&gt;
    before do&lt;br /&gt;
      DueDate.destroy_all&lt;br /&gt;
      DueDate.create(&lt;br /&gt;
        parent: assignment,&lt;br /&gt;
        due_at: 3.days.ago,&lt;br /&gt;
        deadline_type: submission_type,&lt;br /&gt;
        submission_allowed_id: ok_right.id,&lt;br /&gt;
        review_allowed_id: ok_right.id&lt;br /&gt;
      )&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns empty collection' do&lt;br /&gt;
      expect(DueDate.upcoming).to be_empty&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
# Instance method with error handling&lt;br /&gt;
describe '#copy' do&lt;br /&gt;
  let(:original) do&lt;br /&gt;
    DueDate.create(&lt;br /&gt;
      parent: assignment,&lt;br /&gt;
      due_at: 2.days.from_now,&lt;br /&gt;
      deadline_type: submission_type,&lt;br /&gt;
      submission_allowed_id: ok_right.id,&lt;br /&gt;
      review_allowed_id: late_right.id,&lt;br /&gt;
      round: 1&lt;br /&gt;
    )&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  let(:copied) { original.copy(assignment2.id) }&lt;br /&gt;
&lt;br /&gt;
  it 'creates a new persisted record' do&lt;br /&gt;
    expect(copied).to be_persisted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'has different id from original' do&lt;br /&gt;
    expect(copied.id).not_to eq(original.id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'preserves all attributes' do&lt;br /&gt;
    expect(copied.due_at).to eq(original.due_at)&lt;br /&gt;
    expect(copied.deadline_type_id).to eq(original.deadline_type_id)&lt;br /&gt;
    expect(copied.round).to eq(original.round)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when copying to non-existent assignment' do&lt;br /&gt;
    it 'raises error' do&lt;br /&gt;
      expect do&lt;br /&gt;
        original.copy(99999)&lt;br /&gt;
      end.to raise_error(ActiveRecord::RecordNotFound)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Test Metrics ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDate model''': 60+ examples covering all methods and edge cases&lt;br /&gt;
* '''Context depth''': 2-3 levels for complex scenarios&lt;br /&gt;
* '''Code coverage''': 100% coverage of DueDate model methods&lt;br /&gt;
&lt;br /&gt;
== Implementation Summary ==&lt;br /&gt;
&lt;br /&gt;
=== Models Created/Modified ===&lt;br /&gt;
&lt;br /&gt;
* '''DeadlineType''' - Canonical deadline type definitions with semantic helper methods&lt;br /&gt;
* '''DeadlineRight''' - Permission state model (OK/Late/No) with comparison methods&lt;br /&gt;
* '''DueDate''' - Refactored with instance methods, scopes, and permission checking&lt;br /&gt;
* '''TopicDueDate''' - STI subclass for topic-specific deadlines&lt;br /&gt;
* '''Assignment''' - Includes DueDateActions mixin&lt;br /&gt;
* '''SignUpTopic''' - Includes DueDateActions mixin&lt;br /&gt;
&lt;br /&gt;
=== Modules Created ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions''' - Permission checking combining deadline and role constraints&lt;br /&gt;
* '''DueDateActions''' - Deadline-related operations for parent models (Assignment, SignUpTopic)&lt;br /&gt;
&lt;br /&gt;
=== Key Improvements ===&lt;br /&gt;
&lt;br /&gt;
# '''Separation of Concerns''': Permission logic separated into dedicated modules&lt;br /&gt;
# '''Instance Methods''': Replaced class methods with testable instance methods&lt;br /&gt;
# '''Semantic Helpers''': Methods like &amp;lt;code&amp;gt;submission?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;allows_action?&amp;lt;/code&amp;gt; improve code readability&lt;br /&gt;
# '''Polymorphic Design''': Single DueDate model serves both Assignment and SignUpTopic&lt;br /&gt;
# '''Unified Permissions''': Role-based and time-based checks combined in one interface&lt;br /&gt;
# '''Data Integrity''': Removed duplicate deadline types, enforced foreign key constraints&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
=== Demo Link ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add deadline notification system using the new permission framework&lt;br /&gt;
* Implement deadline templates for common assignment patterns&lt;br /&gt;
* Add API endpoints for mobile app integration&lt;br /&gt;
* Create admin dashboard for monitoring deadline compliance across courses&lt;br /&gt;
* Add automated deadline extension workflows based on configurable rules&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167176</id>
		<title>CSC/ECE 517 Fall 2025 - E2566. Finish DueDates</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167176"/>
		<updated>2025-12-02T00:11:03Z</updated>

		<summary type="html">&lt;p&gt;Skim253: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
This project aims to complete the implementation of the DueDate system in Expertiza. The current implementation consists mostly of class methods and lacks proper definition of different deadline types and permission checking functionality. This redesign will create a more robust, readable, and maintainable due date system.&lt;br /&gt;
&lt;br /&gt;
=== Existing Components ===&lt;br /&gt;
* &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model with polymorphic parent association (Assignment/SignUpTopic)&lt;br /&gt;
* &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; subclasses&lt;br /&gt;
* Basic CRUD operations (within &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AssignmentForm&amp;lt;/code&amp;gt;) and sorting functionality (&amp;lt;code&amp;gt;deadline_sort&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Database schema with &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt; field&lt;br /&gt;
&lt;br /&gt;
=== Current Behavior ===&lt;br /&gt;
&lt;br /&gt;
====Admin's View====&lt;br /&gt;
[[File:Duedates.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* When editing an assignment, due dates can be modified under the Due Dates tab.&lt;br /&gt;
* Each row contains the following columns: &amp;lt;code&amp;gt;Date &amp;amp; Time&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Use Date Updater&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Submission Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Teammate Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Meta-Review Allowed&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Reminder&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:Assignments-CurrentStage.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* In the Assignments table, the ''DueDate'' of each assignment is displayed as Current Stage and Stage Deadline.&lt;br /&gt;
&lt;br /&gt;
====Student's View====&lt;br /&gt;
[[File:Student-assignments.png|600px]]&lt;br /&gt;
* Students can view the due date information under &amp;lt;code&amp;gt;Current State&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Stage Deadline&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
* The actions available to students are determined by the current DueDate status.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-not-over.png|600px]]&lt;br /&gt;
* For example, during the submission period, students can access the &amp;lt;code&amp;gt;Submit a hyperlink&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Submit a file&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-over.png|600px]]&lt;br /&gt;
* Once the due date has passed, students cannot submit either a hyperlink or a file.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Currently, there are some glaring issues with how due_dates was created in the first place, including redundancy and lack of modularity.&lt;br /&gt;
&lt;br /&gt;
# No dedicated &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to define different kinds of deadlines. The existing &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; class only serves as an association for &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; models, and is never referenced in the current &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; implementation.&lt;br /&gt;
# Overuse of class methods making the code difficult to maintain.&lt;br /&gt;
# Missing permission checking logic for user actions.&lt;br /&gt;
# Incomplete deadline type definitions — &amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt; need to be added.&lt;br /&gt;
# Duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table, which may lead to potential bugs.&lt;br /&gt;
# No clear separation of concerns — data persistence, business logic, and permission logic are mixed in a single model.&lt;br /&gt;
&lt;br /&gt;
=== Tasks Identified ===&lt;br /&gt;
&lt;br /&gt;
# Redesign the &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.&lt;br /&gt;
# Refactor the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model to separate persistence, business logic, and permission logic into distinct layers.&lt;br /&gt;
# Introduce instance-level methods (e.g., &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;) to replace class-level utilities and improve testability.&lt;br /&gt;
# Integrate role-based and time-based permission checks within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; to ensure consistent access control.&lt;br /&gt;
# Add missing deadline types (&amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt;) and remove duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries for data integrity.&lt;br /&gt;
# Implement reusable mixins (e.g., &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;) to handle permission evaluation and deadline-related operations.&lt;br /&gt;
# Update controllers and APIs to use the new mixin-based approach for consistent permission checks and deadline queries.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Proposed Solution ==&lt;br /&gt;
&lt;br /&gt;
=== 1. DeadlineType Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Although the current codebase includes a &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model, it only serves as a passive lookup table for associations with &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt;.  &lt;br /&gt;
In practice, most parts of the system still rely on hard-coded numeric IDs or manual lookups such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Example of current usage&lt;br /&gt;
deadline_type_id = DeadlineType.find_by(name: 'review').id&lt;br /&gt;
if due_date.deadline_type_id == deadline_type_id&lt;br /&gt;
  # Perform review-related logic&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of using the model as an active domain object, &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; directly compares IDs or string literals.  &lt;br /&gt;
This leads to several structural issues:&lt;br /&gt;
&lt;br /&gt;
* Inconsistent references (some use IDs, others strings)&lt;br /&gt;
* Tight coupling between &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
* Lack of helper semantics (e.g., &amp;lt;code&amp;gt;is_review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;is_submission?&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Data duplication and integrity risks (two &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; rows)&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; will serve as a source of truth, defining all valid deadline types and providing meaningful interfaces.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE deadline_types (&lt;br /&gt;
  id BIGINT PRIMARY KEY,&lt;br /&gt;
  name VARCHAR(255) NOT NULL UNIQUE,&lt;br /&gt;
  description TEXT NOT NULL,&lt;br /&gt;
  created_at TIMESTAMP,&lt;br /&gt;
  updated_at TIMESTAMP&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Deadline Type Definitions ====&lt;br /&gt;
* &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;: submission - Student work submission deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt;: review - Peer review deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;3&amp;lt;/code&amp;gt;: teammate_review - Team member evaluation deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;5&amp;lt;/code&amp;gt;: metareview - Meta-review deadlines (kept for backward compatibility)  &lt;br /&gt;
* &amp;lt;code&amp;gt;6&amp;lt;/code&amp;gt;: drop_topic - Topic drop deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;7&amp;lt;/code&amp;gt;: signup - Course/assignment signup deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;8&amp;lt;/code&amp;gt;: team_formation - Team formation deadlines (clean up duplicate ID 10)  &lt;br /&gt;
* &amp;lt;code&amp;gt;11&amp;lt;/code&amp;gt;: quiz - Quiz completion deadlines&lt;br /&gt;
&lt;br /&gt;
==== DeadlineType Model Implementation ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineType &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
  validates :description, presence: true&lt;br /&gt;
&lt;br /&gt;
  has_many :due_dates, foreign_key: :deadline_type_id, dependent: :restrict_with_exception&lt;br /&gt;
&lt;br /&gt;
  # Semantic helper methods for deadline type identification&lt;br /&gt;
  def submission?&lt;br /&gt;
    name == 'submission'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def review?&lt;br /&gt;
    name == 'review'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def teammate_review?&lt;br /&gt;
    name == 'teammate_review'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def quiz?&lt;br /&gt;
    name == 'quiz'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def team_formation?&lt;br /&gt;
    name == 'team_formation'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def signup?&lt;br /&gt;
    name == 'signup'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def drop_topic?&lt;br /&gt;
    name == 'drop_topic'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Display methods&lt;br /&gt;
  def display_name&lt;br /&gt;
    name.humanize&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def to_s&lt;br /&gt;
    display_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 2. DeadlineRight Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model represents the permission level for a specific action at a given deadline.&lt;br /&gt;
It defines three states: &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt; (action allowed), &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt; (action allowed with penalty), and &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt; (action denied).&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE deadline_rights (&lt;br /&gt;
  id BIGINT PRIMARY KEY,&lt;br /&gt;
  name VARCHAR(255) NOT NULL UNIQUE,&lt;br /&gt;
  created_at TIMESTAMP,&lt;br /&gt;
  updated_at TIMESTAMP&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== DeadlineRight Model ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineRight &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
&lt;br /&gt;
  # Permission checking methods&lt;br /&gt;
  def allows_action?&lt;br /&gt;
    %w[OK Late].include?(name)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def denies_action?&lt;br /&gt;
    name == 'No'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def allows_with_penalty?&lt;br /&gt;
    name == 'Late'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def allows_without_penalty?&lt;br /&gt;
    name == 'OK'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Display methods&lt;br /&gt;
  def to_s&lt;br /&gt;
    name&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def permission_level&lt;br /&gt;
    case name&lt;br /&gt;
    when 'No'   then 0&lt;br /&gt;
    when 'Late' then 1&lt;br /&gt;
    when 'OK'   then 2&lt;br /&gt;
    else -1&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 3. DueDate Model Refactoring ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model currently mixes persistence logic, deadline calculation, and permission checking, violating SRP.  &lt;br /&gt;
Most of its logic is implemented as class methods, which makes testing and extension difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Current design&lt;br /&gt;
def self.copy(old_assignment_id, new_assignment_id)&lt;br /&gt;
  duedates = where(parent_id: old_assignment_id)&lt;br /&gt;
  duedates.each do |orig_due_date|&lt;br /&gt;
    new_due_date = orig_due_date.dup&lt;br /&gt;
    new_due_date.parent_id = new_assignment_id&lt;br /&gt;
    new_due_date.save&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Improved Design ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
&lt;br /&gt;
  belongs_to :parent, polymorphic: true&lt;br /&gt;
  belongs_to :deadline_type, foreign_key: :deadline_type_id&lt;br /&gt;
&lt;br /&gt;
  validates :due_at, presence: true&lt;br /&gt;
  validates :deadline_type_id, presence: true&lt;br /&gt;
  validates :parent, presence: true&lt;br /&gt;
  validates :round, numericality: { greater_than: 0 }, allow_nil: true&lt;br /&gt;
&lt;br /&gt;
  # Scopes for common queries&lt;br /&gt;
  scope :upcoming, -&amp;gt; { where('due_at &amp;gt; ?', Time.current).order(:due_at) }&lt;br /&gt;
  scope :overdue, -&amp;gt; { where('due_at &amp;lt; ?', Time.current).order(:due_at) }&lt;br /&gt;
  scope :for_round, -&amp;gt;(round_num) { where(round: round_num) }&lt;br /&gt;
  scope :for_deadline_type, -&amp;gt;(type_name) { joins(:deadline_type).where(deadline_types: { name: type_name }) }&lt;br /&gt;
&lt;br /&gt;
  # Instance methods replace class methods&lt;br /&gt;
  def copy(to_assignment_id)&lt;br /&gt;
    to_assignment = Assignment.find(to_assignment_id)&lt;br /&gt;
    new_due_date = dup&lt;br /&gt;
    new_due_date.parent = to_assignment&lt;br /&gt;
    new_due_date.save!&lt;br /&gt;
    new_due_date&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def set(deadline_type_id, parent_id, round)&lt;br /&gt;
    self.deadline_type_id = deadline_type_id&lt;br /&gt;
    self.parent_id = parent_id&lt;br /&gt;
    self.round = round&lt;br /&gt;
    save!&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if this deadline has passed&lt;br /&gt;
  def overdue?&lt;br /&gt;
    due_at &amp;lt; Time.current&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if this deadline is upcoming&lt;br /&gt;
  def upcoming?&lt;br /&gt;
    due_at &amp;gt; Time.current&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the deadline type name&lt;br /&gt;
  def deadline_type_name&lt;br /&gt;
    deadline_type&amp;amp;.name&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Comparison method for sorting&lt;br /&gt;
  def &amp;lt;=&amp;gt;(other)&lt;br /&gt;
    return nil unless other.is_a?(DueDate)&lt;br /&gt;
    due_at &amp;lt;=&amp;gt; other.due_at&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Class methods for collection operations&lt;br /&gt;
  class &amp;lt;&amp;lt; self&lt;br /&gt;
    def sort_due_dates(due_dates)&lt;br /&gt;
      due_dates.sort_by(&amp;amp;:due_at)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    def any_future_due_dates?(due_dates)&lt;br /&gt;
      due_dates.any?(&amp;amp;:upcoming?)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    def copy(from_assignment_id, to_assignment_id)&lt;br /&gt;
      from_assignment = Assignment.find(from_assignment_id)&lt;br /&gt;
      to_assignment = Assignment.find(to_assignment_id)&lt;br /&gt;
&lt;br /&gt;
      from_assignment.due_dates.each do |due_date|&lt;br /&gt;
        new_due_date = due_date.dup&lt;br /&gt;
        new_due_date.parent = to_assignment&lt;br /&gt;
        new_due_date.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  before_save :set_default_round&lt;br /&gt;
&lt;br /&gt;
  def set_default_round&lt;br /&gt;
    self.round ||= 1&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 4. Permission-Checking Methods ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Currently, permission checks depend only on user roles and ignore the actual deadline.  &lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Role-based permissions (participant_helpers.rb)&lt;br /&gt;
def participant_permissions(authorization)&lt;br /&gt;
  case authorization&lt;br /&gt;
  when 'reader'   then { can_submit: false, can_review: true, can_take_quiz: true }&lt;br /&gt;
  when 'reviewer' then { can_submit: false, can_review: true, can_take_quiz: false }&lt;br /&gt;
  when 'submitter' then { can_submit: true, can_review: false, can_take_quiz: false }&lt;br /&gt;
  else { can_submit: true, can_review: true, can_take_quiz: true }&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And deadline-based checks exist separately in the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def submission_allowed(topic_id = nil)&lt;br /&gt;
  check_condition('submission_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
def can_review(topic_id = nil)&lt;br /&gt;
  check_condition('review_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These two layers (role-based and time-based) never interact, leading to inconsistencies (e.g., a user with &amp;lt;code&amp;gt;can_submit=true&amp;lt;/code&amp;gt; after deadline).&lt;br /&gt;
&lt;br /&gt;
==== Proposed Changes ====&lt;br /&gt;
&lt;br /&gt;
The new &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module integrates role-based and deadline-based checks:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDatePermissions&lt;br /&gt;
  # Permission checking methods that combine deadline-based and role-based logic&lt;br /&gt;
  #&lt;br /&gt;
  # These methods must check both:&lt;br /&gt;
  # 1. Whether the action is permitted by the current deadline (submission_allowed_id, review_allowed_id, etc.)&lt;br /&gt;
  # 2. Whether the participant has the necessary permissions (can_submit, can_review, can_take_quiz fields)&lt;br /&gt;
&lt;br /&gt;
  # Check if submission is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_submit?(participant = nil)&lt;br /&gt;
    return false unless submission_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_submit&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: submission_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if review is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_review?(participant = nil)&lt;br /&gt;
    return false unless review_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_review&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: review_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if taking a quiz is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_take_quiz?(participant = nil)&lt;br /&gt;
    return false unless quiz_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_take_quiz&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: quiz_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if teammate review is allowed&lt;br /&gt;
  def can_review_teammate?(participant = nil)&lt;br /&gt;
    return false unless teammate_review_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_review&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: teammate_review_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Generic permission checker&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    permission_field = &amp;quot;#{activity}_allowed_id&amp;quot;&lt;br /&gt;
    return false unless respond_to?(permission_field)&lt;br /&gt;
&lt;br /&gt;
    allowed_id = public_send(permission_field)&lt;br /&gt;
    return false unless allowed_id&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get permission status for an action (OK, Late, No)&lt;br /&gt;
  def permission_status_for(action)&lt;br /&gt;
    permission_field = &amp;quot;#{action}_allowed_id&amp;quot;&lt;br /&gt;
    return 'No' unless respond_to?(permission_field)&lt;br /&gt;
&lt;br /&gt;
    allowed_id = public_send(permission_field)&lt;br /&gt;
    return 'No' unless allowed_id&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name || 'No'&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This module is included in the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 5. DueDateActions Mixin ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module provides deadline-related operations for models that have due dates (Assignment, SignUpTopic).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDateActions&lt;br /&gt;
  # Generic activity permission checker that determines if an activity is permissible&lt;br /&gt;
  # based on the current deadline state for this parent object&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    current_deadline = next_due_date()&lt;br /&gt;
    return false unless current_deadline&lt;br /&gt;
&lt;br /&gt;
    current_deadline.activity_permissible?(activity)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Syntactic sugar methods for common activities&lt;br /&gt;
  def submission_permissible?&lt;br /&gt;
    activity_permissible?(:submission)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def review_permissible?&lt;br /&gt;
    activity_permissible?(:review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def teammate_review_permissible?&lt;br /&gt;
    activity_permissible?(:teammate_review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def quiz_permissible?&lt;br /&gt;
    activity_permissible?(:quiz)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def team_formation_permissible?&lt;br /&gt;
    activity_permissible?(:team_formation)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def signup_permissible?&lt;br /&gt;
    activity_permissible?(:signup)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def drop_topic_permissible?&lt;br /&gt;
    activity_permissible?(:drop_topic)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the next due date for this assignment&lt;br /&gt;
  def next_due_date(topic_id = nil)&lt;br /&gt;
    # If a topic is specified and this assignment has topic-specific deadlines,&lt;br /&gt;
    # look for topic due dates first&lt;br /&gt;
    if topic_id &amp;amp;&amp;amp; has_topic_specific_deadlines?&lt;br /&gt;
      topic_deadline = due_dates.where(parent_id: topic_id, parent_type: 'SignUpTopic')&lt;br /&gt;
                               .upcoming&lt;br /&gt;
                               .first&lt;br /&gt;
      return topic_deadline if topic_deadline&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Fall back to assignment-level due dates&lt;br /&gt;
    due_dates.upcoming.first&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the current stage name for display purposes&lt;br /&gt;
  def current_stage&lt;br /&gt;
    deadline = next_due_date()&lt;br /&gt;
    return 'finished' unless deadline&lt;br /&gt;
&lt;br /&gt;
    deadline.deadline_type&amp;amp;.name || 'unknown'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if assignment has topic-specific deadlines&lt;br /&gt;
  def has_topic_specific_deadlines?&lt;br /&gt;
    staggered_deadline || due_dates.where(parent_type: 'SignUpTopic').exists?&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Copy due dates to a new parent object&lt;br /&gt;
  def copy_due_dates_to(new_parent)&lt;br /&gt;
    due_dates.find_each do |due_date|&lt;br /&gt;
      new_due_date = due_date.dup&lt;br /&gt;
      new_due_date.parent = new_parent&lt;br /&gt;
      new_due_date.save!&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This module is included in &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class Assignment &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
class SignUpTopic &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
The test suite includes comprehensive RSpec tests for the ''DueDate'' model and its associated models (''DeadlineType'', ''DeadlineRight'').&lt;br /&gt;
These tests verify model validations, scopes, instance methods, class methods, and callbacks with extensive coverage of edge cases and error conditions.&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
The test suite follows RSpec best practices with:&lt;br /&gt;
* '''Nested contexts''' for different scenarios (e.g., &amp;quot;when parent is missing&amp;quot;, &amp;quot;when round is 0&amp;quot;)&lt;br /&gt;
* '''Multiple assertions per context''' to verify behavior from different angles&lt;br /&gt;
* '''Edge case coverage''' including empty collections, nil values, and boundary conditions&lt;br /&gt;
* '''Error case testing''' for invalid data and non-existent records&lt;br /&gt;
* '''Clear descriptive names''' using &amp;quot;context 'when...'&amp;quot; and &amp;quot;it 'does something'&amp;quot; patterns&lt;br /&gt;
&lt;br /&gt;
=== Current Coverage ===&lt;br /&gt;
&lt;br /&gt;
==== Associations ====&lt;br /&gt;
* Tests verify &amp;lt;code&amp;gt;belongs_to :parent&amp;lt;/code&amp;gt; (polymorphic) and &amp;lt;code&amp;gt;belongs_to :deadline_type&amp;lt;/code&amp;gt; relationships&lt;br /&gt;
&lt;br /&gt;
==== Validations ====&lt;br /&gt;
* '''Required field validation''': Tests for parent, due_at, and deadline_type_id presence&lt;br /&gt;
* '''Round validation''': Tests for positive values, zero rejection, negative rejection, and nil handling&lt;br /&gt;
* '''Error messages''': Verifies specific error messages are added to appropriate fields&lt;br /&gt;
* '''Valid record creation''': Confirms records persist when all requirements are met&lt;br /&gt;
&lt;br /&gt;
==== Scopes ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;''': Returns future due dates ordered by due_at, excludes past dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;''': Returns past due dates ordered by due_at, excludes future dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;''': Filters by round number, handles non-existent rounds&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt;''': Filters by deadline type name, handles non-existent types&lt;br /&gt;
&lt;br /&gt;
==== Instance Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#overdue?&amp;lt;/code&amp;gt;''': Tests past dates (true), future dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#upcoming?&amp;lt;/code&amp;gt;''': Tests future dates (true), past dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#set&amp;lt;/code&amp;gt;''': Verifies updating deadline_type_id, parent_id, and round with persistence and error handling&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#copy&amp;lt;/code&amp;gt;''': Tests duplication to new assignment, attribute preservation, error handling for non-existent targets&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#deadline_type_name&amp;lt;/code&amp;gt;''': Returns associated deadline type name, handles nil cases&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#last_deadline?&amp;lt;/code&amp;gt;''': Tests detection of final deadline, handles multiple deadlines and single deadline scenarios&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#&amp;lt;=&amp;gt;&amp;lt;/code&amp;gt;''': Tests comparison by due_at time, handles non-DueDate objects&lt;br /&gt;
&lt;br /&gt;
==== Class Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.sort_due_dates&amp;lt;/code&amp;gt;''': Sorts by due_at ascending, handles empty arrays and single elements&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.any_future_due_dates?&amp;lt;/code&amp;gt;''': Detects future dates in collections, handles empty arrays and mixed date collections&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.copy&amp;lt;/code&amp;gt;''': Copies all due dates between assignments, preserves attributes, handles empty sources and non-existent records&lt;br /&gt;
&lt;br /&gt;
==== Callbacks ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;before_save :set_default_round&amp;lt;/code&amp;gt;''': Sets round to 1 when not specified, preserves explicit values, handles nil&lt;br /&gt;
&lt;br /&gt;
=== Test Examples ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Validation with multiple assertions&lt;br /&gt;
describe 'validations' do&lt;br /&gt;
  context 'when parent is missing' do&lt;br /&gt;
    let(:due_date) do&lt;br /&gt;
      DueDate.new(&lt;br /&gt;
        parent: nil,&lt;br /&gt;
        due_at: 2.days.from_now,&lt;br /&gt;
        deadline_type: submission_type,&lt;br /&gt;
        submission_allowed_id: ok_right.id,&lt;br /&gt;
        review_allowed_id: ok_right.id&lt;br /&gt;
      )&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'is invalid' do&lt;br /&gt;
      expect(due_date).to be_invalid&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'has error on parent field' do&lt;br /&gt;
      due_date.valid?&lt;br /&gt;
      expect(due_date.errors[:parent]).to include('must exist')&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when round validation' do&lt;br /&gt;
    context 'with round value of 0' do&lt;br /&gt;
      let(:due_date) do&lt;br /&gt;
        DueDate.new(&lt;br /&gt;
          parent: assignment,&lt;br /&gt;
          due_at: 2.days.from_now,&lt;br /&gt;
          deadline_type: submission_type,&lt;br /&gt;
          submission_allowed_id: ok_right.id,&lt;br /&gt;
          review_allowed_id: ok_right.id,&lt;br /&gt;
          round: 0&lt;br /&gt;
        )&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'is invalid' do&lt;br /&gt;
        expect(due_date).to be_invalid&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'has error on round field' do&lt;br /&gt;
        due_date.valid?&lt;br /&gt;
        expect(due_date.errors[:round]).to be_present&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'with nil round' do&lt;br /&gt;
      let(:due_date) do&lt;br /&gt;
        DueDate.create(&lt;br /&gt;
          parent: assignment,&lt;br /&gt;
          due_at: 2.days.from_now,&lt;br /&gt;
          deadline_type: submission_type,&lt;br /&gt;
          submission_allowed_id: ok_right.id,&lt;br /&gt;
          review_allowed_id: ok_right.id,&lt;br /&gt;
          round: nil&lt;br /&gt;
        )&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'is valid' do&lt;br /&gt;
        expect(due_date).to be_valid&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'sets default round to 1' do&lt;br /&gt;
        expect(due_date.round).to eq(1)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
# Scope with edge cases&lt;br /&gt;
describe '.upcoming' do&lt;br /&gt;
  it 'returns only future due dates' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).to contain_exactly(upcoming_due_date1, upcoming_due_date2)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'orders results by due_at ascending' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).to eq([upcoming_due_date1, upcoming_due_date2])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'excludes past due dates' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).not_to include(past_due_date)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when all due dates are in the past' do&lt;br /&gt;
    before do&lt;br /&gt;
      DueDate.destroy_all&lt;br /&gt;
      DueDate.create(&lt;br /&gt;
        parent: assignment,&lt;br /&gt;
        due_at: 3.days.ago,&lt;br /&gt;
        deadline_type: submission_type,&lt;br /&gt;
        submission_allowed_id: ok_right.id,&lt;br /&gt;
        review_allowed_id: ok_right.id&lt;br /&gt;
      )&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns empty collection' do&lt;br /&gt;
      expect(DueDate.upcoming).to be_empty&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
# Instance method with error handling&lt;br /&gt;
describe '#copy' do&lt;br /&gt;
  let(:original) do&lt;br /&gt;
    DueDate.create(&lt;br /&gt;
      parent: assignment,&lt;br /&gt;
      due_at: 2.days.from_now,&lt;br /&gt;
      deadline_type: submission_type,&lt;br /&gt;
      submission_allowed_id: ok_right.id,&lt;br /&gt;
      review_allowed_id: late_right.id,&lt;br /&gt;
      round: 1&lt;br /&gt;
    )&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  let(:copied) { original.copy(assignment2.id) }&lt;br /&gt;
&lt;br /&gt;
  it 'creates a new persisted record' do&lt;br /&gt;
    expect(copied).to be_persisted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'has different id from original' do&lt;br /&gt;
    expect(copied.id).not_to eq(original.id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'preserves all attributes' do&lt;br /&gt;
    expect(copied.due_at).to eq(original.due_at)&lt;br /&gt;
    expect(copied.deadline_type_id).to eq(original.deadline_type_id)&lt;br /&gt;
    expect(copied.round).to eq(original.round)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when copying to non-existent assignment' do&lt;br /&gt;
    it 'raises error' do&lt;br /&gt;
      expect do&lt;br /&gt;
        original.copy(99999)&lt;br /&gt;
      end.to raise_error(ActiveRecord::RecordNotFound)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Test Metrics ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDate model''': 60+ examples covering all methods and edge cases&lt;br /&gt;
* '''Context depth''': 2-3 levels for complex scenarios&lt;br /&gt;
* '''Code coverage''': 100% coverage of DueDate model methods&lt;br /&gt;
&lt;br /&gt;
== Implementation Summary ==&lt;br /&gt;
&lt;br /&gt;
=== Models Created/Modified ===&lt;br /&gt;
&lt;br /&gt;
* '''DeadlineType''' - Canonical deadline type definitions with semantic helper methods&lt;br /&gt;
* '''DeadlineRight''' - Permission state model (OK/Late/No) with comparison methods&lt;br /&gt;
* '''DueDate''' - Refactored with instance methods, scopes, and permission checking&lt;br /&gt;
* '''TopicDueDate''' - STI subclass for topic-specific deadlines&lt;br /&gt;
* '''Assignment''' - Includes DueDateActions mixin&lt;br /&gt;
* '''SignUpTopic''' - Includes DueDateActions mixin&lt;br /&gt;
&lt;br /&gt;
=== Modules Created ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions''' - Permission checking combining deadline and role constraints&lt;br /&gt;
* '''DueDateActions''' - Deadline-related operations for parent models (Assignment, SignUpTopic)&lt;br /&gt;
&lt;br /&gt;
=== Key Improvements ===&lt;br /&gt;
&lt;br /&gt;
# '''Separation of Concerns''': Permission logic separated into dedicated modules&lt;br /&gt;
# '''Instance Methods''': Replaced class methods with testable instance methods&lt;br /&gt;
# '''Semantic Helpers''': Methods like &amp;lt;code&amp;gt;submission?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;allows_action?&amp;lt;/code&amp;gt; improve code readability&lt;br /&gt;
# '''Polymorphic Design''': Single DueDate model serves both Assignment and SignUpTopic&lt;br /&gt;
# '''Unified Permissions''': Role-based and time-based checks combined in one interface&lt;br /&gt;
# '''Data Integrity''': Removed duplicate deadline types, enforced foreign key constraints&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
=== Demo Link ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add deadline notification system using the new permission framework&lt;br /&gt;
* Implement deadline templates for common assignment patterns&lt;br /&gt;
* Add API endpoints for mobile app integration&lt;br /&gt;
* Create admin dashboard for monitoring deadline compliance across courses&lt;br /&gt;
* Add automated deadline extension workflows based on configurable rules&lt;br /&gt;
&lt;br /&gt;
== Implementation Summary ==&lt;br /&gt;
&lt;br /&gt;
=== Models Created/Modified ===&lt;br /&gt;
&lt;br /&gt;
* '''DeadlineType''' - Canonical deadline type definitions with semantic helper methods&lt;br /&gt;
* '''DeadlineRight''' - Permission state model (OK/Late/No) with comparison methods&lt;br /&gt;
* '''DueDate''' - Refactored with instance methods, scopes, and permission checking&lt;br /&gt;
* '''TopicDueDate''' - STI subclass for topic-specific deadlines&lt;br /&gt;
* '''Assignment''' - Includes DueDateActions mixin&lt;br /&gt;
* '''SignUpTopic''' - Includes DueDateActions mixin&lt;br /&gt;
&lt;br /&gt;
=== Modules Created ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions''' - Permission checking combining deadline and role constraints&lt;br /&gt;
* '''DueDateActions''' - Deadline-related operations for parent models (Assignment, SignUpTopic)&lt;br /&gt;
&lt;br /&gt;
=== Key Improvements ===&lt;br /&gt;
&lt;br /&gt;
# '''Separation of Concerns''': Permission logic separated into dedicated modules&lt;br /&gt;
# '''Instance Methods''': Replaced class methods with testable instance methods&lt;br /&gt;
# '''Semantic Helpers''': Methods like &amp;lt;code&amp;gt;submission?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;allows_action?&amp;lt;/code&amp;gt; improve code readability&lt;br /&gt;
# '''Polymorphic Design''': Single DueDate model serves both Assignment and SignUpTopic&lt;br /&gt;
# '''Unified Permissions''': Role-based and time-based checks combined in one interface&lt;br /&gt;
# '''Data Integrity''': Removed duplicate deadline types, enforced foreign key constraints&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
=== Demo Link ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167175</id>
		<title>CSC/ECE 517 Fall 2025 - E2566. Finish DueDates</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167175"/>
		<updated>2025-12-02T00:09:11Z</updated>

		<summary type="html">&lt;p&gt;Skim253: /* Testing Plan */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
This project aims to complete the implementation of the DueDate system in Expertiza. The current implementation consists mostly of class methods and lacks proper definition of different deadline types and permission checking functionality. This redesign will create a more robust, readable, and maintainable due date system.&lt;br /&gt;
&lt;br /&gt;
=== Existing Components ===&lt;br /&gt;
* &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model with polymorphic parent association (Assignment/SignUpTopic)&lt;br /&gt;
* &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; subclasses&lt;br /&gt;
* Basic CRUD operations (within &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AssignmentForm&amp;lt;/code&amp;gt;) and sorting functionality (&amp;lt;code&amp;gt;deadline_sort&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Database schema with &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt; field&lt;br /&gt;
&lt;br /&gt;
=== Current Behavior ===&lt;br /&gt;
&lt;br /&gt;
====Admin's View====&lt;br /&gt;
[[File:Duedates.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* When editing an assignment, due dates can be modified under the Due Dates tab.&lt;br /&gt;
* Each row contains the following columns: &amp;lt;code&amp;gt;Date &amp;amp; Time&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Use Date Updater&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Submission Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Teammate Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Meta-Review Allowed&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Reminder&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:Assignments-CurrentStage.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* In the Assignments table, the ''DueDate'' of each assignment is displayed as Current Stage and Stage Deadline.&lt;br /&gt;
&lt;br /&gt;
====Student's View====&lt;br /&gt;
[[File:Student-assignments.png|600px]]&lt;br /&gt;
* Students can view the due date information under &amp;lt;code&amp;gt;Current State&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Stage Deadline&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
* The actions available to students are determined by the current DueDate status.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-not-over.png|600px]]&lt;br /&gt;
* For example, during the submission period, students can access the &amp;lt;code&amp;gt;Submit a hyperlink&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Submit a file&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-over.png|600px]]&lt;br /&gt;
* Once the due date has passed, students cannot submit either a hyperlink or a file.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Currently, there are some glaring issues with how due_dates was created in the first place, including redundancy and lack of modularity.&lt;br /&gt;
&lt;br /&gt;
# No dedicated &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to define different kinds of deadlines. The existing &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; class only serves as an association for &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; models, and is never referenced in the current &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; implementation.&lt;br /&gt;
# Overuse of class methods making the code difficult to maintain.&lt;br /&gt;
# Missing permission checking logic for user actions.&lt;br /&gt;
# Incomplete deadline type definitions — &amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt; need to be added.&lt;br /&gt;
# Duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table, which may lead to potential bugs.&lt;br /&gt;
# No clear separation of concerns — data persistence, business logic, and permission logic are mixed in a single model.&lt;br /&gt;
&lt;br /&gt;
=== Tasks Identified ===&lt;br /&gt;
&lt;br /&gt;
# Redesign the &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.&lt;br /&gt;
# Refactor the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model to separate persistence, business logic, and permission logic into distinct layers.&lt;br /&gt;
# Introduce instance-level methods (e.g., &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;) to replace class-level utilities and improve testability.&lt;br /&gt;
# Integrate role-based and time-based permission checks within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; to ensure consistent access control.&lt;br /&gt;
# Add missing deadline types (&amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt;) and remove duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries for data integrity.&lt;br /&gt;
# Implement reusable mixins (e.g., &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;) to handle permission evaluation and deadline-related operations.&lt;br /&gt;
# Update controllers and APIs to use the new mixin-based approach for consistent permission checks and deadline queries.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Proposed Solution ==&lt;br /&gt;
&lt;br /&gt;
=== 1. DeadlineType Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Although the current codebase includes a &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model, it only serves as a passive lookup table for associations with &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt;.  &lt;br /&gt;
In practice, most parts of the system still rely on hard-coded numeric IDs or manual lookups such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Example of current usage&lt;br /&gt;
deadline_type_id = DeadlineType.find_by(name: 'review').id&lt;br /&gt;
if due_date.deadline_type_id == deadline_type_id&lt;br /&gt;
  # Perform review-related logic&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of using the model as an active domain object, &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; directly compares IDs or string literals.  &lt;br /&gt;
This leads to several structural issues:&lt;br /&gt;
&lt;br /&gt;
* Inconsistent references (some use IDs, others strings)&lt;br /&gt;
* Tight coupling between &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
* Lack of helper semantics (e.g., &amp;lt;code&amp;gt;is_review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;is_submission?&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Data duplication and integrity risks (two &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; rows)&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; will serve as a source of truth, defining all valid deadline types and providing meaningful interfaces.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE deadline_types (&lt;br /&gt;
  id BIGINT PRIMARY KEY,&lt;br /&gt;
  name VARCHAR(255) NOT NULL UNIQUE,&lt;br /&gt;
  description TEXT NOT NULL,&lt;br /&gt;
  created_at TIMESTAMP,&lt;br /&gt;
  updated_at TIMESTAMP&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Deadline Type Definitions ====&lt;br /&gt;
* &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;: submission - Student work submission deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt;: review - Peer review deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;3&amp;lt;/code&amp;gt;: teammate_review - Team member evaluation deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;5&amp;lt;/code&amp;gt;: metareview - Meta-review deadlines (kept for backward compatibility)  &lt;br /&gt;
* &amp;lt;code&amp;gt;6&amp;lt;/code&amp;gt;: drop_topic - Topic drop deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;7&amp;lt;/code&amp;gt;: signup - Course/assignment signup deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;8&amp;lt;/code&amp;gt;: team_formation - Team formation deadlines (clean up duplicate ID 10)  &lt;br /&gt;
* &amp;lt;code&amp;gt;11&amp;lt;/code&amp;gt;: quiz - Quiz completion deadlines&lt;br /&gt;
&lt;br /&gt;
==== DeadlineType Model Implementation ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineType &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
  validates :description, presence: true&lt;br /&gt;
&lt;br /&gt;
  has_many :due_dates, foreign_key: :deadline_type_id, dependent: :restrict_with_exception&lt;br /&gt;
&lt;br /&gt;
  # Semantic helper methods for deadline type identification&lt;br /&gt;
  def submission?&lt;br /&gt;
    name == 'submission'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def review?&lt;br /&gt;
    name == 'review'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def teammate_review?&lt;br /&gt;
    name == 'teammate_review'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def quiz?&lt;br /&gt;
    name == 'quiz'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def team_formation?&lt;br /&gt;
    name == 'team_formation'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def signup?&lt;br /&gt;
    name == 'signup'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def drop_topic?&lt;br /&gt;
    name == 'drop_topic'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Display methods&lt;br /&gt;
  def display_name&lt;br /&gt;
    name.humanize&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def to_s&lt;br /&gt;
    display_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 2. DeadlineRight Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model represents the permission level for a specific action at a given deadline.&lt;br /&gt;
It defines three states: &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt; (action allowed), &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt; (action allowed with penalty), and &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt; (action denied).&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE deadline_rights (&lt;br /&gt;
  id BIGINT PRIMARY KEY,&lt;br /&gt;
  name VARCHAR(255) NOT NULL UNIQUE,&lt;br /&gt;
  created_at TIMESTAMP,&lt;br /&gt;
  updated_at TIMESTAMP&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== DeadlineRight Model ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineRight &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
&lt;br /&gt;
  # Permission checking methods&lt;br /&gt;
  def allows_action?&lt;br /&gt;
    %w[OK Late].include?(name)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def denies_action?&lt;br /&gt;
    name == 'No'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def allows_with_penalty?&lt;br /&gt;
    name == 'Late'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def allows_without_penalty?&lt;br /&gt;
    name == 'OK'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Display methods&lt;br /&gt;
  def to_s&lt;br /&gt;
    name&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def permission_level&lt;br /&gt;
    case name&lt;br /&gt;
    when 'No'   then 0&lt;br /&gt;
    when 'Late' then 1&lt;br /&gt;
    when 'OK'   then 2&lt;br /&gt;
    else -1&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 3. DueDate Model Refactoring ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model currently mixes persistence logic, deadline calculation, and permission checking, violating SRP.  &lt;br /&gt;
Most of its logic is implemented as class methods, which makes testing and extension difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Current design&lt;br /&gt;
def self.copy(old_assignment_id, new_assignment_id)&lt;br /&gt;
  duedates = where(parent_id: old_assignment_id)&lt;br /&gt;
  duedates.each do |orig_due_date|&lt;br /&gt;
    new_due_date = orig_due_date.dup&lt;br /&gt;
    new_due_date.parent_id = new_assignment_id&lt;br /&gt;
    new_due_date.save&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Improved Design ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
&lt;br /&gt;
  belongs_to :parent, polymorphic: true&lt;br /&gt;
  belongs_to :deadline_type, foreign_key: :deadline_type_id&lt;br /&gt;
&lt;br /&gt;
  validates :due_at, presence: true&lt;br /&gt;
  validates :deadline_type_id, presence: true&lt;br /&gt;
  validates :parent, presence: true&lt;br /&gt;
  validates :round, numericality: { greater_than: 0 }, allow_nil: true&lt;br /&gt;
&lt;br /&gt;
  # Scopes for common queries&lt;br /&gt;
  scope :upcoming, -&amp;gt; { where('due_at &amp;gt; ?', Time.current).order(:due_at) }&lt;br /&gt;
  scope :overdue, -&amp;gt; { where('due_at &amp;lt; ?', Time.current).order(:due_at) }&lt;br /&gt;
  scope :for_round, -&amp;gt;(round_num) { where(round: round_num) }&lt;br /&gt;
  scope :for_deadline_type, -&amp;gt;(type_name) { joins(:deadline_type).where(deadline_types: { name: type_name }) }&lt;br /&gt;
&lt;br /&gt;
  # Instance methods replace class methods&lt;br /&gt;
  def copy(to_assignment_id)&lt;br /&gt;
    to_assignment = Assignment.find(to_assignment_id)&lt;br /&gt;
    new_due_date = dup&lt;br /&gt;
    new_due_date.parent = to_assignment&lt;br /&gt;
    new_due_date.save!&lt;br /&gt;
    new_due_date&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def set(deadline_type_id, parent_id, round)&lt;br /&gt;
    self.deadline_type_id = deadline_type_id&lt;br /&gt;
    self.parent_id = parent_id&lt;br /&gt;
    self.round = round&lt;br /&gt;
    save!&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if this deadline has passed&lt;br /&gt;
  def overdue?&lt;br /&gt;
    due_at &amp;lt; Time.current&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if this deadline is upcoming&lt;br /&gt;
  def upcoming?&lt;br /&gt;
    due_at &amp;gt; Time.current&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the deadline type name&lt;br /&gt;
  def deadline_type_name&lt;br /&gt;
    deadline_type&amp;amp;.name&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Comparison method for sorting&lt;br /&gt;
  def &amp;lt;=&amp;gt;(other)&lt;br /&gt;
    return nil unless other.is_a?(DueDate)&lt;br /&gt;
    due_at &amp;lt;=&amp;gt; other.due_at&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Class methods for collection operations&lt;br /&gt;
  class &amp;lt;&amp;lt; self&lt;br /&gt;
    def sort_due_dates(due_dates)&lt;br /&gt;
      due_dates.sort_by(&amp;amp;:due_at)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    def any_future_due_dates?(due_dates)&lt;br /&gt;
      due_dates.any?(&amp;amp;:upcoming?)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    def copy(from_assignment_id, to_assignment_id)&lt;br /&gt;
      from_assignment = Assignment.find(from_assignment_id)&lt;br /&gt;
      to_assignment = Assignment.find(to_assignment_id)&lt;br /&gt;
&lt;br /&gt;
      from_assignment.due_dates.each do |due_date|&lt;br /&gt;
        new_due_date = due_date.dup&lt;br /&gt;
        new_due_date.parent = to_assignment&lt;br /&gt;
        new_due_date.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  before_save :set_default_round&lt;br /&gt;
&lt;br /&gt;
  def set_default_round&lt;br /&gt;
    self.round ||= 1&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 4. Permission-Checking Methods ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Currently, permission checks depend only on user roles and ignore the actual deadline.  &lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Role-based permissions (participant_helpers.rb)&lt;br /&gt;
def participant_permissions(authorization)&lt;br /&gt;
  case authorization&lt;br /&gt;
  when 'reader'   then { can_submit: false, can_review: true, can_take_quiz: true }&lt;br /&gt;
  when 'reviewer' then { can_submit: false, can_review: true, can_take_quiz: false }&lt;br /&gt;
  when 'submitter' then { can_submit: true, can_review: false, can_take_quiz: false }&lt;br /&gt;
  else { can_submit: true, can_review: true, can_take_quiz: true }&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And deadline-based checks exist separately in the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def submission_allowed(topic_id = nil)&lt;br /&gt;
  check_condition('submission_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
def can_review(topic_id = nil)&lt;br /&gt;
  check_condition('review_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These two layers (role-based and time-based) never interact, leading to inconsistencies (e.g., a user with &amp;lt;code&amp;gt;can_submit=true&amp;lt;/code&amp;gt; after deadline).&lt;br /&gt;
&lt;br /&gt;
==== Proposed Changes ====&lt;br /&gt;
&lt;br /&gt;
The new &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module integrates role-based and deadline-based checks:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDatePermissions&lt;br /&gt;
  # Permission checking methods that combine deadline-based and role-based logic&lt;br /&gt;
  #&lt;br /&gt;
  # These methods must check both:&lt;br /&gt;
  # 1. Whether the action is permitted by the current deadline (submission_allowed_id, review_allowed_id, etc.)&lt;br /&gt;
  # 2. Whether the participant has the necessary permissions (can_submit, can_review, can_take_quiz fields)&lt;br /&gt;
&lt;br /&gt;
  # Check if submission is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_submit?(participant = nil)&lt;br /&gt;
    return false unless submission_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_submit&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: submission_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if review is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_review?(participant = nil)&lt;br /&gt;
    return false unless review_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_review&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: review_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if taking a quiz is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_take_quiz?(participant = nil)&lt;br /&gt;
    return false unless quiz_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_take_quiz&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: quiz_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if teammate review is allowed&lt;br /&gt;
  def can_review_teammate?(participant = nil)&lt;br /&gt;
    return false unless teammate_review_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_review&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: teammate_review_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Generic permission checker&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    permission_field = &amp;quot;#{activity}_allowed_id&amp;quot;&lt;br /&gt;
    return false unless respond_to?(permission_field)&lt;br /&gt;
&lt;br /&gt;
    allowed_id = public_send(permission_field)&lt;br /&gt;
    return false unless allowed_id&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get permission status for an action (OK, Late, No)&lt;br /&gt;
  def permission_status_for(action)&lt;br /&gt;
    permission_field = &amp;quot;#{action}_allowed_id&amp;quot;&lt;br /&gt;
    return 'No' unless respond_to?(permission_field)&lt;br /&gt;
&lt;br /&gt;
    allowed_id = public_send(permission_field)&lt;br /&gt;
    return 'No' unless allowed_id&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name || 'No'&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This module is included in the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 5. DueDateActions Mixin ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module provides deadline-related operations for models that have due dates (Assignment, SignUpTopic).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDateActions&lt;br /&gt;
  # Generic activity permission checker that determines if an activity is permissible&lt;br /&gt;
  # based on the current deadline state for this parent object&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    current_deadline = next_due_date()&lt;br /&gt;
    return false unless current_deadline&lt;br /&gt;
&lt;br /&gt;
    current_deadline.activity_permissible?(activity)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Syntactic sugar methods for common activities&lt;br /&gt;
  def submission_permissible?&lt;br /&gt;
    activity_permissible?(:submission)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def review_permissible?&lt;br /&gt;
    activity_permissible?(:review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def teammate_review_permissible?&lt;br /&gt;
    activity_permissible?(:teammate_review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def quiz_permissible?&lt;br /&gt;
    activity_permissible?(:quiz)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def team_formation_permissible?&lt;br /&gt;
    activity_permissible?(:team_formation)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def signup_permissible?&lt;br /&gt;
    activity_permissible?(:signup)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def drop_topic_permissible?&lt;br /&gt;
    activity_permissible?(:drop_topic)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the next due date for this assignment&lt;br /&gt;
  def next_due_date(topic_id = nil)&lt;br /&gt;
    # If a topic is specified and this assignment has topic-specific deadlines,&lt;br /&gt;
    # look for topic due dates first&lt;br /&gt;
    if topic_id &amp;amp;&amp;amp; has_topic_specific_deadlines?&lt;br /&gt;
      topic_deadline = due_dates.where(parent_id: topic_id, parent_type: 'SignUpTopic')&lt;br /&gt;
                               .upcoming&lt;br /&gt;
                               .first&lt;br /&gt;
      return topic_deadline if topic_deadline&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Fall back to assignment-level due dates&lt;br /&gt;
    due_dates.upcoming.first&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the current stage name for display purposes&lt;br /&gt;
  def current_stage&lt;br /&gt;
    deadline = next_due_date()&lt;br /&gt;
    return 'finished' unless deadline&lt;br /&gt;
&lt;br /&gt;
    deadline.deadline_type&amp;amp;.name || 'unknown'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if assignment has topic-specific deadlines&lt;br /&gt;
  def has_topic_specific_deadlines?&lt;br /&gt;
    staggered_deadline || due_dates.where(parent_type: 'SignUpTopic').exists?&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Copy due dates to a new parent object&lt;br /&gt;
  def copy_due_dates_to(new_parent)&lt;br /&gt;
    due_dates.find_each do |due_date|&lt;br /&gt;
      new_due_date = due_date.dup&lt;br /&gt;
      new_due_date.parent = new_parent&lt;br /&gt;
      new_due_date.save!&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This module is included in &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class Assignment &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
class SignUpTopic &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 6. Controller and API Updates ===&lt;br /&gt;
&lt;br /&gt;
Controllers can now use the new unified mixins for permission checks and deadline queries.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDatesController &amp;lt; ApplicationController&lt;br /&gt;
  def can_perform_action&lt;br /&gt;
    assignment = Assignment.find(params[:assignment_id])&lt;br /&gt;
    participant = current_user.participant_for(assignment)&lt;br /&gt;
    current_due_date = assignment.next_due_date(params[:topic_id])&lt;br /&gt;
&lt;br /&gt;
    action = params[:action_type].to_sym&lt;br /&gt;
    allowed = case action&lt;br /&gt;
              when :submit then current_due_date&amp;amp;.can_submit?(participant)&lt;br /&gt;
              when :review then current_due_date&amp;amp;.can_review?(participant)&lt;br /&gt;
              when :quiz then current_due_date&amp;amp;.can_take_quiz?(participant)&lt;br /&gt;
              else false&lt;br /&gt;
              end&lt;br /&gt;
&lt;br /&gt;
    render json: { allowed: allowed, current_stage: assignment.current_stage }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def upcoming_deadlines&lt;br /&gt;
    assignment = Assignment.find(params[:assignment_id])&lt;br /&gt;
    deadlines = assignment.due_dates.upcoming.limit(5)&lt;br /&gt;
    render json: deadlines&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def current_stage&lt;br /&gt;
    assignment = Assignment.find(params[:assignment_id])&lt;br /&gt;
    render json: { &lt;br /&gt;
      stage: assignment.current_stage,&lt;br /&gt;
      next_deadline: assignment.next_due_date&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== New Endpoints ====&lt;br /&gt;
* &amp;lt;code&amp;gt;GET /assignments/:id/due_dates/permissions?action_type=submit&amp;lt;/code&amp;gt; — Check if user can perform an action&lt;br /&gt;
* &amp;lt;code&amp;gt;GET /assignments/:id/due_dates/upcoming&amp;lt;/code&amp;gt; — Get upcoming deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;GET /assignments/:id/due_dates/current_stage&amp;lt;/code&amp;gt; — Get current stage and next deadline&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
This project aims to complete the implementation of the DueDate system in Expertiza. The current implementation consists mostly of class methods and lacks proper definition of different deadline types and permission checking functionality. This redesign will create a more robust, readable, and maintainable due date system.&lt;br /&gt;
&lt;br /&gt;
=== Existing Components ===&lt;br /&gt;
* &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model with polymorphic parent association (Assignment/SignUpTopic)&lt;br /&gt;
* &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; subclasses&lt;br /&gt;
* Basic CRUD operations (within &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AssignmentForm&amp;lt;/code&amp;gt;) and sorting functionality (&amp;lt;code&amp;gt;deadline_sort&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Database schema with &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt; field&lt;br /&gt;
&lt;br /&gt;
=== Current Behavior ===&lt;br /&gt;
&lt;br /&gt;
====Admin's View====&lt;br /&gt;
[[File:Duedates.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* When editing an assignment, due dates can be modified under the Due Dates tab.&lt;br /&gt;
* Each row contains the following columns: &amp;lt;code&amp;gt;Date &amp;amp; Time&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Use Date Updater&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Submission Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Teammate Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Meta-Review Allowed&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Reminder&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:Assignments-CurrentStage.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* In the Assignments table, the ''DueDate'' of each assignment is displayed as Current Stage and Stage Deadline.&lt;br /&gt;
&lt;br /&gt;
====Student's View====&lt;br /&gt;
[[File:Student-assignments.png|600px]]&lt;br /&gt;
* Students can view the due date information under &amp;lt;code&amp;gt;Current State&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Stage Deadline&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
* The actions available to students are determined by the current DueDate status.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-not-over.png|600px]]&lt;br /&gt;
* For example, during the submission period, students can access the &amp;lt;code&amp;gt;Submit a hyperlink&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Submit a file&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-over.png|600px]]&lt;br /&gt;
* Once the due date has passed, students cannot submit either a hyperlink or a file.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Currently, there are some glaring issues with how due_dates was created in the first place, including redundancy and lack of modularity.&lt;br /&gt;
&lt;br /&gt;
# No dedicated &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to define different kinds of deadlines. The existing &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; class only serves as an association for &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; models, and is never referenced in the current &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; implementation.&lt;br /&gt;
# Overuse of class methods making the code difficult to maintain.&lt;br /&gt;
# Missing permission checking logic for user actions.&lt;br /&gt;
# Incomplete deadline type definitions — &amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt; need to be added.&lt;br /&gt;
# Duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table, which may lead to potential bugs.&lt;br /&gt;
# No clear separation of concerns — data persistence, business logic, and permission logic are mixed in a single model.&lt;br /&gt;
&lt;br /&gt;
=== Tasks Identified ===&lt;br /&gt;
&lt;br /&gt;
# Redesign the &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.&lt;br /&gt;
# Refactor the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model to separate persistence, business logic, and permission logic into distinct layers.&lt;br /&gt;
# Introduce instance-level methods (e.g., &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;) to replace class-level utilities and improve testability.&lt;br /&gt;
# Integrate role-based and time-based permission checks within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; to ensure consistent access control.&lt;br /&gt;
# Add missing deadline types (&amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt;) and remove duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries for data integrity.&lt;br /&gt;
# Implement reusable mixins (e.g., &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;) to handle permission evaluation and deadline-related operations.&lt;br /&gt;
# Update controllers and APIs to use the new mixin-based approach for consistent permission checks and deadline queries.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Proposed Solution ==&lt;br /&gt;
&lt;br /&gt;
=== 1. DeadlineType Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Although the current codebase includes a &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model, it only serves as a passive lookup table for associations with &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt;.  &lt;br /&gt;
In practice, most parts of the system still rely on hard-coded numeric IDs or manual lookups such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Example of current usage&lt;br /&gt;
deadline_type_id = DeadlineType.find_by(name: 'review').id&lt;br /&gt;
if due_date.deadline_type_id == deadline_type_id&lt;br /&gt;
  # Perform review-related logic&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of using the model as an active domain object, &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; directly compares IDs or string literals.  &lt;br /&gt;
This leads to several structural issues:&lt;br /&gt;
&lt;br /&gt;
* Inconsistent references (some use IDs, others strings)&lt;br /&gt;
* Tight coupling between &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
* Lack of helper semantics (e.g., &amp;lt;code&amp;gt;is_review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;is_submission?&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Data duplication and integrity risks (two &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; rows)&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; will serve as a source of truth, defining all valid deadline types and providing meaningful interfaces.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE deadline_types (&lt;br /&gt;
  id BIGINT PRIMARY KEY,&lt;br /&gt;
  name VARCHAR(255) NOT NULL UNIQUE,&lt;br /&gt;
  description TEXT NOT NULL,&lt;br /&gt;
  created_at TIMESTAMP,&lt;br /&gt;
  updated_at TIMESTAMP&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Deadline Type Definitions ====&lt;br /&gt;
* &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;: submission - Student work submission deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt;: review - Peer review deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;3&amp;lt;/code&amp;gt;: teammate_review - Team member evaluation deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;5&amp;lt;/code&amp;gt;: metareview - Meta-review deadlines (kept for backward compatibility)  &lt;br /&gt;
* &amp;lt;code&amp;gt;6&amp;lt;/code&amp;gt;: drop_topic - Topic drop deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;7&amp;lt;/code&amp;gt;: signup - Course/assignment signup deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;8&amp;lt;/code&amp;gt;: team_formation - Team formation deadlines (clean up duplicate ID 10)  &lt;br /&gt;
* &amp;lt;code&amp;gt;11&amp;lt;/code&amp;gt;: quiz - Quiz completion deadlines&lt;br /&gt;
&lt;br /&gt;
==== DeadlineType Model Implementation ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineType &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
  validates :description, presence: true&lt;br /&gt;
&lt;br /&gt;
  has_many :due_dates, foreign_key: :deadline_type_id, dependent: :restrict_with_exception&lt;br /&gt;
&lt;br /&gt;
  # Semantic helper methods for deadline type identification&lt;br /&gt;
  def submission?&lt;br /&gt;
    name == 'submission'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def review?&lt;br /&gt;
    name == 'review'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def teammate_review?&lt;br /&gt;
    name == 'teammate_review'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def quiz?&lt;br /&gt;
    name == 'quiz'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def team_formation?&lt;br /&gt;
    name == 'team_formation'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def signup?&lt;br /&gt;
    name == 'signup'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def drop_topic?&lt;br /&gt;
    name == 'drop_topic'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Display methods&lt;br /&gt;
  def display_name&lt;br /&gt;
    name.humanize&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def to_s&lt;br /&gt;
    display_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 2. DeadlineRight Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model represents the permission level for a specific action at a given deadline.&lt;br /&gt;
It defines three states: &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt; (action allowed), &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt; (action allowed with penalty), and &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt; (action denied).&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE deadline_rights (&lt;br /&gt;
  id BIGINT PRIMARY KEY,&lt;br /&gt;
  name VARCHAR(255) NOT NULL UNIQUE,&lt;br /&gt;
  created_at TIMESTAMP,&lt;br /&gt;
  updated_at TIMESTAMP&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== DeadlineRight Model ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineRight &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
&lt;br /&gt;
  # Permission checking methods&lt;br /&gt;
  def allows_action?&lt;br /&gt;
    %w[OK Late].include?(name)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def denies_action?&lt;br /&gt;
    name == 'No'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def allows_with_penalty?&lt;br /&gt;
    name == 'Late'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def allows_without_penalty?&lt;br /&gt;
    name == 'OK'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Display methods&lt;br /&gt;
  def to_s&lt;br /&gt;
    name&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def permission_level&lt;br /&gt;
    case name&lt;br /&gt;
    when 'No'   then 0&lt;br /&gt;
    when 'Late' then 1&lt;br /&gt;
    when 'OK'   then 2&lt;br /&gt;
    else -1&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 3. DueDate Model Refactoring ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model currently mixes persistence logic, deadline calculation, and permission checking, violating SRP.  &lt;br /&gt;
Most of its logic is implemented as class methods, which makes testing and extension difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Old implementation (class method)&lt;br /&gt;
def self.copy(old_assignment_id, new_assignment_id)&lt;br /&gt;
  duedates = where(parent_id: old_assignment_id)&lt;br /&gt;
  duedates.each do |orig_due_date|&lt;br /&gt;
    new_due_date = orig_due_date.dup&lt;br /&gt;
    new_due_date.parent_id = new_assignment_id&lt;br /&gt;
    new_due_date.save&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Improved Design ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
&lt;br /&gt;
  belongs_to :parent, polymorphic: true&lt;br /&gt;
  belongs_to :deadline_type, foreign_key: :deadline_type_id&lt;br /&gt;
&lt;br /&gt;
  validates :due_at, presence: true&lt;br /&gt;
  validates :deadline_type_id, presence: true&lt;br /&gt;
  validates :parent, presence: true&lt;br /&gt;
  validates :round, numericality: { greater_than: 0 }, allow_nil: true&lt;br /&gt;
&lt;br /&gt;
  # Scopes for common queries&lt;br /&gt;
  scope :upcoming, -&amp;gt; { where('due_at &amp;gt; ?', Time.current).order(:due_at) }&lt;br /&gt;
  scope :overdue, -&amp;gt; { where('due_at &amp;lt; ?', Time.current).order(:due_at) }&lt;br /&gt;
  scope :for_round, -&amp;gt;(round_num) { where(round: round_num) }&lt;br /&gt;
  scope :for_deadline_type, -&amp;gt;(type_name) { joins(:deadline_type).where(deadline_types: { name: type_name }) }&lt;br /&gt;
&lt;br /&gt;
  # Instance methods replace class methods&lt;br /&gt;
  def copy(to_assignment_id)&lt;br /&gt;
    to_assignment = Assignment.find(to_assignment_id)&lt;br /&gt;
    new_due_date = dup&lt;br /&gt;
    new_due_date.parent = to_assignment&lt;br /&gt;
    new_due_date.save!&lt;br /&gt;
    new_due_date&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def set(deadline_type_id, parent_id, round)&lt;br /&gt;
    self.deadline_type_id = deadline_type_id&lt;br /&gt;
    self.parent_id = parent_id&lt;br /&gt;
    self.round = round&lt;br /&gt;
    save!&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if this deadline has passed&lt;br /&gt;
  def overdue?&lt;br /&gt;
    due_at &amp;lt; Time.current&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if this deadline is upcoming&lt;br /&gt;
  def upcoming?&lt;br /&gt;
    due_at &amp;gt; Time.current&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the deadline type name&lt;br /&gt;
  def deadline_type_name&lt;br /&gt;
    deadline_type&amp;amp;.name&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Comparison method for sorting&lt;br /&gt;
  def &amp;lt;=&amp;gt;(other)&lt;br /&gt;
    return nil unless other.is_a?(DueDate)&lt;br /&gt;
    due_at &amp;lt;=&amp;gt; other.due_at&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Class methods for collection operations&lt;br /&gt;
  class &amp;lt;&amp;lt; self&lt;br /&gt;
    def sort_due_dates(due_dates)&lt;br /&gt;
      due_dates.sort_by(&amp;amp;:due_at)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    def any_future_due_dates?(due_dates)&lt;br /&gt;
      due_dates.any?(&amp;amp;:upcoming?)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    def copy(from_assignment_id, to_assignment_id)&lt;br /&gt;
      from_assignment = Assignment.find(from_assignment_id)&lt;br /&gt;
      to_assignment = Assignment.find(to_assignment_id)&lt;br /&gt;
&lt;br /&gt;
      from_assignment.due_dates.each do |due_date|&lt;br /&gt;
        new_due_date = due_date.dup&lt;br /&gt;
        new_due_date.parent = to_assignment&lt;br /&gt;
        new_due_date.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  before_save :set_default_round&lt;br /&gt;
&lt;br /&gt;
  def set_default_round&lt;br /&gt;
    self.round ||= 1&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 4. Permission-Checking Methods ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Currently, permission checks depend only on user roles and ignore the actual deadline.  &lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Role-based permissions (participant_helpers.rb)&lt;br /&gt;
def participant_permissions(authorization)&lt;br /&gt;
  case authorization&lt;br /&gt;
  when 'reader'   then { can_submit: false, can_review: true, can_take_quiz: true }&lt;br /&gt;
  when 'reviewer' then { can_submit: false, can_review: true, can_take_quiz: false }&lt;br /&gt;
  when 'submitter' then { can_submit: true, can_review: false, can_take_quiz: false }&lt;br /&gt;
  else { can_submit: true, can_review: true, can_take_quiz: true }&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And deadline-based checks exist separately in the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def submission_allowed(topic_id = nil)&lt;br /&gt;
  check_condition('submission_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
def can_review(topic_id = nil)&lt;br /&gt;
  check_condition('review_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These two layers (role-based and time-based) never interact, leading to inconsistencies (e.g., a user with &amp;lt;code&amp;gt;can_submit=true&amp;lt;/code&amp;gt; after deadline).&lt;br /&gt;
&lt;br /&gt;
==== Proposed Changes ====&lt;br /&gt;
&lt;br /&gt;
The new &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module integrates role-based and deadline-based checks:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDatePermissions&lt;br /&gt;
  # Permission checking methods that combine deadline-based and role-based logic&lt;br /&gt;
  #&lt;br /&gt;
  # These methods must check both:&lt;br /&gt;
  # 1. Whether the action is permitted by the current deadline (submission_allowed_id, review_allowed_id, etc.)&lt;br /&gt;
  # 2. Whether the participant has the necessary permissions (can_submit, can_review, can_take_quiz fields)&lt;br /&gt;
&lt;br /&gt;
  # Check if submission is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_submit?(participant = nil)&lt;br /&gt;
    return false unless submission_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_submit&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: submission_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if review is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_review?(participant = nil)&lt;br /&gt;
    return false unless review_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_review&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: review_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if taking a quiz is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_take_quiz?(participant = nil)&lt;br /&gt;
    return false unless quiz_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_take_quiz&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: quiz_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if teammate review is allowed&lt;br /&gt;
  def can_review_teammate?(participant = nil)&lt;br /&gt;
    return false unless teammate_review_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_review&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: teammate_review_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Generic permission checker&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    permission_field = &amp;quot;#{activity}_allowed_id&amp;quot;&lt;br /&gt;
    return false unless respond_to?(permission_field)&lt;br /&gt;
&lt;br /&gt;
    allowed_id = public_send(permission_field)&lt;br /&gt;
    return false unless allowed_id&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get permission status for an action (OK, Late, No)&lt;br /&gt;
  def permission_status_for(action)&lt;br /&gt;
    permission_field = &amp;quot;#{action}_allowed_id&amp;quot;&lt;br /&gt;
    return 'No' unless respond_to?(permission_field)&lt;br /&gt;
&lt;br /&gt;
    allowed_id = public_send(permission_field)&lt;br /&gt;
    return 'No' unless allowed_id&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name || 'No'&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This module is included in the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 5. DueDateActions Mixin ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module provides deadline-related operations for models that have due dates (Assignment, SignUpTopic).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDateActions&lt;br /&gt;
  # Generic activity permission checker that determines if an activity is permissible&lt;br /&gt;
  # based on the current deadline state for this parent object&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    current_deadline = next_due_date()&lt;br /&gt;
    return false unless current_deadline&lt;br /&gt;
&lt;br /&gt;
    current_deadline.activity_permissible?(activity)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Syntactic sugar methods for common activities&lt;br /&gt;
  def submission_permissible?&lt;br /&gt;
    activity_permissible?(:submission)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def review_permissible?&lt;br /&gt;
    activity_permissible?(:review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def teammate_review_permissible?&lt;br /&gt;
    activity_permissible?(:teammate_review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def quiz_permissible?&lt;br /&gt;
    activity_permissible?(:quiz)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def team_formation_permissible?&lt;br /&gt;
    activity_permissible?(:team_formation)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def signup_permissible?&lt;br /&gt;
    activity_permissible?(:signup)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def drop_topic_permissible?&lt;br /&gt;
    activity_permissible?(:drop_topic)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the next due date for this assignment&lt;br /&gt;
  def next_due_date(topic_id = nil)&lt;br /&gt;
    # If a topic is specified and this assignment has topic-specific deadlines,&lt;br /&gt;
    # look for topic due dates first&lt;br /&gt;
    if topic_id &amp;amp;&amp;amp; has_topic_specific_deadlines?&lt;br /&gt;
      topic_deadline = due_dates.where(parent_id: topic_id, parent_type: 'SignUpTopic')&lt;br /&gt;
                               .upcoming&lt;br /&gt;
                               .first&lt;br /&gt;
      return topic_deadline if topic_deadline&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Fall back to assignment-level due dates&lt;br /&gt;
    due_dates.upcoming.first&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the current stage name for display purposes&lt;br /&gt;
  def current_stage&lt;br /&gt;
    deadline = next_due_date()&lt;br /&gt;
    return 'finished' unless deadline&lt;br /&gt;
&lt;br /&gt;
    deadline.deadline_type&amp;amp;.name || 'unknown'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if assignment has topic-specific deadlines&lt;br /&gt;
  def has_topic_specific_deadlines?&lt;br /&gt;
    staggered_deadline || due_dates.where(parent_type: 'SignUpTopic').exists?&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Copy due dates to a new parent object&lt;br /&gt;
  def copy_due_dates_to(new_parent)&lt;br /&gt;
    due_dates.find_each do |due_date|&lt;br /&gt;
      new_due_date = due_date.dup&lt;br /&gt;
      new_due_date.parent = new_parent&lt;br /&gt;
      new_due_date.save!&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This module is included in &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class Assignment &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
class SignUpTopic &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Testing ==&lt;br /&gt;
&lt;br /&gt;
The test suite includes comprehensive RSpec tests for the ''DueDate'' model and its associated models (''DeadlineType'', ''DeadlineRight'').&lt;br /&gt;
These tests verify model validations, scopes, instance methods, class methods, and callbacks with extensive coverage of edge cases and error conditions.&lt;br /&gt;
&lt;br /&gt;
=== Test Structure ===&lt;br /&gt;
&lt;br /&gt;
The test suite follows RSpec best practices with:&lt;br /&gt;
* '''Nested contexts''' for different scenarios (e.g., &amp;quot;when parent is missing&amp;quot;, &amp;quot;when round is 0&amp;quot;)&lt;br /&gt;
* '''Multiple assertions per context''' to verify behavior from different angles&lt;br /&gt;
* '''Edge case coverage''' including empty collections, nil values, and boundary conditions&lt;br /&gt;
* '''Error case testing''' for invalid data and non-existent records&lt;br /&gt;
* '''Clear descriptive names''' using &amp;quot;context 'when...'&amp;quot; and &amp;quot;it 'does something'&amp;quot; patterns&lt;br /&gt;
&lt;br /&gt;
=== Current Coverage ===&lt;br /&gt;
&lt;br /&gt;
==== Associations ====&lt;br /&gt;
* Tests verify &amp;lt;code&amp;gt;belongs_to :parent&amp;lt;/code&amp;gt; (polymorphic) and &amp;lt;code&amp;gt;belongs_to :deadline_type&amp;lt;/code&amp;gt; relationships&lt;br /&gt;
&lt;br /&gt;
==== Validations ====&lt;br /&gt;
* '''Required field validation''': Tests for parent, due_at, and deadline_type_id presence&lt;br /&gt;
* '''Round validation''': Tests for positive values, zero rejection, negative rejection, and nil handling&lt;br /&gt;
* '''Error messages''': Verifies specific error messages are added to appropriate fields&lt;br /&gt;
* '''Valid record creation''': Confirms records persist when all requirements are met&lt;br /&gt;
&lt;br /&gt;
==== Scopes ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.upcoming&amp;lt;/code&amp;gt;''': Returns future due dates ordered by due_at, excludes past dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.overdue&amp;lt;/code&amp;gt;''': Returns past due dates ordered by due_at, excludes future dates, handles empty results&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_round&amp;lt;/code&amp;gt;''': Filters by round number, handles non-existent rounds&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.for_deadline_type&amp;lt;/code&amp;gt;''': Filters by deadline type name, handles non-existent types&lt;br /&gt;
&lt;br /&gt;
==== Instance Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#overdue?&amp;lt;/code&amp;gt;''': Tests past dates (true), future dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#upcoming?&amp;lt;/code&amp;gt;''': Tests future dates (true), past dates (false)&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#set&amp;lt;/code&amp;gt;''': Verifies updating deadline_type_id, parent_id, and round with persistence and error handling&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#copy&amp;lt;/code&amp;gt;''': Tests duplication to new assignment, attribute preservation, error handling for non-existent targets&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#deadline_type_name&amp;lt;/code&amp;gt;''': Returns associated deadline type name, handles nil cases&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#last_deadline?&amp;lt;/code&amp;gt;''': Tests detection of final deadline, handles multiple deadlines and single deadline scenarios&lt;br /&gt;
* '''&amp;lt;code&amp;gt;#&amp;lt;=&amp;gt;&amp;lt;/code&amp;gt;''': Tests comparison by due_at time, handles non-DueDate objects&lt;br /&gt;
&lt;br /&gt;
==== Class Methods ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.sort_due_dates&amp;lt;/code&amp;gt;''': Sorts by due_at ascending, handles empty arrays and single elements&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.any_future_due_dates?&amp;lt;/code&amp;gt;''': Detects future dates in collections, handles empty arrays and mixed date collections&lt;br /&gt;
* '''&amp;lt;code&amp;gt;.copy&amp;lt;/code&amp;gt;''': Copies all due dates between assignments, preserves attributes, handles empty sources and non-existent records&lt;br /&gt;
&lt;br /&gt;
==== Callbacks ====&lt;br /&gt;
* '''&amp;lt;code&amp;gt;before_save :set_default_round&amp;lt;/code&amp;gt;''': Sets round to 1 when not specified, preserves explicit values, handles nil&lt;br /&gt;
&lt;br /&gt;
=== Test Examples ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Validation with multiple assertions&lt;br /&gt;
describe 'validations' do&lt;br /&gt;
  context 'when parent is missing' do&lt;br /&gt;
    let(:due_date) do&lt;br /&gt;
      DueDate.new(&lt;br /&gt;
        parent: nil,&lt;br /&gt;
        due_at: 2.days.from_now,&lt;br /&gt;
        deadline_type: submission_type,&lt;br /&gt;
        submission_allowed_id: ok_right.id,&lt;br /&gt;
        review_allowed_id: ok_right.id&lt;br /&gt;
      )&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'is invalid' do&lt;br /&gt;
      expect(due_date).to be_invalid&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'has error on parent field' do&lt;br /&gt;
      due_date.valid?&lt;br /&gt;
      expect(due_date.errors[:parent]).to include('must exist')&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when round validation' do&lt;br /&gt;
    context 'with round value of 0' do&lt;br /&gt;
      let(:due_date) do&lt;br /&gt;
        DueDate.new(&lt;br /&gt;
          parent: assignment,&lt;br /&gt;
          due_at: 2.days.from_now,&lt;br /&gt;
          deadline_type: submission_type,&lt;br /&gt;
          submission_allowed_id: ok_right.id,&lt;br /&gt;
          review_allowed_id: ok_right.id,&lt;br /&gt;
          round: 0&lt;br /&gt;
        )&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'is invalid' do&lt;br /&gt;
        expect(due_date).to be_invalid&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'has error on round field' do&lt;br /&gt;
        due_date.valid?&lt;br /&gt;
        expect(due_date.errors[:round]).to be_present&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'with nil round' do&lt;br /&gt;
      let(:due_date) do&lt;br /&gt;
        DueDate.create(&lt;br /&gt;
          parent: assignment,&lt;br /&gt;
          due_at: 2.days.from_now,&lt;br /&gt;
          deadline_type: submission_type,&lt;br /&gt;
          submission_allowed_id: ok_right.id,&lt;br /&gt;
          review_allowed_id: ok_right.id,&lt;br /&gt;
          round: nil&lt;br /&gt;
        )&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'is valid' do&lt;br /&gt;
        expect(due_date).to be_valid&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      it 'sets default round to 1' do&lt;br /&gt;
        expect(due_date.round).to eq(1)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
# Scope with edge cases&lt;br /&gt;
describe '.upcoming' do&lt;br /&gt;
  it 'returns only future due dates' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).to contain_exactly(upcoming_due_date1, upcoming_due_date2)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'orders results by due_at ascending' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).to eq([upcoming_due_date1, upcoming_due_date2])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'excludes past due dates' do&lt;br /&gt;
    upcoming = DueDate.upcoming&lt;br /&gt;
    expect(upcoming).not_to include(past_due_date)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when all due dates are in the past' do&lt;br /&gt;
    before do&lt;br /&gt;
      DueDate.destroy_all&lt;br /&gt;
      DueDate.create(&lt;br /&gt;
        parent: assignment,&lt;br /&gt;
        due_at: 3.days.ago,&lt;br /&gt;
        deadline_type: submission_type,&lt;br /&gt;
        submission_allowed_id: ok_right.id,&lt;br /&gt;
        review_allowed_id: ok_right.id&lt;br /&gt;
      )&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    it 'returns empty collection' do&lt;br /&gt;
      expect(DueDate.upcoming).to be_empty&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
# Instance method with error handling&lt;br /&gt;
describe '#copy' do&lt;br /&gt;
  let(:original) do&lt;br /&gt;
    DueDate.create(&lt;br /&gt;
      parent: assignment,&lt;br /&gt;
      due_at: 2.days.from_now,&lt;br /&gt;
      deadline_type: submission_type,&lt;br /&gt;
      submission_allowed_id: ok_right.id,&lt;br /&gt;
      review_allowed_id: late_right.id,&lt;br /&gt;
      round: 1&lt;br /&gt;
    )&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  let(:copied) { original.copy(assignment2.id) }&lt;br /&gt;
&lt;br /&gt;
  it 'creates a new persisted record' do&lt;br /&gt;
    expect(copied).to be_persisted&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'has different id from original' do&lt;br /&gt;
    expect(copied.id).not_to eq(original.id)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'preserves all attributes' do&lt;br /&gt;
    expect(copied.due_at).to eq(original.due_at)&lt;br /&gt;
    expect(copied.deadline_type_id).to eq(original.deadline_type_id)&lt;br /&gt;
    expect(copied.round).to eq(original.round)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  context 'when copying to non-existent assignment' do&lt;br /&gt;
    it 'raises error' do&lt;br /&gt;
      expect do&lt;br /&gt;
        original.copy(99999)&lt;br /&gt;
      end.to raise_error(ActiveRecord::RecordNotFound)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Test Metrics ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDate model''': 60+ examples covering all methods and edge cases&lt;br /&gt;
* '''Context depth''': 2-3 levels for complex scenarios&lt;br /&gt;
* '''Code coverage''': 100% coverage of DueDate model methods&lt;br /&gt;
&lt;br /&gt;
== Implementation Summary ==&lt;br /&gt;
&lt;br /&gt;
=== Models Created/Modified ===&lt;br /&gt;
&lt;br /&gt;
* '''DeadlineType''' - Canonical deadline type definitions with semantic helper methods&lt;br /&gt;
* '''DeadlineRight''' - Permission state model (OK/Late/No) with comparison methods&lt;br /&gt;
* '''DueDate''' - Refactored with instance methods, scopes, and permission checking&lt;br /&gt;
* '''TopicDueDate''' - STI subclass for topic-specific deadlines&lt;br /&gt;
* '''Assignment''' - Includes DueDateActions mixin&lt;br /&gt;
* '''SignUpTopic''' - Includes DueDateActions mixin&lt;br /&gt;
&lt;br /&gt;
=== Modules Created ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions''' - Permission checking combining deadline and role constraints&lt;br /&gt;
* '''DueDateActions''' - Deadline-related operations for parent models (Assignment, SignUpTopic)&lt;br /&gt;
&lt;br /&gt;
=== Key Improvements ===&lt;br /&gt;
&lt;br /&gt;
# '''Separation of Concerns''': Permission logic separated into dedicated modules&lt;br /&gt;
# '''Instance Methods''': Replaced class methods with testable instance methods&lt;br /&gt;
# '''Semantic Helpers''': Methods like &amp;lt;code&amp;gt;submission?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;allows_action?&amp;lt;/code&amp;gt; improve code readability&lt;br /&gt;
# '''Polymorphic Design''': Single DueDate model serves both Assignment and SignUpTopic&lt;br /&gt;
# '''Unified Permissions''': Role-based and time-based checks combined in one interface&lt;br /&gt;
# '''Data Integrity''': Removed duplicate deadline types, enforced foreign key constraints&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
=== Demo Link ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;br /&gt;
&lt;br /&gt;
* Add deadline notification system using the new permission framework&lt;br /&gt;
* Implement deadline templates for common assignment patterns&lt;br /&gt;
* Add API endpoints for mobile app integration&lt;br /&gt;
* Create admin dashboard for monitoring deadline compliance across courses&lt;br /&gt;
* Add automated deadline extension workflows based on configurable rules&lt;br /&gt;
&lt;br /&gt;
== Implementation Summary ==&lt;br /&gt;
&lt;br /&gt;
=== Models Created/Modified ===&lt;br /&gt;
&lt;br /&gt;
* '''DeadlineType''' - Canonical deadline type definitions with semantic helper methods&lt;br /&gt;
* '''DeadlineRight''' - Permission state model (OK/Late/No) with comparison methods&lt;br /&gt;
* '''DueDate''' - Refactored with instance methods, scopes, and permission checking&lt;br /&gt;
* '''TopicDueDate''' - STI subclass for topic-specific deadlines&lt;br /&gt;
* '''Assignment''' - Includes DueDateActions mixin&lt;br /&gt;
* '''SignUpTopic''' - Includes DueDateActions mixin&lt;br /&gt;
&lt;br /&gt;
=== Modules Created ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions''' - Permission checking combining deadline and role constraints&lt;br /&gt;
* '''DueDateActions''' - Deadline-related operations for parent models (Assignment, SignUpTopic)&lt;br /&gt;
&lt;br /&gt;
=== Key Improvements ===&lt;br /&gt;
&lt;br /&gt;
# '''Separation of Concerns''': Permission logic separated into dedicated modules&lt;br /&gt;
# '''Instance Methods''': Replaced class methods with testable instance methods&lt;br /&gt;
# '''Semantic Helpers''': Methods like &amp;lt;code&amp;gt;submission?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;allows_action?&amp;lt;/code&amp;gt; improve code readability&lt;br /&gt;
# '''Polymorphic Design''': Single DueDate model serves both Assignment and SignUpTopic&lt;br /&gt;
# '''Unified Permissions''': Role-based and time-based checks combined in one interface&lt;br /&gt;
# '''Data Integrity''': Removed duplicate deadline types, enforced foreign key constraints&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
=== Demo Link ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167149</id>
		<title>CSC/ECE 517 Fall 2025 - E2566. Finish DueDates</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167149"/>
		<updated>2025-12-01T03:34:42Z</updated>

		<summary type="html">&lt;p&gt;Skim253: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
This project aims to complete the implementation of the DueDate system in Expertiza. The current implementation consists mostly of class methods and lacks proper definition of different deadline types and permission checking functionality. This redesign will create a more robust, readable, and maintainable due date system.&lt;br /&gt;
&lt;br /&gt;
=== Existing Components ===&lt;br /&gt;
* &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model with polymorphic parent association (Assignment/SignUpTopic)&lt;br /&gt;
* &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; subclasses&lt;br /&gt;
* Basic CRUD operations (within &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AssignmentForm&amp;lt;/code&amp;gt;) and sorting functionality (&amp;lt;code&amp;gt;deadline_sort&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Database schema with &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt; field&lt;br /&gt;
&lt;br /&gt;
=== Current Behavior ===&lt;br /&gt;
&lt;br /&gt;
====Admin's View====&lt;br /&gt;
[[File:Duedates.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* When editing an assignment, due dates can be modified under the Due Dates tab.&lt;br /&gt;
* Each row contains the following columns: &amp;lt;code&amp;gt;Date &amp;amp; Time&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Use Date Updater&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Submission Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Teammate Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Meta-Review Allowed&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Reminder&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:Assignments-CurrentStage.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* In the Assignments table, the ''DueDate'' of each assignment is displayed as Current Stage and Stage Deadline.&lt;br /&gt;
&lt;br /&gt;
====Student's View====&lt;br /&gt;
[[File:Student-assignments.png|600px]]&lt;br /&gt;
* Students can view the due date information under &amp;lt;code&amp;gt;Current State&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Stage Deadline&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
* The actions available to students are determined by the current DueDate status.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-not-over.png|600px]]&lt;br /&gt;
* For example, during the submission period, students can access the &amp;lt;code&amp;gt;Submit a hyperlink&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Submit a file&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-over.png|600px]]&lt;br /&gt;
* Once the due date has passed, students cannot submit either a hyperlink or a file.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Currently, there are some glaring issues with how due_dates was created in the first place, including redundancy and lack of modularity.&lt;br /&gt;
&lt;br /&gt;
# No dedicated &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to define different kinds of deadlines. The existing &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; class only serves as an association for &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; models, and is never referenced in the current &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; implementation.&lt;br /&gt;
# Overuse of class methods making the code difficult to maintain.&lt;br /&gt;
# Missing permission checking logic for user actions.&lt;br /&gt;
# Incomplete deadline type definitions — &amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt; need to be added.&lt;br /&gt;
# Duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table, which may lead to potential bugs.&lt;br /&gt;
# No clear separation of concerns — data persistence, business logic, and permission logic are mixed in a single model.&lt;br /&gt;
&lt;br /&gt;
=== Tasks Identified ===&lt;br /&gt;
&lt;br /&gt;
# Redesign the &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.&lt;br /&gt;
# Refactor the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model to separate persistence, business logic, and permission logic into distinct layers.&lt;br /&gt;
# Introduce instance-level methods (e.g., &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;copy&amp;lt;/code&amp;gt;) to replace class-level utilities and improve testability.&lt;br /&gt;
# Integrate role-based and time-based permission checks within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; to ensure consistent access control.&lt;br /&gt;
# Add missing deadline types (&amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt;) and remove duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries for data integrity.&lt;br /&gt;
# Implement reusable mixins (e.g., &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt;) to handle permission evaluation and deadline-related operations.&lt;br /&gt;
# Update controllers and APIs to use the new mixin-based approach for consistent permission checks and deadline queries.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Proposed Solution ==&lt;br /&gt;
&lt;br /&gt;
=== 1. DeadlineType Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Although the current codebase includes a &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model, it only serves as a passive lookup table for associations with &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt;.  &lt;br /&gt;
In practice, most parts of the system still rely on hard-coded numeric IDs or manual lookups such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Example of current usage&lt;br /&gt;
deadline_type_id = DeadlineType.find_by(name: 'review').id&lt;br /&gt;
if due_date.deadline_type_id == deadline_type_id&lt;br /&gt;
  # Perform review-related logic&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of using the model as an active domain object, &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; directly compares IDs or string literals.  &lt;br /&gt;
This leads to several structural issues:&lt;br /&gt;
&lt;br /&gt;
* Inconsistent references (some use IDs, others strings)&lt;br /&gt;
* Tight coupling between &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
* Lack of helper semantics (e.g., &amp;lt;code&amp;gt;is_review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;is_submission?&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Data duplication and integrity risks (two &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; rows)&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; will serve as a source of truth, defining all valid deadline types and providing meaningful interfaces.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE deadline_types (&lt;br /&gt;
  id BIGINT PRIMARY KEY,&lt;br /&gt;
  name VARCHAR(255) NOT NULL UNIQUE,&lt;br /&gt;
  description TEXT NOT NULL,&lt;br /&gt;
  created_at TIMESTAMP,&lt;br /&gt;
  updated_at TIMESTAMP&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Deadline Type Definitions ====&lt;br /&gt;
* &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;: submission - Student work submission deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt;: review - Peer review deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;3&amp;lt;/code&amp;gt;: teammate_review - Team member evaluation deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;5&amp;lt;/code&amp;gt;: metareview - Meta-review deadlines (kept for backward compatibility)  &lt;br /&gt;
* &amp;lt;code&amp;gt;6&amp;lt;/code&amp;gt;: drop_topic - Topic drop deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;7&amp;lt;/code&amp;gt;: signup - Course/assignment signup deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;8&amp;lt;/code&amp;gt;: team_formation - Team formation deadlines (clean up duplicate ID 10)  &lt;br /&gt;
* &amp;lt;code&amp;gt;11&amp;lt;/code&amp;gt;: quiz - Quiz completion deadlines&lt;br /&gt;
&lt;br /&gt;
==== DeadlineType Model Implementation ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineType &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
  validates :description, presence: true&lt;br /&gt;
&lt;br /&gt;
  has_many :due_dates, foreign_key: :deadline_type_id, dependent: :restrict_with_exception&lt;br /&gt;
&lt;br /&gt;
  # Semantic helper methods for deadline type identification&lt;br /&gt;
  def submission?&lt;br /&gt;
    name == 'submission'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def review?&lt;br /&gt;
    name == 'review'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def teammate_review?&lt;br /&gt;
    name == 'teammate_review'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def quiz?&lt;br /&gt;
    name == 'quiz'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def team_formation?&lt;br /&gt;
    name == 'team_formation'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def signup?&lt;br /&gt;
    name == 'signup'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def drop_topic?&lt;br /&gt;
    name == 'drop_topic'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Display methods&lt;br /&gt;
  def display_name&lt;br /&gt;
    name.humanize&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def to_s&lt;br /&gt;
    display_name&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 2. DeadlineRight Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Purpose ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DeadlineRight&amp;lt;/code&amp;gt; model represents the permission level for a specific action at a given deadline.&lt;br /&gt;
It defines three states: &amp;lt;code&amp;gt;OK&amp;lt;/code&amp;gt; (action allowed), &amp;lt;code&amp;gt;Late&amp;lt;/code&amp;gt; (action allowed with penalty), and &amp;lt;code&amp;gt;No&amp;lt;/code&amp;gt; (action denied).&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE deadline_rights (&lt;br /&gt;
  id BIGINT PRIMARY KEY,&lt;br /&gt;
  name VARCHAR(255) NOT NULL UNIQUE,&lt;br /&gt;
  created_at TIMESTAMP,&lt;br /&gt;
  updated_at TIMESTAMP&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== DeadlineRight Model ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DeadlineRight &amp;lt; ApplicationRecord&lt;br /&gt;
  validates :name, presence: true, uniqueness: true&lt;br /&gt;
&lt;br /&gt;
  # Permission checking methods&lt;br /&gt;
  def allows_action?&lt;br /&gt;
    %w[OK Late].include?(name)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def denies_action?&lt;br /&gt;
    name == 'No'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def allows_with_penalty?&lt;br /&gt;
    name == 'Late'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def allows_without_penalty?&lt;br /&gt;
    name == 'OK'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Display methods&lt;br /&gt;
  def to_s&lt;br /&gt;
    name&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def permission_level&lt;br /&gt;
    case name&lt;br /&gt;
    when 'No'   then 0&lt;br /&gt;
    when 'Late' then 1&lt;br /&gt;
    when 'OK'   then 2&lt;br /&gt;
    else -1&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 3. DueDate Model Refactoring ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model currently mixes persistence logic, deadline calculation, and permission checking, violating SRP.  &lt;br /&gt;
Most of its logic is implemented as class methods, which makes testing and extension difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Current design&lt;br /&gt;
def self.copy(old_assignment_id, new_assignment_id)&lt;br /&gt;
  duedates = where(parent_id: old_assignment_id)&lt;br /&gt;
  duedates.each do |orig_due_date|&lt;br /&gt;
    new_due_date = orig_due_date.dup&lt;br /&gt;
    new_due_date.parent_id = new_assignment_id&lt;br /&gt;
    new_due_date.save&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Improved Design ====&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
&lt;br /&gt;
  belongs_to :parent, polymorphic: true&lt;br /&gt;
  belongs_to :deadline_type, foreign_key: :deadline_type_id&lt;br /&gt;
&lt;br /&gt;
  validates :due_at, presence: true&lt;br /&gt;
  validates :deadline_type_id, presence: true&lt;br /&gt;
  validates :parent, presence: true&lt;br /&gt;
  validates :round, numericality: { greater_than: 0 }, allow_nil: true&lt;br /&gt;
&lt;br /&gt;
  # Scopes for common queries&lt;br /&gt;
  scope :upcoming, -&amp;gt; { where('due_at &amp;gt; ?', Time.current).order(:due_at) }&lt;br /&gt;
  scope :overdue, -&amp;gt; { where('due_at &amp;lt; ?', Time.current).order(:due_at) }&lt;br /&gt;
  scope :for_round, -&amp;gt;(round_num) { where(round: round_num) }&lt;br /&gt;
  scope :for_deadline_type, -&amp;gt;(type_name) { joins(:deadline_type).where(deadline_types: { name: type_name }) }&lt;br /&gt;
&lt;br /&gt;
  # Instance methods replace class methods&lt;br /&gt;
  def copy(to_assignment_id)&lt;br /&gt;
    to_assignment = Assignment.find(to_assignment_id)&lt;br /&gt;
    new_due_date = dup&lt;br /&gt;
    new_due_date.parent = to_assignment&lt;br /&gt;
    new_due_date.save!&lt;br /&gt;
    new_due_date&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def set(deadline_type_id, parent_id, round)&lt;br /&gt;
    self.deadline_type_id = deadline_type_id&lt;br /&gt;
    self.parent_id = parent_id&lt;br /&gt;
    self.round = round&lt;br /&gt;
    save!&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if this deadline has passed&lt;br /&gt;
  def overdue?&lt;br /&gt;
    due_at &amp;lt; Time.current&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if this deadline is upcoming&lt;br /&gt;
  def upcoming?&lt;br /&gt;
    due_at &amp;gt; Time.current&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the deadline type name&lt;br /&gt;
  def deadline_type_name&lt;br /&gt;
    deadline_type&amp;amp;.name&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Comparison method for sorting&lt;br /&gt;
  def &amp;lt;=&amp;gt;(other)&lt;br /&gt;
    return nil unless other.is_a?(DueDate)&lt;br /&gt;
    due_at &amp;lt;=&amp;gt; other.due_at&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Class methods for collection operations&lt;br /&gt;
  class &amp;lt;&amp;lt; self&lt;br /&gt;
    def sort_due_dates(due_dates)&lt;br /&gt;
      due_dates.sort_by(&amp;amp;:due_at)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    def any_future_due_dates?(due_dates)&lt;br /&gt;
      due_dates.any?(&amp;amp;:upcoming?)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    def copy(from_assignment_id, to_assignment_id)&lt;br /&gt;
      from_assignment = Assignment.find(from_assignment_id)&lt;br /&gt;
      to_assignment = Assignment.find(to_assignment_id)&lt;br /&gt;
&lt;br /&gt;
      from_assignment.due_dates.each do |due_date|&lt;br /&gt;
        new_due_date = due_date.dup&lt;br /&gt;
        new_due_date.parent = to_assignment&lt;br /&gt;
        new_due_date.save!&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  private&lt;br /&gt;
&lt;br /&gt;
  before_save :set_default_round&lt;br /&gt;
&lt;br /&gt;
  def set_default_round&lt;br /&gt;
    self.round ||= 1&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 4. Permission-Checking Methods ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Currently, permission checks depend only on user roles and ignore the actual deadline.  &lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Role-based permissions (participant_helpers.rb)&lt;br /&gt;
def participant_permissions(authorization)&lt;br /&gt;
  case authorization&lt;br /&gt;
  when 'reader'   then { can_submit: false, can_review: true, can_take_quiz: true }&lt;br /&gt;
  when 'reviewer' then { can_submit: false, can_review: true, can_take_quiz: false }&lt;br /&gt;
  when 'submitter' then { can_submit: true, can_review: false, can_take_quiz: false }&lt;br /&gt;
  else { can_submit: true, can_review: true, can_take_quiz: true }&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And deadline-based checks exist separately in the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def submission_allowed(topic_id = nil)&lt;br /&gt;
  check_condition('submission_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
def can_review(topic_id = nil)&lt;br /&gt;
  check_condition('review_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These two layers (role-based and time-based) never interact, leading to inconsistencies (e.g., a user with &amp;lt;code&amp;gt;can_submit=true&amp;lt;/code&amp;gt; after deadline).&lt;br /&gt;
&lt;br /&gt;
==== Proposed Changes ====&lt;br /&gt;
&lt;br /&gt;
The new &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; module integrates role-based and deadline-based checks:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDatePermissions&lt;br /&gt;
  # Permission checking methods that combine deadline-based and role-based logic&lt;br /&gt;
  #&lt;br /&gt;
  # These methods must check both:&lt;br /&gt;
  # 1. Whether the action is permitted by the current deadline (submission_allowed_id, review_allowed_id, etc.)&lt;br /&gt;
  # 2. Whether the participant has the necessary permissions (can_submit, can_review, can_take_quiz fields)&lt;br /&gt;
&lt;br /&gt;
  # Check if submission is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_submit?(participant = nil)&lt;br /&gt;
    return false unless submission_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_submit&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: submission_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if review is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_review?(participant = nil)&lt;br /&gt;
    return false unless review_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_review&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: review_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if taking a quiz is allowed based on both deadline and participant permissions&lt;br /&gt;
  def can_take_quiz?(participant = nil)&lt;br /&gt;
    return false unless quiz_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_take_quiz&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: quiz_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if teammate review is allowed&lt;br /&gt;
  def can_review_teammate?(participant = nil)&lt;br /&gt;
    return false unless teammate_review_allowed_id&lt;br /&gt;
    return false if participant &amp;amp;&amp;amp; !participant.can_review&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: teammate_review_allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Generic permission checker&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    permission_field = &amp;quot;#{activity}_allowed_id&amp;quot;&lt;br /&gt;
    return false unless respond_to?(permission_field)&lt;br /&gt;
&lt;br /&gt;
    allowed_id = public_send(permission_field)&lt;br /&gt;
    return false unless allowed_id&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name&amp;amp;.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get permission status for an action (OK, Late, No)&lt;br /&gt;
  def permission_status_for(action)&lt;br /&gt;
    permission_field = &amp;quot;#{action}_allowed_id&amp;quot;&lt;br /&gt;
    return 'No' unless respond_to?(permission_field)&lt;br /&gt;
&lt;br /&gt;
    allowed_id = public_send(permission_field)&lt;br /&gt;
    return 'No' unless allowed_id&lt;br /&gt;
&lt;br /&gt;
    deadline_right = DeadlineRight.find_by(id: allowed_id)&lt;br /&gt;
    deadline_right&amp;amp;.name || 'No'&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This module is included in the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 5. DueDateActions Mixin ===&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDateActions&amp;lt;/code&amp;gt; module provides deadline-related operations for models that have due dates (Assignment, SignUpTopic).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDateActions&lt;br /&gt;
  # Generic activity permission checker that determines if an activity is permissible&lt;br /&gt;
  # based on the current deadline state for this parent object&lt;br /&gt;
  def activity_permissible?(activity)&lt;br /&gt;
    current_deadline = next_due_date()&lt;br /&gt;
    return false unless current_deadline&lt;br /&gt;
&lt;br /&gt;
    current_deadline.activity_permissible?(activity)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Syntactic sugar methods for common activities&lt;br /&gt;
  def submission_permissible?&lt;br /&gt;
    activity_permissible?(:submission)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def review_permissible?&lt;br /&gt;
    activity_permissible?(:review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def teammate_review_permissible?&lt;br /&gt;
    activity_permissible?(:teammate_review)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def quiz_permissible?&lt;br /&gt;
    activity_permissible?(:quiz)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def team_formation_permissible?&lt;br /&gt;
    activity_permissible?(:team_formation)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def signup_permissible?&lt;br /&gt;
    activity_permissible?(:signup)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def drop_topic_permissible?&lt;br /&gt;
    activity_permissible?(:drop_topic)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the next due date for this assignment&lt;br /&gt;
  def next_due_date(topic_id = nil)&lt;br /&gt;
    # If a topic is specified and this assignment has topic-specific deadlines,&lt;br /&gt;
    # look for topic due dates first&lt;br /&gt;
    if topic_id &amp;amp;&amp;amp; has_topic_specific_deadlines?&lt;br /&gt;
      topic_deadline = due_dates.where(parent_id: topic_id, parent_type: 'SignUpTopic')&lt;br /&gt;
                               .upcoming&lt;br /&gt;
                               .first&lt;br /&gt;
      return topic_deadline if topic_deadline&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    # Fall back to assignment-level due dates&lt;br /&gt;
    due_dates.upcoming.first&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Get the current stage name for display purposes&lt;br /&gt;
  def current_stage&lt;br /&gt;
    deadline = next_due_date()&lt;br /&gt;
    return 'finished' unless deadline&lt;br /&gt;
&lt;br /&gt;
    deadline.deadline_type&amp;amp;.name || 'unknown'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Check if assignment has topic-specific deadlines&lt;br /&gt;
  def has_topic_specific_deadlines?&lt;br /&gt;
    staggered_deadline || due_dates.where(parent_type: 'SignUpTopic').exists?&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  # Copy due dates to a new parent object&lt;br /&gt;
  def copy_due_dates_to(new_parent)&lt;br /&gt;
    due_dates.find_each do |due_date|&lt;br /&gt;
      new_due_date = due_date.dup&lt;br /&gt;
      new_due_date.parent = new_parent&lt;br /&gt;
      new_due_date.save!&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This module is included in &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;SignUpTopic&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class Assignment &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
class SignUpTopic &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateActions&lt;br /&gt;
  # ...&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 6. Controller and API Updates ===&lt;br /&gt;
&lt;br /&gt;
Controllers can now use the new unified mixins for permission checks and deadline queries.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDatesController &amp;lt; ApplicationController&lt;br /&gt;
  def can_perform_action&lt;br /&gt;
    assignment = Assignment.find(params[:assignment_id])&lt;br /&gt;
    participant = current_user.participant_for(assignment)&lt;br /&gt;
    current_due_date = assignment.next_due_date(params[:topic_id])&lt;br /&gt;
&lt;br /&gt;
    action = params[:action_type].to_sym&lt;br /&gt;
    allowed = case action&lt;br /&gt;
              when :submit then current_due_date&amp;amp;.can_submit?(participant)&lt;br /&gt;
              when :review then current_due_date&amp;amp;.can_review?(participant)&lt;br /&gt;
              when :quiz then current_due_date&amp;amp;.can_take_quiz?(participant)&lt;br /&gt;
              else false&lt;br /&gt;
              end&lt;br /&gt;
&lt;br /&gt;
    render json: { allowed: allowed, current_stage: assignment.current_stage }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def upcoming_deadlines&lt;br /&gt;
    assignment = Assignment.find(params[:assignment_id])&lt;br /&gt;
    deadlines = assignment.due_dates.upcoming.limit(5)&lt;br /&gt;
    render json: deadlines&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def current_stage&lt;br /&gt;
    assignment = Assignment.find(params[:assignment_id])&lt;br /&gt;
    render json: { &lt;br /&gt;
      stage: assignment.current_stage,&lt;br /&gt;
      next_deadline: assignment.next_due_date&lt;br /&gt;
    }&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== New Endpoints ====&lt;br /&gt;
* &amp;lt;code&amp;gt;GET /assignments/:id/due_dates/permissions?action_type=submit&amp;lt;/code&amp;gt; — Check if user can perform an action&lt;br /&gt;
* &amp;lt;code&amp;gt;GET /assignments/:id/due_dates/upcoming&amp;lt;/code&amp;gt; — Get upcoming deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;GET /assignments/:id/due_dates/current_stage&amp;lt;/code&amp;gt; — Get current stage and next deadline&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Testing Plan ==&lt;br /&gt;
&lt;br /&gt;
The current test suite already includes RSpec tests for ''Assignment'' and ''DueDate'' models.&lt;br /&gt;
These tests verify whether deadlines allow actions such as submission, review, or quiz based on ''DeadlineRight''.&lt;br /&gt;
However, they only validate time-based permissions, not user role constraints.&lt;br /&gt;
&lt;br /&gt;
=== Current Coverage ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDate validations''' ensure required fields (parent, due_at, deadline_type_id) are present&lt;br /&gt;
* '''DueDate scopes''' test upcoming, overdue, for_round, and for_deadline_type queries&lt;br /&gt;
* '''DueDate instance methods''' verify overdue?, upcoming?, set, copy, and comparison operations&lt;br /&gt;
* '''DueDate class methods''' test sort_due_dates, any_future_due_dates?, and copy operations&lt;br /&gt;
* '''DueDate callbacks''' verify default round setting behavior&lt;br /&gt;
&lt;br /&gt;
No existing tests consider ''Participant'' role flags (''can_submit'', ''can_review'', ''can_take_quiz'').&lt;br /&gt;
&lt;br /&gt;
=== Planned Updates ===&lt;br /&gt;
&lt;br /&gt;
* Add tests for ''DueDatePermissions'' module methods: ''can_submit?'', ''can_review?'', and ''can_take_quiz?'' with participant parameters&lt;br /&gt;
* Add tests for ''DueDateActions'' module methods in Assignment context&lt;br /&gt;
* Test integration of role-based and time-based logic through unified permission checking&lt;br /&gt;
* Verify backward compatibility with existing ''Assignment'' tests&lt;br /&gt;
&lt;br /&gt;
=== Example Test Cases ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
describe DueDate, '#can_submit?' do&lt;br /&gt;
  let(:due_date) { create(:due_date, submission_allowed_id: ok_right.id) }&lt;br /&gt;
  let(:participant) { create(:participant, can_submit: true) }&lt;br /&gt;
&lt;br /&gt;
  it 'returns true when both deadline and participant allow submission' do&lt;br /&gt;
    expect(due_date.can_submit?(participant)).to be true&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'returns false when participant cannot submit' do&lt;br /&gt;
    participant.can_submit = false&lt;br /&gt;
    expect(due_date.can_submit?(participant)).to be false&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'returns false when deadline does not allow submission' do&lt;br /&gt;
    due_date.submission_allowed_id = no_right.id&lt;br /&gt;
    expect(due_date.can_submit?(participant)).to be false&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
describe Assignment, '#activity_permissible?' do&lt;br /&gt;
  let(:assignment) { create(:assignment) }&lt;br /&gt;
  let(:due_date) { create(:due_date, parent: assignment, submission_allowed_id: ok_right.id) }&lt;br /&gt;
&lt;br /&gt;
  it 'returns true when current deadline allows the activity' do&lt;br /&gt;
    expect(assignment.activity_permissible?(:submission)).to be true&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it 'returns false when no upcoming deadline exists' do&lt;br /&gt;
    due_date.update(due_at: 1.day.ago)&lt;br /&gt;
    expect(assignment.activity_permissible?(:submission)).to be false&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Expected Outcome ===&lt;br /&gt;
&lt;br /&gt;
* Both user roles and deadlines influence action permissions&lt;br /&gt;
* Permission checks are consistent across the application&lt;br /&gt;
* All existing tests continue to pass&lt;br /&gt;
* New tests verify the integrated permission system&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Implementation Summary ==&lt;br /&gt;
&lt;br /&gt;
=== Models Created/Modified ===&lt;br /&gt;
&lt;br /&gt;
* '''DeadlineType''' - Canonical deadline type definitions with semantic helper methods&lt;br /&gt;
* '''DeadlineRight''' - Permission state model (OK/Late/No) with comparison methods&lt;br /&gt;
* '''DueDate''' - Refactored with instance methods, scopes, and permission checking&lt;br /&gt;
* '''TopicDueDate''' - STI subclass for topic-specific deadlines&lt;br /&gt;
* '''Assignment''' - Includes DueDateActions mixin&lt;br /&gt;
* '''SignUpTopic''' - Includes DueDateActions mixin&lt;br /&gt;
&lt;br /&gt;
=== Modules Created ===&lt;br /&gt;
&lt;br /&gt;
* '''DueDatePermissions''' - Permission checking combining deadline and role constraints&lt;br /&gt;
* '''DueDateActions''' - Deadline-related operations for parent models (Assignment, SignUpTopic)&lt;br /&gt;
&lt;br /&gt;
=== Key Improvements ===&lt;br /&gt;
&lt;br /&gt;
# '''Separation of Concerns''': Permission logic separated into dedicated modules&lt;br /&gt;
# '''Instance Methods''': Replaced class methods with testable instance methods&lt;br /&gt;
# '''Semantic Helpers''': Methods like &amp;lt;code&amp;gt;submission?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;allows_action?&amp;lt;/code&amp;gt; improve code readability&lt;br /&gt;
# '''Polymorphic Design''': Single DueDate model serves both Assignment and SignUpTopic&lt;br /&gt;
# '''Unified Permissions''': Role-based and time-based checks combined in one interface&lt;br /&gt;
# '''Data Integrity''': Removed duplicate deadline types, enforced foreign key constraints&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
=== Demo Link ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167089</id>
		<title>CSC/ECE 517 Fall 2025 - E2566. Finish DueDates</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167089"/>
		<updated>2025-11-11T01:11:32Z</updated>

		<summary type="html">&lt;p&gt;Skim253: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
This project aims to complete the implementation of the DueDate system in Expertiza. The current implementation consists mostly of class methods and lacks proper definition of different deadline types and permission checking functionality. This redesign will create a more robust, readable, and maintainable due date system.&lt;br /&gt;
&lt;br /&gt;
=== Existing Components ===&lt;br /&gt;
* &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model with polymorphic parent association (Assignment/SignUpTopic)&lt;br /&gt;
* &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; subclasses&lt;br /&gt;
* Basic CRUD operations (within &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AssignmentForm&amp;lt;/code&amp;gt;) and sorting functionality (&amp;lt;code&amp;gt;deadline_sort&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Database schema with &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt; field&lt;br /&gt;
&lt;br /&gt;
=== Current Behavior ===&lt;br /&gt;
&lt;br /&gt;
====Admin's View====&lt;br /&gt;
[[File:Duedates.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* When editing an assignment, due dates can be modified under the Due Dates tab.&lt;br /&gt;
* Each row contains the following columns: &amp;lt;code&amp;gt;Date &amp;amp; Time&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Use Date Updater&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Submission Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Teammate Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Meta-Review Allowed&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Reminder&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:Assignments-CurrentStage.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* In the Assignments table, the ''DueDate'' of each assignment is displayed as Current Stage and Stage Deadline.&lt;br /&gt;
&lt;br /&gt;
====Student's View====&lt;br /&gt;
[[File:Student-assignments.png|600px]]&lt;br /&gt;
* Students can view the due date information under &amp;lt;code&amp;gt;Current State&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Stage Deadline&amp;lt;/code&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
* The actions available to students are determined by the current DueDate status.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-not-over.png|600px]]&lt;br /&gt;
* For example, during the submission period, students can access the &amp;lt;code&amp;gt;Submit a hyperlink&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Submit a file&amp;lt;/code&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
[[File:Student-duedate-over.png|600px]]&lt;br /&gt;
* Once the due date has passed, students cannot submit either a hyperlink or a file.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Currently, there are some glaring issues with how due_dates was created in the first place, including redundancy and lack of modularity.&lt;br /&gt;
&lt;br /&gt;
# No dedicated &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to define different kinds of deadlines. The existing &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; class only serves as an association for &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; models, and is never referenced in the current &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; implementation.&lt;br /&gt;
# Overuse of class methods making the code difficult to maintain.&lt;br /&gt;
# Missing permission checking logic for user actions.&lt;br /&gt;
# Incomplete deadline type definitions — &amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt; need to be added.&lt;br /&gt;
# Duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table, which may lead to potential bugs.&lt;br /&gt;
# No clear separation of concerns — data persistence, business logic, and permission logic are mixed in a single model.&lt;br /&gt;
&lt;br /&gt;
=== Tasks Identified ===&lt;br /&gt;
&lt;br /&gt;
# Redesign the &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.&lt;br /&gt;
# Refactor the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model to separate persistence, business logic, and permission logic into distinct layers.&lt;br /&gt;
# Introduce instance-level methods (e.g., &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;copy_to&amp;lt;/code&amp;gt;) to replace class-level utilities and improve testability.&lt;br /&gt;
# Integrate role-based and time-based permission checks within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; to ensure consistent access control.&lt;br /&gt;
# Add missing deadline types (&amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt;) and remove duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries for data integrity.&lt;br /&gt;
# Implement reusable mixins (e.g., &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateQueries&amp;lt;/code&amp;gt;) to handle permission evaluation and query operations.&lt;br /&gt;
# Update controllers and APIs to use the new mixin-based approach for consistent permission checks and deadline queries.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Proposed Solution ==&lt;br /&gt;
&lt;br /&gt;
=== 1. DeadlineType Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Although the current codebase includes a &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model, it only serves as a passive lookup table for associations with &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt;.  &lt;br /&gt;
In practice, most parts of the system still rely on hard-coded numeric IDs or manual lookups such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Example of current usage&lt;br /&gt;
deadline_type_id = DeadlineType.find_by(name: 'review').id&lt;br /&gt;
if due_date.deadline_type_id == deadline_type_id&lt;br /&gt;
  # Perform review-related logic&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of using the model as an active domain object, &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; directly compares IDs or string literals.  &lt;br /&gt;
This leads to several structural issues:&lt;br /&gt;
&lt;br /&gt;
* Inconsistent references (some use IDs, others strings)&lt;br /&gt;
* Tight coupling between &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
* Lack of helper semantics (e.g., &amp;lt;code&amp;gt;is_review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;is_submission?&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Data duplication and integrity risks (two &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; rows)&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; will serve as a source of truth, defining all valid deadline types and providing meaningful interfaces.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE deadline_types (&lt;br /&gt;
  id INT PRIMARY KEY,&lt;br /&gt;
  name VARCHAR(255) NOT NULL UNIQUE,&lt;br /&gt;
  description TEXT,&lt;br /&gt;
  created_at TIMESTAMP,&lt;br /&gt;
  updated_at TIMESTAMP&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Deadline Type Definitions ====&lt;br /&gt;
* &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;: submission - Student work submission deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt;: review - Peer review deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;3&amp;lt;/code&amp;gt;: teammate_review - Team member evaluation deadlines (NEW)  &lt;br /&gt;
* &amp;lt;code&amp;gt;5&amp;lt;/code&amp;gt;: metareview - Meta-review deadlines (kept for backward compatibility)  &lt;br /&gt;
* &amp;lt;code&amp;gt;6&amp;lt;/code&amp;gt;: drop_topic - Topic drop deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;7&amp;lt;/code&amp;gt;: signup - Course/assignment signup deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;8&amp;lt;/code&amp;gt;: team_formation - Team formation deadlines (clean up duplicate ID 10)  &lt;br /&gt;
* &amp;lt;code&amp;gt;11&amp;lt;/code&amp;gt;: quiz - Quiz completion deadlines&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 2. DueDate Model Refactoring ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model currently mixes persistence logic, deadline calculation, and permission checking, violating SRP.  &lt;br /&gt;
Most of its logic is implemented as class methods, which makes testing and extension difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Current design&lt;br /&gt;
def self.copy(old_assignment_id, new_assignment_id)&lt;br /&gt;
  duedates = where(parent_id: old_assignment_id)&lt;br /&gt;
  duedates.each do |orig_due_date|&lt;br /&gt;
    new_due_date = orig_due_date.dup&lt;br /&gt;
    new_due_date.parent_id = new_assignment_id&lt;br /&gt;
    new_due_date.save&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To address this:&lt;br /&gt;
* Replace class methods with instance-level methods.&lt;br /&gt;
* Move sorting, copying, and query logic to mixins.&lt;br /&gt;
* Introduce mixins such as &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; for permission-related checks.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Improved design&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
&lt;br /&gt;
  def copy_to(new_assignment)&lt;br /&gt;
    dup.tap { |d| d.parent_id = new_assignment.id }.save&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def next_due_date&lt;br /&gt;
    DueDate.where('due_at &amp;gt; ?', Time.zone.now).order(:due_at).first&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 3. Add Permission-Checking Methods ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Currently, permission checks depend only on user roles and ignore the actual deadline.  &lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Role-based permissions (participant_helpers.rb)&lt;br /&gt;
def participant_permissions(authorization)&lt;br /&gt;
  case authorization&lt;br /&gt;
  when 'reader'   then { can_submit: false, can_review: true, can_take_quiz: true }&lt;br /&gt;
  when 'reviewer' then { can_submit: false, can_review: true, can_take_quiz: false }&lt;br /&gt;
  when 'submitter' then { can_submit: true, can_review: false, can_take_quiz: false }&lt;br /&gt;
  else { can_submit: true, can_review: true, can_take_quiz: true }&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And deadline-based checks exist separately in the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def submission_allowed(topic_id = nil)&lt;br /&gt;
  check_condition('submission_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
def can_review(topic_id = nil)&lt;br /&gt;
  check_condition('review_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These two layers (role-based and time-based) never interact, leading to inconsistencies (e.g., a user with &amp;lt;code&amp;gt;can_submit=true&amp;lt;/code&amp;gt; after deadline).&lt;br /&gt;
&lt;br /&gt;
==== Proposed Changes ====&lt;br /&gt;
&lt;br /&gt;
Integrate role-based and deadline-based checks into the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model (or a dedicated mixin):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  def can_submit?&lt;br /&gt;
    right = DeadlineRight.find(submission_allowed_id)&lt;br /&gt;
    right.name.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def can_review?&lt;br /&gt;
    right = DeadlineRight.find(review_allowed_id)&lt;br /&gt;
    right.name.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def can_take_quiz?&lt;br /&gt;
    right = DeadlineRight.find(quiz_allowed_id)&lt;br /&gt;
    right.name.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then unify both layers via &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class Participant &amp;lt; ApplicationRecord&lt;br /&gt;
  def allowed_to?(action, assignment, topic_id = nil)&lt;br /&gt;
    due_date = assignment.find_current_stage(topic_id)&lt;br /&gt;
    return false if due_date.nil?&lt;br /&gt;
&lt;br /&gt;
    case action&lt;br /&gt;
    when :submit then can_submit &amp;amp;&amp;amp; due_date.can_submit?&lt;br /&gt;
    when :review then can_review &amp;amp;&amp;amp; due_date.can_review?&lt;br /&gt;
    when :quiz   then can_take_quiz &amp;amp;&amp;amp; due_date.can_take_quiz?&lt;br /&gt;
    else false&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This ensures that permission logic respects both the participant’s role and the current deadline phase.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 4. Permission and Query Mixins ===&lt;br /&gt;
&lt;br /&gt;
Encapsulate permission and query logic in reusable mixins instead of separate service classes.&lt;br /&gt;
This approach keeps logic close to the models and removes the need for dedicated service objects, improving maintainability and cohesion.&lt;br /&gt;
&lt;br /&gt;
==== DueDatePermissions Mixin ====&lt;br /&gt;
Permission checks that combine user role and deadline phase.&lt;br /&gt;
It is included in the Participant model for unified access control.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDatePermissions&lt;br /&gt;
  def can_submit?(user, assignment)&lt;br /&gt;
    user.participant.allowed_to?(:submit, assignment)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def can_review?(user, assignment)&lt;br /&gt;
    user.participant.allowed_to?(:review, assignment)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def can_form_teams?(user, assignment)&lt;br /&gt;
    user.participant.allowed_to?(:team_formation, assignment)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example integration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class Participant &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== DueDateQueries Mixin ====&lt;br /&gt;
Reusable query methods for upcoming and overdue deadlines.&lt;br /&gt;
It is included in the Assignment model to simplify deadline lookups.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDateQueries&lt;br /&gt;
  def upcoming_deadlines(limit: 5)&lt;br /&gt;
    due_dates&lt;br /&gt;
      .where('due_at &amp;gt; ?', Time.zone.now)&lt;br /&gt;
      .order(:due_at)&lt;br /&gt;
      .limit(limit)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def overdue_deadlines&lt;br /&gt;
    due_dates&lt;br /&gt;
      .where('due_at &amp;lt; ?', Time.zone.now)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example integration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class Assignment &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateQueries&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 5. Controller and API Updates ===&lt;br /&gt;
&lt;br /&gt;
Controllers can now use the new unified mixins (&amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateQueries&amp;lt;/code&amp;gt;) &lt;br /&gt;
for permission checks and deadline queries.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDatesController &amp;lt; ApplicationController&lt;br /&gt;
  def can_perform_action&lt;br /&gt;
    action = params[:action_type].to_sym&lt;br /&gt;
    allowed = current_user.participant.public_send(&amp;quot;can_#{action}?&amp;quot;, current_user, @assignment)&lt;br /&gt;
    render json: { allowed: allowed }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def upcoming_deadlines&lt;br /&gt;
    deadlines = @assignment.upcoming_deadlines&lt;br /&gt;
    render json: deadlines&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== New Endpoints ====&lt;br /&gt;
* &amp;lt;code&amp;gt;GET /assignments/:id/due_dates/permissions&amp;lt;/code&amp;gt; — Check user permissions for various actions  &lt;br /&gt;
* &amp;lt;code&amp;gt;GET /assignments/:id/due_dates/upcoming&amp;lt;/code&amp;gt; — Get upcoming deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;GET /assignments/:id/due_dates/current/:action_type&amp;lt;/code&amp;gt; — Get current deadline for a specific action  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Testing Plan ==&lt;br /&gt;
&lt;br /&gt;
The current test suite already includes RSpec tests for ''Assignment'' and ''DueDate'' models.&lt;br /&gt;
These tests verify whether deadlines allow actions such as submission, review, or quiz based on ''DeadlineRight''.&lt;br /&gt;
However, they only validate time-based permissions, not user role constraints.&lt;br /&gt;
&lt;br /&gt;
=== Current Coverage ===&lt;br /&gt;
&lt;br /&gt;
'''Assignment#check_condition''' ensures correct evaluation of ''DeadlineRight'' values (''OK'', ''Late'', ''No'').&lt;br /&gt;
&lt;br /&gt;
'''submission_allowed''', '''quiz_allowed''', and '''can_review''' are tested for deadline behavior.&lt;br /&gt;
&lt;br /&gt;
No existing tests consider ''Participant'' role flags (''can_submit'', ''can_review'', ''can_take_quiz'').&lt;br /&gt;
&lt;br /&gt;
=== Planned Updates ===&lt;br /&gt;
&lt;br /&gt;
Add tests for new ''DueDate'' instance methods: ''can_submit?'', ''can_review?'', and ''can_take_quiz?''.&lt;br /&gt;
&lt;br /&gt;
Integrate role-based and time-based logic under a unified interface in ''Participant#allowed_to?''.&lt;br /&gt;
&lt;br /&gt;
Additionally, verify that ''DueDatePermissions'' and ''DueDateQueries'' mixins return consistent results compared to the previous service-based approach.&lt;br /&gt;
&lt;br /&gt;
Ensure backward compatibility with existing ''Assignment'' tests.&lt;br /&gt;
&lt;br /&gt;
=== Example Case ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
describe Participant, '#allowed_to?' do&lt;br /&gt;
  it 'returns false when role allows submission but deadline is closed' do&lt;br /&gt;
    allow(assignment).to receive(:find_current_stage).and_return(due_date)&lt;br /&gt;
    allow(DeadlineRight).to receive(:find).and_return(double(name: 'No'))&lt;br /&gt;
    expect(participant.allowed_to?(:submit, assignment)).to be false&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Expected Outcome ===&lt;br /&gt;
&lt;br /&gt;
Both user roles and deadlines influence action permissions.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
=== Demo Link ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=File:Student-duedate-not-over.png&amp;diff=167088</id>
		<title>File:Student-duedate-not-over.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=File:Student-duedate-not-over.png&amp;diff=167088"/>
		<updated>2025-11-11T01:06:09Z</updated>

		<summary type="html">&lt;p&gt;Skim253: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=File:Student-duedate-over.png&amp;diff=167087</id>
		<title>File:Student-duedate-over.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=File:Student-duedate-over.png&amp;diff=167087"/>
		<updated>2025-11-11T01:05:56Z</updated>

		<summary type="html">&lt;p&gt;Skim253: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=File:Student-assignments.png&amp;diff=167086</id>
		<title>File:Student-assignments.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=File:Student-assignments.png&amp;diff=167086"/>
		<updated>2025-11-11T01:05:46Z</updated>

		<summary type="html">&lt;p&gt;Skim253: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167081</id>
		<title>CSC/ECE 517 Fall 2025 - E2566. Finish DueDates</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167081"/>
		<updated>2025-11-10T23:42:36Z</updated>

		<summary type="html">&lt;p&gt;Skim253: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
This project aims to complete the implementation of the DueDate system in Expertiza. The current implementation consists mostly of class methods and lacks proper definition of different deadline types and permission checking functionality. This redesign will create a more robust, readable, and maintainable due date system.&lt;br /&gt;
&lt;br /&gt;
=== Existing Components ===&lt;br /&gt;
* &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model with polymorphic parent association (Assignment/SignUpTopic)&lt;br /&gt;
* &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; subclasses&lt;br /&gt;
* Basic CRUD operations (within &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AssignmentForm&amp;lt;/code&amp;gt;) and sorting functionality (&amp;lt;code&amp;gt;deadline_sort&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Database schema with &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt; field&lt;br /&gt;
&lt;br /&gt;
=== Current Behavior ===&lt;br /&gt;
&lt;br /&gt;
[[File:Duedates.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* When editing an assignment, due dates can be modified under the Due Dates tab.&lt;br /&gt;
* Each row contains the following columns: &amp;lt;code&amp;gt;Date &amp;amp; Time&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Use Date Updater&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Submission Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Teammate Review Allowed&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;Meta-Review Allowed&amp;lt;/code&amp;gt;, and &amp;lt;code&amp;gt;Reminder&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
[[File:Assignments-CurrentStage.png|600px]]&lt;br /&gt;
&lt;br /&gt;
* In the Assignments table, the ''DueDate'' of each assignment is displayed as Current Stage and Stage Deadline.&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Currently, there are some glaring issues with how due_dates was created in the first place, including redundancy and lack of modularity.&lt;br /&gt;
&lt;br /&gt;
# No dedicated &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to define different kinds of deadlines. The existing &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; class only serves as an association for &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; models, and is never referenced in the current &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; implementation.&lt;br /&gt;
# Overuse of class methods making the code difficult to maintain.&lt;br /&gt;
# Missing permission checking logic for user actions.&lt;br /&gt;
# Incomplete deadline type definitions — &amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt; need to be added.&lt;br /&gt;
# Duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table, which may lead to potential bugs.&lt;br /&gt;
# No clear separation of concerns — data persistence, business logic, and permission logic are mixed in a single model.&lt;br /&gt;
&lt;br /&gt;
=== Tasks Identified ===&lt;br /&gt;
&lt;br /&gt;
# Redesign the &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.&lt;br /&gt;
# Refactor the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model to separate persistence, business logic, and permission logic into distinct layers.&lt;br /&gt;
# Introduce instance-level methods (e.g., &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;copy_to&amp;lt;/code&amp;gt;) to replace class-level utilities and improve testability.&lt;br /&gt;
# Integrate role-based and time-based permission checks within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; to ensure consistent access control.&lt;br /&gt;
# Add missing deadline types (&amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt;) and remove duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries for data integrity.&lt;br /&gt;
# Implement reusable mixins (e.g., &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateQueries&amp;lt;/code&amp;gt;) to handle permission evaluation and query operations.&lt;br /&gt;
# Update controllers and APIs to use the new mixin-based approach for consistent permission checks and deadline queries.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Proposed Solution ==&lt;br /&gt;
&lt;br /&gt;
=== 1. DeadlineType Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Although the current codebase includes a &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model, it only serves as a passive lookup table for associations with &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt;.  &lt;br /&gt;
In practice, most parts of the system still rely on hard-coded numeric IDs or manual lookups such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Example of current usage&lt;br /&gt;
deadline_type_id = DeadlineType.find_by(name: 'review').id&lt;br /&gt;
if due_date.deadline_type_id == deadline_type_id&lt;br /&gt;
  # Perform review-related logic&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of using the model as an active domain object, &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; directly compares IDs or string literals.  &lt;br /&gt;
This leads to several structural issues:&lt;br /&gt;
&lt;br /&gt;
* Inconsistent references (some use IDs, others strings)&lt;br /&gt;
* Tight coupling between &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
* Lack of helper semantics (e.g., &amp;lt;code&amp;gt;is_review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;is_submission?&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Data duplication and integrity risks (two &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; rows)&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; will serve as a source of truth, defining all valid deadline types and providing meaningful interfaces.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE deadline_types (&lt;br /&gt;
  id INT PRIMARY KEY,&lt;br /&gt;
  name VARCHAR(255) NOT NULL UNIQUE,&lt;br /&gt;
  description TEXT,&lt;br /&gt;
  created_at TIMESTAMP,&lt;br /&gt;
  updated_at TIMESTAMP&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Deadline Type Definitions ====&lt;br /&gt;
* &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;: submission - Student work submission deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt;: review - Peer review deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;3&amp;lt;/code&amp;gt;: teammate_review - Team member evaluation deadlines (NEW)  &lt;br /&gt;
* &amp;lt;code&amp;gt;5&amp;lt;/code&amp;gt;: metareview - Meta-review deadlines (kept for backward compatibility)  &lt;br /&gt;
* &amp;lt;code&amp;gt;6&amp;lt;/code&amp;gt;: drop_topic - Topic drop deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;7&amp;lt;/code&amp;gt;: signup - Course/assignment signup deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;8&amp;lt;/code&amp;gt;: team_formation - Team formation deadlines (clean up duplicate ID 10)  &lt;br /&gt;
* &amp;lt;code&amp;gt;11&amp;lt;/code&amp;gt;: quiz - Quiz completion deadlines&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 2. DueDate Model Refactoring ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model currently mixes persistence logic, deadline calculation, and permission checking, violating SRP.  &lt;br /&gt;
Most of its logic is implemented as class methods, which makes testing and extension difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Current design&lt;br /&gt;
def self.copy(old_assignment_id, new_assignment_id)&lt;br /&gt;
  duedates = where(parent_id: old_assignment_id)&lt;br /&gt;
  duedates.each do |orig_due_date|&lt;br /&gt;
    new_due_date = orig_due_date.dup&lt;br /&gt;
    new_due_date.parent_id = new_assignment_id&lt;br /&gt;
    new_due_date.save&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To address this:&lt;br /&gt;
* Replace class methods with instance-level methods.&lt;br /&gt;
* Move sorting, copying, and query logic to mixins.&lt;br /&gt;
* Introduce mixins such as &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; for permission-related checks.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Improved design&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
&lt;br /&gt;
  def copy_to(new_assignment)&lt;br /&gt;
    dup.tap { |d| d.parent_id = new_assignment.id }.save&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def next_due_date&lt;br /&gt;
    DueDate.where('due_at &amp;gt; ?', Time.zone.now).order(:due_at).first&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 3. Add Permission-Checking Methods ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Currently, permission checks depend only on user roles and ignore the actual deadline.  &lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Role-based permissions (participant_helpers.rb)&lt;br /&gt;
def participant_permissions(authorization)&lt;br /&gt;
  case authorization&lt;br /&gt;
  when 'reader'   then { can_submit: false, can_review: true, can_take_quiz: true }&lt;br /&gt;
  when 'reviewer' then { can_submit: false, can_review: true, can_take_quiz: false }&lt;br /&gt;
  when 'submitter' then { can_submit: true, can_review: false, can_take_quiz: false }&lt;br /&gt;
  else { can_submit: true, can_review: true, can_take_quiz: true }&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And deadline-based checks exist separately in the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def submission_allowed(topic_id = nil)&lt;br /&gt;
  check_condition('submission_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
def can_review(topic_id = nil)&lt;br /&gt;
  check_condition('review_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These two layers (role-based and time-based) never interact, leading to inconsistencies (e.g., a user with &amp;lt;code&amp;gt;can_submit=true&amp;lt;/code&amp;gt; after deadline).&lt;br /&gt;
&lt;br /&gt;
==== Proposed Changes ====&lt;br /&gt;
&lt;br /&gt;
Integrate role-based and deadline-based checks into the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model (or a dedicated mixin):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  def can_submit?&lt;br /&gt;
    right = DeadlineRight.find(submission_allowed_id)&lt;br /&gt;
    right.name.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def can_review?&lt;br /&gt;
    right = DeadlineRight.find(review_allowed_id)&lt;br /&gt;
    right.name.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def can_take_quiz?&lt;br /&gt;
    right = DeadlineRight.find(quiz_allowed_id)&lt;br /&gt;
    right.name.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then unify both layers via &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class Participant &amp;lt; ApplicationRecord&lt;br /&gt;
  def allowed_to?(action, assignment, topic_id = nil)&lt;br /&gt;
    due_date = assignment.find_current_stage(topic_id)&lt;br /&gt;
    return false if due_date.nil?&lt;br /&gt;
&lt;br /&gt;
    case action&lt;br /&gt;
    when :submit then can_submit &amp;amp;&amp;amp; due_date.can_submit?&lt;br /&gt;
    when :review then can_review &amp;amp;&amp;amp; due_date.can_review?&lt;br /&gt;
    when :quiz   then can_take_quiz &amp;amp;&amp;amp; due_date.can_take_quiz?&lt;br /&gt;
    else false&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This ensures that permission logic respects both the participant’s role and the current deadline phase.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 4. Permission and Query Mixins ===&lt;br /&gt;
&lt;br /&gt;
Encapsulate permission and query logic in reusable mixins instead of separate service classes.&lt;br /&gt;
This approach keeps logic close to the models and removes the need for dedicated service objects, improving maintainability and cohesion.&lt;br /&gt;
&lt;br /&gt;
==== DueDatePermissions Mixin ====&lt;br /&gt;
Permission checks that combine user role and deadline phase.&lt;br /&gt;
It is included in the Participant model for unified access control.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDatePermissions&lt;br /&gt;
  def can_submit?(user, assignment)&lt;br /&gt;
    user.participant.allowed_to?(:submit, assignment)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def can_review?(user, assignment)&lt;br /&gt;
    user.participant.allowed_to?(:review, assignment)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def can_form_teams?(user, assignment)&lt;br /&gt;
    user.participant.allowed_to?(:team_formation, assignment)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example integration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class Participant &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== DueDateQueries Mixin ====&lt;br /&gt;
Reusable query methods for upcoming and overdue deadlines.&lt;br /&gt;
It is included in the Assignment model to simplify deadline lookups.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDateQueries&lt;br /&gt;
  def upcoming_deadlines(limit: 5)&lt;br /&gt;
    due_dates&lt;br /&gt;
      .where('due_at &amp;gt; ?', Time.zone.now)&lt;br /&gt;
      .order(:due_at)&lt;br /&gt;
      .limit(limit)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def overdue_deadlines&lt;br /&gt;
    due_dates&lt;br /&gt;
      .where('due_at &amp;lt; ?', Time.zone.now)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example integration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class Assignment &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateQueries&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 5. Controller and API Updates ===&lt;br /&gt;
&lt;br /&gt;
Controllers can now use the new unified mixins (&amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateQueries&amp;lt;/code&amp;gt;) &lt;br /&gt;
for permission checks and deadline queries.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDatesController &amp;lt; ApplicationController&lt;br /&gt;
  def can_perform_action&lt;br /&gt;
    action = params[:action_type].to_sym&lt;br /&gt;
    allowed = current_user.participant.public_send(&amp;quot;can_#{action}?&amp;quot;, current_user, @assignment)&lt;br /&gt;
    render json: { allowed: allowed }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def upcoming_deadlines&lt;br /&gt;
    deadlines = @assignment.upcoming_deadlines&lt;br /&gt;
    render json: deadlines&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== New Endpoints ====&lt;br /&gt;
* &amp;lt;code&amp;gt;GET /assignments/:id/due_dates/permissions&amp;lt;/code&amp;gt; — Check user permissions for various actions  &lt;br /&gt;
* &amp;lt;code&amp;gt;GET /assignments/:id/due_dates/upcoming&amp;lt;/code&amp;gt; — Get upcoming deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;GET /assignments/:id/due_dates/current/:action_type&amp;lt;/code&amp;gt; — Get current deadline for a specific action  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Testing Plan ==&lt;br /&gt;
&lt;br /&gt;
The current test suite already includes RSpec tests for ''Assignment'' and ''DueDate'' models.&lt;br /&gt;
These tests verify whether deadlines allow actions such as submission, review, or quiz based on ''DeadlineRight''.&lt;br /&gt;
However, they only validate time-based permissions, not user role constraints.&lt;br /&gt;
&lt;br /&gt;
=== Current Coverage ===&lt;br /&gt;
&lt;br /&gt;
'''Assignment#check_condition''' ensures correct evaluation of ''DeadlineRight'' values (''OK'', ''Late'', ''No'').&lt;br /&gt;
&lt;br /&gt;
'''submission_allowed''', '''quiz_allowed''', and '''can_review''' are tested for deadline behavior.&lt;br /&gt;
&lt;br /&gt;
No existing tests consider ''Participant'' role flags (''can_submit'', ''can_review'', ''can_take_quiz'').&lt;br /&gt;
&lt;br /&gt;
=== Planned Updates ===&lt;br /&gt;
&lt;br /&gt;
Add tests for new ''DueDate'' instance methods: ''can_submit?'', ''can_review?'', and ''can_take_quiz?''.&lt;br /&gt;
&lt;br /&gt;
Integrate role-based and time-based logic under a unified interface in ''Participant#allowed_to?''.&lt;br /&gt;
&lt;br /&gt;
Additionally, verify that ''DueDatePermissions'' and ''DueDateQueries'' mixins return consistent results compared to the previous service-based approach.&lt;br /&gt;
&lt;br /&gt;
Ensure backward compatibility with existing ''Assignment'' tests.&lt;br /&gt;
&lt;br /&gt;
=== Example Case ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
describe Participant, '#allowed_to?' do&lt;br /&gt;
  it 'returns false when role allows submission but deadline is closed' do&lt;br /&gt;
    allow(assignment).to receive(:find_current_stage).and_return(due_date)&lt;br /&gt;
    allow(DeadlineRight).to receive(:find).and_return(double(name: 'No'))&lt;br /&gt;
    expect(participant.allowed_to?(:submit, assignment)).to be false&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Expected Outcome ===&lt;br /&gt;
&lt;br /&gt;
Both user roles and deadlines influence action permissions.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
=== Demo Link ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167076</id>
		<title>CSC/ECE 517 Fall 2025 - E2566. Finish DueDates</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2025_-_E2566._Finish_DueDates&amp;diff=167076"/>
		<updated>2025-11-10T23:36:21Z</updated>

		<summary type="html">&lt;p&gt;Skim253: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Introduction ==&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
&lt;br /&gt;
This project aims to complete the implementation of the DueDate system in Expertiza. The current implementation consists mostly of class methods and lacks proper definition of different deadline types and permission checking functionality. This redesign will create a more robust, readable, and maintainable due date system.&lt;br /&gt;
&lt;br /&gt;
=== Existing Components ===&lt;br /&gt;
[[File:Duedates.png|600px]]&lt;br /&gt;
[[File:Assignments-CurrentStage.png|600px]]&lt;br /&gt;
* &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model with polymorphic parent association (Assignment/SignUpTopic)&lt;br /&gt;
* &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; subclasses&lt;br /&gt;
* Basic CRUD operations (within &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;AssignmentForm&amp;lt;/code&amp;gt;) and sorting functionality (&amp;lt;code&amp;gt;deadline_sort&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Database schema with &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt; field&lt;br /&gt;
&lt;br /&gt;
=== Motivation ===&lt;br /&gt;
Currently, there are some glaring issues with how due_dates was created in the first place, including redundancy and lack of modularity.&lt;br /&gt;
&lt;br /&gt;
# No dedicated &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to define different kinds of deadlines. The existing &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; class only serves as an association for &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt; models, and is never referenced in the current &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; implementation.&lt;br /&gt;
# Overuse of class methods making the code difficult to maintain.&lt;br /&gt;
# Missing permission checking logic for user actions.&lt;br /&gt;
# Incomplete deadline type definitions — &amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt; need to be added.&lt;br /&gt;
# Duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries in &amp;lt;code&amp;gt;deadline_types&amp;lt;/code&amp;gt; table, which may lead to potential bugs.&lt;br /&gt;
# No clear separation of concerns — data persistence, business logic, and permission logic are mixed in a single model.&lt;br /&gt;
&lt;br /&gt;
=== Tasks Identified ===&lt;br /&gt;
&lt;br /&gt;
# Redesign the &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.&lt;br /&gt;
# Refactor the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model to separate persistence, business logic, and permission logic into distinct layers.&lt;br /&gt;
# Introduce instance-level methods (e.g., &amp;lt;code&amp;gt;next_due_date&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;copy_to&amp;lt;/code&amp;gt;) to replace class-level utilities and improve testability.&lt;br /&gt;
# Integrate role-based and time-based permission checks within &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt; to ensure consistent access control.&lt;br /&gt;
# Add missing deadline types (&amp;lt;code&amp;gt;teammate_review&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;quiz&amp;lt;/code&amp;gt;) and remove duplicate &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; entries for data integrity.&lt;br /&gt;
# Implement reusable mixins (e.g., &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateQueries&amp;lt;/code&amp;gt;) to handle permission evaluation and query operations.&lt;br /&gt;
# Update controllers and APIs to use the new mixin-based approach for consistent permission checks and deadline queries.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Proposed Solution ==&lt;br /&gt;
&lt;br /&gt;
=== 1. DeadlineType Model Implementation ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Although the current codebase includes a &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; model, it only serves as a passive lookup table for associations with &amp;lt;code&amp;gt;AssignmentDueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;TopicDueDate&amp;lt;/code&amp;gt;.  &lt;br /&gt;
In practice, most parts of the system still rely on hard-coded numeric IDs or manual lookups such as:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Example of current usage&lt;br /&gt;
deadline_type_id = DeadlineType.find_by(name: 'review').id&lt;br /&gt;
if due_date.deadline_type_id == deadline_type_id&lt;br /&gt;
  # Perform review-related logic&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Instead of using the model as an active domain object, &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; directly compares IDs or string literals.  &lt;br /&gt;
This leads to several structural issues:&lt;br /&gt;
&lt;br /&gt;
* Inconsistent references (some use IDs, others strings)&lt;br /&gt;
* Tight coupling between &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; and &amp;lt;code&amp;gt;deadline_type_id&amp;lt;/code&amp;gt;&lt;br /&gt;
* Lack of helper semantics (e.g., &amp;lt;code&amp;gt;is_review?&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;is_submission?&amp;lt;/code&amp;gt;)&lt;br /&gt;
* Data duplication and integrity risks (two &amp;lt;code&amp;gt;team_formation&amp;lt;/code&amp;gt; rows)&lt;br /&gt;
&lt;br /&gt;
The redesigned &amp;lt;code&amp;gt;DeadlineType&amp;lt;/code&amp;gt; will serve as a source of truth, defining all valid deadline types and providing meaningful interfaces.&lt;br /&gt;
&lt;br /&gt;
==== Database Schema ====&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;sql&amp;quot;&amp;gt;&lt;br /&gt;
CREATE TABLE deadline_types (&lt;br /&gt;
  id INT PRIMARY KEY,&lt;br /&gt;
  name VARCHAR(255) NOT NULL UNIQUE,&lt;br /&gt;
  description TEXT,&lt;br /&gt;
  created_at TIMESTAMP,&lt;br /&gt;
  updated_at TIMESTAMP&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Deadline Type Definitions ====&lt;br /&gt;
* &amp;lt;code&amp;gt;1&amp;lt;/code&amp;gt;: submission - Student work submission deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;2&amp;lt;/code&amp;gt;: review - Peer review deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;3&amp;lt;/code&amp;gt;: teammate_review - Team member evaluation deadlines (NEW)  &lt;br /&gt;
* &amp;lt;code&amp;gt;5&amp;lt;/code&amp;gt;: metareview - Meta-review deadlines (kept for backward compatibility)  &lt;br /&gt;
* &amp;lt;code&amp;gt;6&amp;lt;/code&amp;gt;: drop_topic - Topic drop deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;7&amp;lt;/code&amp;gt;: signup - Course/assignment signup deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;8&amp;lt;/code&amp;gt;: team_formation - Team formation deadlines (clean up duplicate ID 10)  &lt;br /&gt;
* &amp;lt;code&amp;gt;11&amp;lt;/code&amp;gt;: quiz - Quiz completion deadlines&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 2. DueDate Model Refactoring ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model currently mixes persistence logic, deadline calculation, and permission checking, violating SRP.  &lt;br /&gt;
Most of its logic is implemented as class methods, which makes testing and extension difficult.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Current design&lt;br /&gt;
def self.copy(old_assignment_id, new_assignment_id)&lt;br /&gt;
  duedates = where(parent_id: old_assignment_id)&lt;br /&gt;
  duedates.each do |orig_due_date|&lt;br /&gt;
    new_due_date = orig_due_date.dup&lt;br /&gt;
    new_due_date.parent_id = new_assignment_id&lt;br /&gt;
    new_due_date.save&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To address this:&lt;br /&gt;
* Replace class methods with instance-level methods.&lt;br /&gt;
* Move sorting, copying, and query logic to mixins.&lt;br /&gt;
* Introduce mixins such as &amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt; for permission-related checks.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Improved design&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
&lt;br /&gt;
  def copy_to(new_assignment)&lt;br /&gt;
    dup.tap { |d| d.parent_id = new_assignment.id }.save&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def next_due_date&lt;br /&gt;
    DueDate.where('due_at &amp;gt; ?', Time.zone.now).order(:due_at).first&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 3. Add Permission-Checking Methods ===&lt;br /&gt;
&lt;br /&gt;
==== Current Problems ====&lt;br /&gt;
&lt;br /&gt;
Currently, permission checks depend only on user roles and ignore the actual deadline.  &lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
# Role-based permissions (participant_helpers.rb)&lt;br /&gt;
def participant_permissions(authorization)&lt;br /&gt;
  case authorization&lt;br /&gt;
  when 'reader'   then { can_submit: false, can_review: true, can_take_quiz: true }&lt;br /&gt;
  when 'reviewer' then { can_submit: false, can_review: true, can_take_quiz: false }&lt;br /&gt;
  when 'submitter' then { can_submit: true, can_review: false, can_take_quiz: false }&lt;br /&gt;
  else { can_submit: true, can_review: true, can_take_quiz: true }&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And deadline-based checks exist separately in the &amp;lt;code&amp;gt;Assignment&amp;lt;/code&amp;gt; model:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
def submission_allowed(topic_id = nil)&lt;br /&gt;
  check_condition('submission_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
def can_review(topic_id = nil)&lt;br /&gt;
  check_condition('review_allowed_id', topic_id)&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These two layers (role-based and time-based) never interact, leading to inconsistencies (e.g., a user with &amp;lt;code&amp;gt;can_submit=true&amp;lt;/code&amp;gt; after deadline).&lt;br /&gt;
&lt;br /&gt;
==== Proposed Changes ====&lt;br /&gt;
&lt;br /&gt;
Integrate role-based and deadline-based checks into the &amp;lt;code&amp;gt;DueDate&amp;lt;/code&amp;gt; model (or a dedicated mixin):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDate &amp;lt; ApplicationRecord&lt;br /&gt;
  def can_submit?&lt;br /&gt;
    right = DeadlineRight.find(submission_allowed_id)&lt;br /&gt;
    right.name.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def can_review?&lt;br /&gt;
    right = DeadlineRight.find(review_allowed_id)&lt;br /&gt;
    right.name.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def can_take_quiz?&lt;br /&gt;
    right = DeadlineRight.find(quiz_allowed_id)&lt;br /&gt;
    right.name.in?(%w[OK Late])&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then unify both layers via &amp;lt;code&amp;gt;Participant&amp;lt;/code&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class Participant &amp;lt; ApplicationRecord&lt;br /&gt;
  def allowed_to?(action, assignment, topic_id = nil)&lt;br /&gt;
    due_date = assignment.find_current_stage(topic_id)&lt;br /&gt;
    return false if due_date.nil?&lt;br /&gt;
&lt;br /&gt;
    case action&lt;br /&gt;
    when :submit then can_submit &amp;amp;&amp;amp; due_date.can_submit?&lt;br /&gt;
    when :review then can_review &amp;amp;&amp;amp; due_date.can_review?&lt;br /&gt;
    when :quiz   then can_take_quiz &amp;amp;&amp;amp; due_date.can_take_quiz?&lt;br /&gt;
    else false&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This ensures that permission logic respects both the participant’s role and the current deadline phase.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 4. Permission and Query Mixins ===&lt;br /&gt;
&lt;br /&gt;
Encapsulate permission and query logic in reusable mixins instead of separate service classes.&lt;br /&gt;
This approach keeps logic close to the models and removes the need for dedicated service objects, improving maintainability and cohesion.&lt;br /&gt;
&lt;br /&gt;
==== DueDatePermissions Mixin ====&lt;br /&gt;
Permission checks that combine user role and deadline phase.&lt;br /&gt;
It is included in the Participant model for unified access control.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDatePermissions&lt;br /&gt;
  def can_submit?(user, assignment)&lt;br /&gt;
    user.participant.allowed_to?(:submit, assignment)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def can_review?(user, assignment)&lt;br /&gt;
    user.participant.allowed_to?(:review, assignment)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def can_form_teams?(user, assignment)&lt;br /&gt;
    user.participant.allowed_to?(:team_formation, assignment)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example integration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class Participant &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDatePermissions&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== DueDateQueries Mixin ====&lt;br /&gt;
Reusable query methods for upcoming and overdue deadlines.&lt;br /&gt;
It is included in the Assignment model to simplify deadline lookups.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
module DueDateQueries&lt;br /&gt;
  def upcoming_deadlines(limit: 5)&lt;br /&gt;
    due_dates&lt;br /&gt;
      .where('due_at &amp;gt; ?', Time.zone.now)&lt;br /&gt;
      .order(:due_at)&lt;br /&gt;
      .limit(limit)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def overdue_deadlines&lt;br /&gt;
    due_dates&lt;br /&gt;
      .where('due_at &amp;lt; ?', Time.zone.now)&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Example integration:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class Assignment &amp;lt; ApplicationRecord&lt;br /&gt;
  include DueDateQueries&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== 5. Controller and API Updates ===&lt;br /&gt;
&lt;br /&gt;
Controllers can now use the new unified mixins (&amp;lt;code&amp;gt;DueDatePermissions&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;DueDateQueries&amp;lt;/code&amp;gt;) &lt;br /&gt;
for permission checks and deadline queries.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
class DueDatesController &amp;lt; ApplicationController&lt;br /&gt;
  def can_perform_action&lt;br /&gt;
    action = params[:action_type].to_sym&lt;br /&gt;
    allowed = current_user.participant.public_send(&amp;quot;can_#{action}?&amp;quot;, current_user, @assignment)&lt;br /&gt;
    render json: { allowed: allowed }&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def upcoming_deadlines&lt;br /&gt;
    deadlines = @assignment.upcoming_deadlines&lt;br /&gt;
    render json: deadlines&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== New Endpoints ====&lt;br /&gt;
* &amp;lt;code&amp;gt;GET /assignments/:id/due_dates/permissions&amp;lt;/code&amp;gt; — Check user permissions for various actions  &lt;br /&gt;
* &amp;lt;code&amp;gt;GET /assignments/:id/due_dates/upcoming&amp;lt;/code&amp;gt; — Get upcoming deadlines  &lt;br /&gt;
* &amp;lt;code&amp;gt;GET /assignments/:id/due_dates/current/:action_type&amp;lt;/code&amp;gt; — Get current deadline for a specific action  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Testing Plan ==&lt;br /&gt;
&lt;br /&gt;
The current test suite already includes RSpec tests for ''Assignment'' and ''DueDate'' models.&lt;br /&gt;
These tests verify whether deadlines allow actions such as submission, review, or quiz based on ''DeadlineRight''.&lt;br /&gt;
However, they only validate time-based permissions, not user role constraints.&lt;br /&gt;
&lt;br /&gt;
=== Current Coverage ===&lt;br /&gt;
&lt;br /&gt;
'''Assignment#check_condition''' ensures correct evaluation of ''DeadlineRight'' values (''OK'', ''Late'', ''No'').&lt;br /&gt;
&lt;br /&gt;
'''submission_allowed''', '''quiz_allowed''', and '''can_review''' are tested for deadline behavior.&lt;br /&gt;
&lt;br /&gt;
No existing tests consider ''Participant'' role flags (''can_submit'', ''can_review'', ''can_take_quiz'').&lt;br /&gt;
&lt;br /&gt;
=== Planned Updates ===&lt;br /&gt;
&lt;br /&gt;
Add tests for new ''DueDate'' instance methods: ''can_submit?'', ''can_review?'', and ''can_take_quiz?''.&lt;br /&gt;
&lt;br /&gt;
Integrate role-based and time-based logic under a unified interface in ''Participant#allowed_to?''.&lt;br /&gt;
&lt;br /&gt;
Additionally, verify that ''DueDatePermissions'' and ''DueDateQueries'' mixins return consistent results compared to the previous service-based approach.&lt;br /&gt;
&lt;br /&gt;
Ensure backward compatibility with existing ''Assignment'' tests.&lt;br /&gt;
&lt;br /&gt;
=== Example Case ===&lt;br /&gt;
&amp;lt;syntaxhighlight lang=&amp;quot;ruby&amp;quot;&amp;gt;&lt;br /&gt;
describe Participant, '#allowed_to?' do&lt;br /&gt;
  it 'returns false when role allows submission but deadline is closed' do&lt;br /&gt;
    allow(assignment).to receive(:find_current_stage).and_return(due_date)&lt;br /&gt;
    allow(DeadlineRight).to receive(:find).and_return(double(name: 'No'))&lt;br /&gt;
    expect(participant.allowed_to?(:submit, assignment)).to be false&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/syntaxhighlight&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Expected Outcome ===&lt;br /&gt;
&lt;br /&gt;
Both user roles and deadlines influence action permissions.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Demo ==&lt;br /&gt;
=== Demo Video ===&lt;br /&gt;
&lt;br /&gt;
=== Demo Link ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Future Work ==&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=File:Assignments-CurrentStage.png&amp;diff=167075</id>
		<title>File:Assignments-CurrentStage.png</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=File:Assignments-CurrentStage.png&amp;diff=167075"/>
		<updated>2025-11-10T23:35:55Z</updated>

		<summary type="html">&lt;p&gt;Skim253: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Skim253</name></author>
	</entry>
</feed>