You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
docuseal/app/models/cohort.rb

112 lines
3.6 KiB

# frozen_string_literal: true
# == Schema Information
#
# Table name: cohorts
#
# id :bigint not null, primary key
# cohort_metadata :jsonb
# deleted_at :datetime
# finalized_at :datetime
# name :string not null
# program_type :string not null
# required_student_uploads :jsonb
# sponsor_completed_at :datetime
# sponsor_email :string not null
# status :string default("draft")
# students_completed_at :datetime
# tp_signed_at :datetime
# created_at :datetime not null
# updated_at :datetime not null
# institution_id :bigint not null
# template_id :bigint not null
#
# Indexes
#
# index_cohorts_on_institution_id (institution_id)
# index_cohorts_on_institution_id_and_status (institution_id,status)
# index_cohorts_on_sponsor_email (sponsor_email)
# index_cohorts_on_template_id (template_id)
#
# Foreign Keys
#
# fk_rails_... (institution_id => institutions.id)
# fk_rails_... (template_id => templates.id)
#
class Cohort < ApplicationRecord
include SoftDeletable
include AASM
# Strip whitespace from string attributes
strip_attributes only: %i[name program_type sponsor_email]
# Associations
belongs_to :institution
belongs_to :template
has_many :cohort_enrollments, dependent: :destroy
has_many :submissions, through: :cohort_enrollments
# Validations
validates :name, presence: true
validates :program_type, presence: true, inclusion: { in: %w[learnership internship candidacy] }
validates :sponsor_email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP }
validates :status, inclusion: { in: %w[draft active completed] }
# Scopes
# Note: 'active' scope is provided by SoftDeletable concern
scope :draft, -> { where(status: 'draft') }
scope :active_status, -> { where(status: 'active') }
scope :completed, -> { where(status: 'completed') }
scope :ready_for_sponsor, -> { where(status: 'active').where.not(students_completed_at: nil) }
# State Machine (Basic 3-state version for Story 1.2)
# Enhanced 7-state machine will be implemented in Story 2.2
aasm column: :status do
state :draft, initial: true
state :active
state :completed
# Transition from draft to active (TP signs)
event :activate do
transitions from: :draft, to: :active, after: :mark_tp_signed
end
# Transition from active to completed (all phases done)
event :complete do
transitions from: :active, to: :completed, after: :mark_finalized
end
end
# Check if all students have completed their submissions
# @return [Boolean] true if all student enrollments are completed
def all_students_completed?
return false if cohort_enrollments.students.empty?
cohort_enrollments.students.all?(&:completed?)
end
# Check if sponsor access is ready (TP signed and students completed)
# @return [Boolean] true if ready for sponsor review
def sponsor_access_ready?
active? && tp_signed_at.present? && all_students_completed?
end
# Check if TP can sign (cohort is in draft state)
# @return [Boolean] true if TP can sign
def tp_can_sign?
draft?
end
private
# Callback: Mark TP signing timestamp
def mark_tp_signed
update_column(:tp_signed_at, Time.current)
end
# Callback: Mark finalization timestamp
def mark_finalized
update_column(:finalized_at, Time.current)
end
end