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







