From 8b920779bc8aa91dc08fe4977524d284c40a097e Mon Sep 17 00:00:00 2001 From: Jeremy Friesen Date: Thu, 30 Nov 2023 14:33:30 -0500 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=A7=20Add=20incomplete=20FileSetsRepro?= =?UTF-8?q?cessJob?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit does two things: 1. Extracts a method to answer if we split a given path or not. 2. Establishes a set of jobs that will perform conditional resplitting and/or re-ingesting. Why the WIP? I'm heading into an afternoon of meetings and want to put something forward that I or another can work on later. I feel good about merging this as the jobs are not part of production code paths. Related to: - https://github.com/scientist-softserv/adventist-dl/issues/681 --- app/jobs/file_sets_reprocess_job.rb | 130 ++++++++++++++++++ .../adventist_pages_to_jpgs_splitter.rb | 15 +- spec/factories/file_sets.rb | 12 ++ spec/fixtures/latex.pdf | Bin 0 -> 33801 bytes .../adventist_pages_to_jpgs_splitter_spec.rb | 17 +++ spec/jobs/file_sets_reprocess_job_spec.rb | 19 +++ 6 files changed, 191 insertions(+), 2 deletions(-) create mode 100644 app/jobs/file_sets_reprocess_job.rb create mode 100644 spec/fixtures/latex.pdf create mode 100644 spec/jobs/file_sets_reprocess_job_spec.rb diff --git a/app/jobs/file_sets_reprocess_job.rb b/app/jobs/file_sets_reprocess_job.rb new file mode 100644 index 00000000..f0610930 --- /dev/null +++ b/app/jobs/file_sets_reprocess_job.rb @@ -0,0 +1,130 @@ +# frozen_string_literal: true + +## +# This job is responsible for finding file sets that may need re-processing and then dispatching new +# jobs to perform that processing. +# +# The reasons are two fold, and addressed by the two jobs: +# +# 1. We did not successfully split a PDF; handled by ConditionallyResplitFileSetJob +# 2. We did not successfully attach a PDF; handled by ConditionallyResplitFileSetJob +class FileSetsReprocessJob < ApplicationJob + ## + # @param cname [String, Symbol] when given :all, submit one {FileSetsReprocessJob} per tenant. + # Otherwise, switch to the given tenant and submit a {FileSetsReprocessJob} + def self.for_tenant(cname = :all) + if cname == :all + Account.all.each do |account| + account.switch! + FileSetsReprocessJob.perform_later + end + else + Account.switch!(cname) + FileSetsReprocessJob.perform_later + end + end + + class_attribute :solr_page_size, default: 1000 + class_attribute :solr_q_parameter, + default: "(mime_type_ssi:application/pdf OR label_ssi:*.pdf) AND has_model_ssim:FileSet" + class_attribute :solr_fl_parameter, default: 'id,label_ssi,mime_type_ssi' + class_attribute :desired_mime_type, default: "application/pdf" + + def perform + count = ActiveFedora::SolrService.count(solr_q_parameter) + + (0..(1 + (count / solr_page_size))).each do |page| + ActiveFedora::SolrService.query(solr_q_parameter, + fl: solr_fl_parameter, + rows: solr_page_size, + start: page * solr_page_size).each do |document| + if document[:mime_type_ssi] == desired_mime_type + # Given that we have a mime_type we can assume that we've successfully attached the file. + ConditionallyResplitFileSetJob.perform_later(file_set_id: document[:id]) + else + # We have failed to attach the file to the work. + ConditionallyReingestFileSetJob.perform_later(file_set_id: document[:id]) + end + end + end + end + + ## + # A helper module for conditionally finding a file set. + # + # @see #find + module FileSetFinder + ## + # @param file_set_id [String] + # @return [FileSet] when the given :file_set_id is found. + # @return [FalseClass] when the given :file_set_id is not found. + def self.find(file_set_id:) + FileSet.find(file_set_id) + rescue ActiveFedora::ObjectNotFoundError + message = "#{self.class}##{__method__} unable to find FileSet with ID=#{file_set_id}. " \ + "It may have been deleted between the enqueuing of this job and running this job." + Rails.logger.warning(message) + return false + end + end + + ## + # This job conditionally re-splits a file_set's PDF. How do we know if we need to re-split + # it? See the {#perform} method for details. + # + # 1. The file_set is a PDF. + # 2. The file_set's PDF is one that we would normally split. + # 3. The file_set's parent does not have child works; the assumption being that if it doesn't + # have child works, then + class ConditionallyResplitFileSetJob < ApplicationJob + ## + # @param file_set_id [String] + # + # @return [Symbol] A terse explanation of what was done with this job. + # + # @raise [ActiveFedora::ObjectNotFoundError] when the given FileSet's parent could not be found. + # rubocop:disable Metrics/LineLength + def perform(file_set_id:) + file_set = FileSetFinder.find(file_set_id: file_set_id) + + # We've logged this (see FileSetFinder.find) so we'll move along. + return :file_set_not_found unless file_set + + # When we aren't working with a PDF, let's not proceed. + return :not_a_pdf unless file_set.pdf? + + # When the PDF we are working with isn't something we split, let's bail. + return :non_splitting_pdf unless IiifPrint::SplitPdfs::AdventistPagesToJpgsSplitter.split_for?(path: file_set.label) + + parent = IiifPrint.parent_for(file_set) + + raise ActiveFedora::ObjectNotFoundError, "Expected #{file_set.class} ID=#{file_set.id} to have a parent record." unless parent + + return :parent_does_not_split unless parent.try(:iiif_print_config).try(:pdf_splitter_service) + + # When the parent has children, assume that we've already previously succeeded on splitting + # this PDF. + return :has_children if parent.child_work_ids.any? + + IiifPrint::Jobs::RequestSplitPdfJob.perform_later(file_set: file_set, user: User.batch_user) + :requesting_split + end + # rubocop:enable Metrics/LineLength + end + + ## + # + class ConditionallyReingestFileSetJob < ApplicationJob + ## + # @param file_set_id [String] + # @return [Symbol] A terse explanation of what was done with this job. + def perform(file_set_id:) + file_set = FileSetFinder.find(file_set_id: file_set_id) + + # We've logged this (see FileSetFinder.find) so we'll move along. + return :file_set_not_found unless file_set + + # TODO: The file set does not appear to have a properly attached file. + end + end +end diff --git a/lib/iiif_print/split_pdfs/adventist_pages_to_jpgs_splitter.rb b/lib/iiif_print/split_pdfs/adventist_pages_to_jpgs_splitter.rb index f9fc8700..1b5115e1 100644 --- a/lib/iiif_print/split_pdfs/adventist_pages_to_jpgs_splitter.rb +++ b/lib/iiif_print/split_pdfs/adventist_pages_to_jpgs_splitter.rb @@ -3,13 +3,24 @@ module IiifPrint module SplitPdfs module AdventistPagesToJpgsSplitter + ## + # @param path [String] the path, in particular filename (that hopefully ends with an + # extension). + # + # @param suffixes [Array] the list of suffixes that we want to ignore for splitting. + # @return [TrueClass] when we should be splitting this path. + # @return [TrueClass] when we should not be splitting this path. + def self.split_this?(path:, suffixes: CreateDerivativesJobDecorator::NON_ARCHIVAL_PDF_SUFFIXES) + suffixes.none? { |suffix| path.downcase.end_with?(suffix) } + end + ## # We do not always want to split a PDF; this provides a decision point. # # @param path [String] the path of the file we're attempting to run derivatives against. # @param args [Array] pass through args # @param splitter [IiifPrint::SplitPdfs::BaseSplitter] (for dependency injection) - # @param suffix [String] (for dependency injection) + # @param suffixes [String] (for dependency injection) # # @return [Enumerable] when we are going to skip splitting, return an empty array; otherwise return # an instance of {IiifPrint::SplitPdfs::AdventistPagesToJpgsSplitter}. @@ -20,7 +31,7 @@ def self.call(path, splitter: DerivativeRodeoSplitter, suffixes: CreateDerivativesJobDecorator::NON_ARCHIVAL_PDF_SUFFIXES, **args) - return [] if suffixes.any? { |suffix| path.downcase.end_with?(suffix) } + return [] unless AdventistPagesToJpgsSplitter.split_this?(path: path, suffixes: suffixes) splitter.call(path, **args) end diff --git a/spec/factories/file_sets.rb b/spec/factories/file_sets.rb index 606c6d2a..fc68671e 100644 --- a/spec/factories/file_sets.rb +++ b/spec/factories/file_sets.rb @@ -4,9 +4,21 @@ factory :file_set do transient do user { FactoryBot.create(:user) } + content { nil } end + after(:build) do |fs, evaluator| fs.apply_depositor_metadata evaluator.user end + + factory :file_with_work do + after(:build) do |file, _evaluator| + file.title = ['testfile'] + end + after(:create) do |file, evaluator| + Hydra::Works::UploadFileToFileSet.call(file, evaluator.content) if evaluator.content + create(:generic_work, user: evaluator.user).members << file + end + end end end diff --git a/spec/fixtures/latex.pdf b/spec/fixtures/latex.pdf new file mode 100644 index 0000000000000000000000000000000000000000..bdbe71eb957cfd0afeae58ef7f521d05498269f6 GIT binary patch literal 33801 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 AxBvhE literal 0 HcmV?d00001 diff --git a/spec/iiif_print/split_pdfs/adventist_pages_to_jpgs_splitter_spec.rb b/spec/iiif_print/split_pdfs/adventist_pages_to_jpgs_splitter_spec.rb index 352b7989..f948eda4 100644 --- a/spec/iiif_print/split_pdfs/adventist_pages_to_jpgs_splitter_spec.rb +++ b/spec/iiif_print/split_pdfs/adventist_pages_to_jpgs_splitter_spec.rb @@ -3,6 +3,23 @@ require 'spec_helper' RSpec.describe IiifPrint::SplitPdfs::AdventistPagesToJpgsSplitter do + describe '.split_this?' do + subject { described_class.split_this?(path: path) } + + [ + ["hello.jpg", true], + ["hello.reader.pdf", false], + ["hello.reader.jpg", true], + ["hello.reader.pdf.pdf", true] + ].each do |given_path, expected_value| + context "given #{given_path.inspect}" do + let(:path) { given_path } + + it { is_expected.to eq(expected_value) } + end + end + end + describe '.call' do subject { described_class.call(path, suffixes: ["spec.rb"], file_set: create(:file_set)) } diff --git a/spec/jobs/file_sets_reprocess_job_spec.rb b/spec/jobs/file_sets_reprocess_job_spec.rb new file mode 100644 index 00000000..b40a76e5 --- /dev/null +++ b/spec/jobs/file_sets_reprocess_job_spec.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe FileSetsReprocessJob, clean: true do + describe '#perform' do + let(:user) { FactoryBot.create(:user) } + let(:file_set) { FactoryBot.create(:file_with_work, content: file_content, user: user) } + let(:file_content) { File.open(fixture_path + '/latex.pdf') } + + it 'submits jobs' do + expect(described_class::ConditionallyResplitFileSetJob).to receive(:perform_later).with(file_set_id: file_set.id) + file_set + + described_class.perform_now + # Verifying that we found one record to consider resplitting. + end + end +end