learningtapestry/lcms-engine

View on GitHub
app/jobs/lcms/engine/document_generate_job.rb

Summary

Maintainability
A
0 mins
Test Coverage
F
0%
# frozen_string_literal: true

module Lcms
  module Engine
    class DocumentGenerateJob < Lcms::Engine::ApplicationJob
      include ResqueJob
      include RetryDelayed

      queue_as :default

      WAIT_FOR_JOBS = %w(
        Lcms::Engine::MaterialGenerateJob
        Lcms::Engine::MaterialGeneratePdfJob
        Lcms::Engine::MaterialGenerateGdocJob
      ).freeze

      def perform(document, check_queue: false)
        @document = document

        # Job has been queued from Material job
        if check_queue
          # Exit if any material is still generating
          return if materials_generating?
        elsif document.materials.any?
          # Queue all materials at the first time
          create_gdoc_folders
          return queue_materials
        end

        # If came here:
        # - all materials have been generated
        # - document doesn't have any materials at all
        #
        # So need to check if such job is already running or has been already queued.
        # And return in such case
        return if queued?

        if document.math?
          document.document_parts.default.each { |p| p.update!(content: EmbedEquations.call(p.content)) }
        end

        queue_documents
      end

      private

      attr_accessor :document

      def create_gdoc_folders
        return unless DocTemplate.document_contexts.include?('gdoc') || DocTemplate.material_contexts.include?('gdoc')

        DocumentExporter::Gdoc::Base.new(document).create_gdoc_folders("#{document.id}_v#{document.version}")
      end

      #
      # Checks if there are jobs queued or running for current document
      # and any of its materials
      #
      def materials_generating? # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
        document.materials.each do |material|
          queued = Resque.peek(queue_name, 0, 0)
                     .map { |job| job['args'].first }
                     .detect { |job| same_material?(job, material.id) }

          queued ||=
            Resque::Worker.working.map(&:job).detect do |job|
              next unless job.is_a?(Hash) && (args = job.dig 'payload', 'args').is_a?(Array)

              args.detect { |x| same_material?(x, material.id) }
            end

          return true if queued
        end
        false
      end

      def same_document?(job, type, klass)
        job['job_class'] == klass &&
          job['arguments'].first['_aj_globalid'].index("gid://content/Document/#{document.id}") &&
          job['arguments'].second['content_type'] == type
      end

      def same_material?(job, id)
        WAIT_FOR_JOBS.include?(job['job_class']) &&
          job['arguments'].first['_aj_globalid'].index("gid://content/Material/#{id}") &&
          job['arguments'].second['_aj_globalid'].index("gid://content/Document/#{document.id}")
      end

      def same_self?(job)
        job['job_class'] == self.class.name && job['job_id'] != job_id &&
          job['arguments'].first.try(:[], '_aj_globalid') == "gid://content/Document/#{document.id}"
      end

      def queue_documents
        DocumentGenerator::CONTENT_TYPES.each do |type|
          DocumentGenerator.document_generators.each do |klass|
            next if queued_or_running?(type, klass)

            klass.constantize.perform_later document, content_type: type
          end
        end
      end

      def queue_materials
        document.materials.each { |material| Lcms::Engine::MaterialGenerateJob.perform_later(material, document) }
      end

      def queued?
        queued = Resque.peek(queue_name, 0, 0).map { |job| job['args'].first }.detect { |job| same_self?(job) }

        queued || Resque::Worker.working.map(&:job).detect do |job|
          next unless job.is_a?(Hash) && (args = job.dig 'payload', 'args').is_a?(Array)

          args.detect { |x| same_self?(x) }
        end
      end

      def queued_or_running?(type, klass)
        queued = Resque.peek(queue_name, 0, 0)
                   .map { |job| job['args'].first }
                   .detect { |job| same_document?(job, type, klass) }

        queued ||
          Resque::Worker.working.map(&:job).detect do |job|
            next unless job.is_a?(Hash) && (args = job.dig 'payload', 'args').is_a?(Array)

            args.detect { |x| same_document?(x, type, klass) }
          end
      end
    end
  end
end