CSC/ECE 517 Fall 2025 - E2566. Finish DueDates

From Expertiza_Wiki
Jump to navigation Jump to search

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

  • DueDate model with polymorphic parent association (Assignment/SignUpTopic)
  • AssignmentDueDate and TopicDueDate subclasses
  • Basic CRUD operations (within Assignment and AssignmentForm) and sorting functionality (deadline_sort)
  • Database schema with deadline_type_id field

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

  1. No dedicated DeadlineType model to define different kinds of deadlines. The existing DeadlineType class only serves as an association for AssignmentDueDate and TopicDueDate models, and is never referenced in the current DueDate implementation.
  2. Duplicate team_formation entries in deadline_types table, which may lead to potential bugs.
  3. Incomplete deadline type definitions — teammate_review, quiz need to be added.
  4. No clear separation of concerns — data persistence, business logic, and permission logic are mixed in a single model.

Permission & Behavior Issues

  1. Overuse of class methods making the code difficult to maintain.
  2. Missing permission checking logic for user actions that combines both role-based and time-based constraints.

Tasks Identified

Modeling & Structural

  1. Refactor the DueDate model to separate persistence, business logic, and permission logic into distinct layers.
  2. Implement reusable mixins (DueDatePermissions, DueDateActions) to handle permission evaluation and deadline-related operations.
  3. Introduce instance-level methods (e.g., next_due_date, copy) to replace class-level utilities and improve testability.

Permission & Behavior

  1. Integrate role-based and time-based permission checks within DueDate and Participant to ensure consistent access control.
  2. Redesign the DeadlineType model to act as the single source of truth for all deadline categories, replacing hard-coded IDs and redundant entries.
  3. Add missing deadline types (teammate_review, quiz) and remove duplicate team_formation entries 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 DueDate and deadline_type_id
  • Lack of semantic helper methods (e.g., review?, submission?)
  • Data duplication and integrity risks (two team_formation rows)

Database Schema

deadline_types Table Structure
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

Canonical Deadline Type Entries
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

deadline_rights Table Structure
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:

Responsibility Distribution After Refactoring
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:

Permission Methods
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

Methods in DueDateActions
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

How next_due_date Works
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

  1. Separation of Concerns: Permission logic separated into dedicated modules
  2. Instance Methods: Replaced class methods with testable instance methods
  3. Polymorphic Design: Single DueDate model serves both Assignment and SignUpTopic
  4. Unified Permissions: Role-based and time-based checks combined in one interface
  5. 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 deadline
  • activity_permissible_for? – finds the most recent past deadline relative to a specific time

deadline-based navigation:

  • next_due_date
  • deadlines_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

Click to watch on YouTube

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