Class: CourseSyncBatch

Inherits:
ApplicationRecord show all
Defined in:
app/models/course_sync_batch.rb

Overview

Tracks a bulk enrollments content sync operation.

Lifecycle:
pending -> processing -> completed
-> failed

On first GET, the controller shows a preview and creates a batch record
with status: pending to collect the confirmation token. The view renders
two-stage confirmation if total > BULK_SYNC_THRESHOLD.

POST with matching batch_id and confirmation_state progresses the batch
to processing, applies assignments, and marks it completed/failed.

== Schema Information

Table name: course_sync_batches
Database name: primary

id :bigint not null, primary key
course_id :bigint not null
status :string default("pending"), not null
total_topics_to_assign :integer not null
affected_enrollments :integer not null
assigned_count :integer default(0), not null
failed_count :integer default(0), not null
affected_records :jsonb
metadata :jsonb
created_at :datetime not null
updated_at :datetime not null
processed_at :datetime
completed_at :datetime

Indexes

index_course_sync_batches_on_course_id (course_id)
index_course_sync_batches_on_status (status)

Foreign Keys

fk_rails_... (course_id => courses.id)

Constant Summary collapse

STATUSES =

Statuses.

%w[pending processing completed failed].freeze
BULK_SYNC_THRESHOLD =

Require second confirmation for >100 assignments

100

Constants included from Models::Schedulable

Models::Schedulable::SIMPLE_FORM_OPTIONS

Instance Attribute Summary collapse

Belongs to collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from ApplicationRecord

ransackable_associations, ransackable_attributes, ransackable_scopes, ransortable_attributes, #to_relation

Methods included from Models::Schedulable

config

Methods included from Models::AfterCommittable

#after_commit

Methods included from Models::EventPublishable

#publish_event

Instance Attribute Details

#affected_enrollmentsObject (readonly)

Validates total topics to assign, affected enrollments.

Validations:



57
# File 'app/models/course_sync_batch.rb', line 57

validates :total_topics_to_assign, :affected_enrollments, presence: true, numericality: { only_integer: true, greater_than_or_equal_to: 0 }

#course_idObject (readonly)

Validates course id.

Validations:



53
# File 'app/models/course_sync_batch.rb', line 53

validates :course_id, presence: true

#statusObject (readonly)

Validates status.

Validations:



55
# File 'app/models/course_sync_batch.rb', line 55

validates :status, inclusion: { in: STATUSES }, presence: true

#total_topics_to_assignObject (readonly)

Validates total topics to assign, affected enrollments.

Validations:



57
# File 'app/models/course_sync_batch.rb', line 57

validates :total_topics_to_assign, :affected_enrollments, presence: true, numericality: { only_integer: true, greater_than_or_equal_to: 0 }

Class Method Details

.activeActiveRecord::Relation<CourseSyncBatch>

A relation of CourseSyncBatches that are active. Active Record Scope

Returns:

See Also:



63
# File 'app/models/course_sync_batch.rb', line 63

scope :active, -> { where(status: %w[pending processing]) }

.completedActiveRecord::Relation<CourseSyncBatch>

A relation of CourseSyncBatches that are completed. Active Record Scope

Returns:

See Also:



61
# File 'app/models/course_sync_batch.rb', line 61

scope :completed, -> { where(status: 'completed') }

.failedActiveRecord::Relation<CourseSyncBatch>

A relation of CourseSyncBatches that are failed. Active Record Scope

Returns:

See Also:



62
# File 'app/models/course_sync_batch.rb', line 62

scope :failed, -> { where(status: 'failed') }

.pendingActiveRecord::Relation<CourseSyncBatch>

A relation of CourseSyncBatches that are pending. Active Record Scope

Returns:

See Also:



59
# File 'app/models/course_sync_batch.rb', line 59

scope :pending, -> { where(status: 'pending') }

.processingActiveRecord::Relation<CourseSyncBatch>

A relation of CourseSyncBatches that are processing. Active Record Scope

Returns:

See Also:



60
# File 'app/models/course_sync_batch.rb', line 60

scope :processing, -> { where(status: 'processing') }

Instance Method Details

#completed?Boolean

Returns whether the record completed.

Returns:

  • (Boolean)

    whether the record completed



70
71
# File 'app/models/course_sync_batch.rb', line 70

def completed?    = status == 'completed'
# @return [Boolean] whether the record failed

#courseCourse

Returns the course this record belongs to.

Returns:

  • (Course)

    the course this record belongs to



50
# File 'app/models/course_sync_batch.rb', line 50

belongs_to :course

#failed?Boolean

Returns whether the record failed.

Returns:

  • (Boolean)

    whether the record failed



72
# File 'app/models/course_sync_batch.rb', line 72

def failed?       = status == 'failed'

#large_batch?Boolean

Returns whether the record large batch.

Returns:

  • (Boolean)

    whether the record large batch



75
76
77
# File 'app/models/course_sync_batch.rb', line 75

def large_batch?
  total_topics_to_assign > BULK_SYNC_THRESHOLD
end

#mark_completed!(assigned: 0, failed: 0) ⇒ Object

Mark completed, raising on failure.

Parameters:

  • assigned (Object) (defaults to: 0)

    the assigned

  • failed (Object) (defaults to: 0)

    the failed



87
88
89
90
91
92
93
94
# File 'app/models/course_sync_batch.rb', line 87

def mark_completed!(assigned: 0, failed: 0)
  update!(
    status: 'completed',
    assigned_count: assigned,
    failed_count: failed,
    completed_at: Time.current
  )
end

#mark_failed!(error_message) ⇒ Object

Mark failed, raising on failure.

Parameters:

  • error_message (Object)

    the error message



98
99
100
101
102
103
104
# File 'app/models/course_sync_batch.rb', line 98

def mark_failed!(error_message)
  update!(
    status: 'failed',
    completed_at: Time.current,
    metadata: .merge('error' => error_message)
  )
end

#mark_processing!Object

Mark processing, raising on failure.



80
81
82
# File 'app/models/course_sync_batch.rb', line 80

def mark_processing!
  update!(status: 'processing', processed_at: Time.current)
end

#pending?Boolean

Returns whether the record pending.

Returns:

  • (Boolean)

    whether the record pending



66
67
# File 'app/models/course_sync_batch.rb', line 66

def pending?      = status == 'pending'
# @return [Boolean] whether the record processing

#processing?Boolean

Returns whether the record processing.

Returns:

  • (Boolean)

    whether the record processing



68
69
# File 'app/models/course_sync_batch.rb', line 68

def processing?   = status == 'processing'
# @return [Boolean] whether the record completed

#store_affected_records!(additions_map) ⇒ Object

Stores the affected records (enrollment_id -> [topic_ids]) for potential
rollback operations. Called before applying changes.

Parameters:

  • additions_map (Object)

    the additions map



109
110
111
112
113
114
115
116
117
# File 'app/models/course_sync_batch.rb', line 109

def store_affected_records!(additions_map)
  # additions_map: { enrollment => [topics] }
  # Transform to: { enrollment_id => [topic_ids] } for JSON storage
  records = {}
  additions_map.each do |enrollment, topics|
    records[enrollment.id.to_s] = topics.map(&:id)
  end
  update!(affected_records: records)
end