lib/canvas_sync/jobs/begin_sync_chain_job.rb



module CanvasSync
  module Jobs
    class BeginSyncChainJob < CanvasSync::Job
      attr_reader :globals

      def perform(chain_definition, globals = {})
        @globals = globals

        if globals[:updated_after] == nil && Rails.env.development?
          globals[:updated_after] = false
        end

        if globals[:updated_after] == false
          globals[:updated_after] = nil
        elsif !globals[:updated_after].present? || globals[:updated_after] == true
          last_batch = SyncBatch.where(status: 'completed', batch_genre: genre).last
          globals[:full_sync_every] ||= "sunday/2"
          globals[:updated_after] = ((last_batch.started_at - 1.day).iso8601 rescue nil)
        end

        # Refuse to run syncs of the same genre if there is a running full sync
        if last_full_sync_record&.status == 'processing' && last_full_sync > 12.hours.ago
          Rails.logger.warn("Attempted to start a '#{genre}' sync while a full-sync is still processing.")
          return
        end

        if should_full_sync?(globals[:full_sync_every])
          globals[:updated_after] = nil
        end

        sync_batch = SyncBatch.create!(
          started_at: DateTime.now,
          full_sync: globals[:updated_after] == nil,
          batch_genre: genre,
          status: 'processing',
        )

        globals[:batch_genre] = genre
        globals[:batch_start_time] = sync_batch.started_at.iso8601
        globals[:sync_batch_id] = sync_batch.id

        JobBatches::Batch.new.tap do |b|
          b.description = "CanvasSync Root Batch (SyncBatch##{sync_batch.id})"
          b.on(:complete, "#{self.class.to_s}.batch_completed", sync_batch_id: sync_batch.id)
          b.on(:success, "#{self.class.to_s}.batch_completed", sync_batch_id: sync_batch.id)
          b.context = globals
          b.jobs do
            JobBatches::SerialBatchJob.perform_now(chain_definition)
          end
          sync_batch.update(batch_bid: b.bid)
        end
      end

      def should_full_sync?(opt)
        return true unless last_full_sync.present?
        return false unless opt.is_a?(String)

        case opt.strip
        when %r{^(sunday|monday|tuesday|wednesday|thursday|friday|saturday)(?:/(\d+))?$}
          m = Regexp.last_match
          day = m[1]
          skip = m[2] || "1"
          DateTime.now.send(:"#{day}?") && last_full_sync.end_of_day <= (skip.to_i.weeks.ago.end_of_day)
        when %r{^(\d+)\%$}
          m = Regexp.last_match
          rand(100) < m[1].to_i
        when %r{^(\d+) ?days$}
          m = Regexp.last_match
          last_full_sync.end_of_day <= m[1].to_i.days.ago.end_of_day
        when %r{^(\d+)$} # N.days is converted to a string of seconds
          m = Regexp.last_match
          last_full_sync.end_of_day <= m[1].to_i.seconds.ago.end_of_day
        else
          false
        end
      end

      def last_full_sync_record
        @last_full_sync_record ||= SyncBatch.where(status: ['completed', 'processing'], full_sync: true, batch_genre: genre).last
      end

      def last_full_sync
        last_full_sync_record&.started_at
      end

      def genre
        globals[:batch_genre] || "default"
      end

      def self.batch_completed(status, options)
        sbatch = SyncBatch.find(options['sync_batch_id'])
        sbatch.update!(
          status: status.success? ? 'completed' : 'failed',
          completed_at: DateTime.now,
        )
      end
    end
  end
end