From 3a41bd5b20174fc835379a2456a3bc9279a44246 Mon Sep 17 00:00:00 2001 From: starswan Date: Mon, 25 Nov 2024 13:37:11 +0000 Subject: [PATCH 1/2] Cache PDF on job submission, and download it if available --- Gemfile | 1 + Gemfile.lock | 12 ++++++--- .../vacancies/job_applications_controller.rb | 26 +++++++++++++------ app/jobs/make_job_application_pdf_job.rb | 13 ++++++++++ app/models/job_application.rb | 3 +++ config/application.rb | 2 ++ lib/tasks/backfill_application_pdfs.rake | 11 ++++++++ .../sidekiq_scheduled_jobs_spec.rb | 1 + .../jobs/make_job_application_pdf_job_spec.rb | 10 +++++++ .../vacancies/job_applications_spec.rb | 20 ++++++++++++++ 10 files changed, 88 insertions(+), 11 deletions(-) create mode 100644 app/jobs/make_job_application_pdf_job.rb create mode 100644 lib/tasks/backfill_application_pdfs.rake create mode 100644 spec/jobs/make_job_application_pdf_job_spec.rb diff --git a/Gemfile b/Gemfile index 2cb08b05dd..c5d36549da 100644 --- a/Gemfile +++ b/Gemfile @@ -38,6 +38,7 @@ gem "govuk-components", "~> 5.7.1" gem "govuk_design_system_formbuilder", "~> 5.7.1" gem "high_voltage" gem "httparty" +gem "image_processing", "~> 1.2" gem "ipaddr" gem "jbuilder" gem "jwt" diff --git a/Gemfile.lock b/Gemfile.lock index 690b2e18fe..625a60f3c8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -226,8 +226,7 @@ GEM faraday-net_http (3.3.0) net-http fastimage (2.3.1) - ffi (1.17.0-arm64-darwin) - ffi (1.17.0-x86_64-linux-gnu) + ffi (1.16.3) friendly_id (5.5.1) activerecord (>= 4.0.0) front_matter_parser (1.0.1) @@ -296,6 +295,9 @@ GEM httpclient (2.8.3) i18n (1.14.6) concurrent-ruby (~> 1.0) + image_processing (1.13.0) + mini_magick (>= 4.9.5, < 5) + ruby-vips (>= 2.0.17, < 3) inflection (1.0.0) io-console (0.7.2) ipaddr (1.2.7) @@ -353,7 +355,7 @@ GEM mimemagic (0.4.3) nokogiri (~> 1) rake - mini_magick (5.0.1) + mini_magick (4.13.2) mini_mime (1.1.5) minitest (5.25.1) multi_json (1.15.0) @@ -618,6 +620,9 @@ GEM rubocop-rspec (~> 3, >= 3.0.1) ruby-progressbar (1.13.0) ruby-rc4 (0.1.5) + ruby-vips (2.2.2) + ffi (~> 1.12) + logger rubyzip (2.3.2) sanitize (6.1.3) crass (~> 1.0.2) @@ -812,6 +817,7 @@ DEPENDENCIES govuk_design_system_formbuilder (~> 5.7.1) high_voltage httparty + image_processing (~> 1.2) ipaddr jbuilder jsbundling-rails diff --git a/app/controllers/publishers/vacancies/job_applications_controller.rb b/app/controllers/publishers/vacancies/job_applications_controller.rb index 6395b73888..3e31cae89c 100644 --- a/app/controllers/publishers/vacancies/job_applications_controller.rb +++ b/app/controllers/publishers/vacancies/job_applications_controller.rb @@ -23,14 +23,24 @@ def show end def download_pdf - pdf = JobApplicationPdfGenerator.new(job_application, vacancy).generate - - send_data( - pdf.render, - filename: "job_application_#{job_application.id}.pdf", - type: "application/pdf", - disposition: "inline", - ) + if job_application.pdf_version.attached? + send_data( + job_application.pdf_version.attachment.download, + filename: "job_application_#{job_application.id}.pdf", + type: "application/pdf", + disposition: "inline", + ) + else + pdf = JobApplicationPdfGenerator.new(job_application, vacancy).generate + pdf_data = pdf.render + + send_data( + pdf_data, + filename: "job_application_#{job_application.id}.pdf", + type: "application/pdf", + disposition: "inline", + ) + end end def update_status diff --git a/app/jobs/make_job_application_pdf_job.rb b/app/jobs/make_job_application_pdf_job.rb new file mode 100644 index 0000000000..e9fd579072 --- /dev/null +++ b/app/jobs/make_job_application_pdf_job.rb @@ -0,0 +1,13 @@ +class MakeJobApplicationPdfJob < ApplicationJob + queue_as :low + + def perform(job_application) + pdf = JobApplicationPdfGenerator.new(job_application, job_application.vacancy).generate + pdf_data = pdf.render + + job_application.pdf_version.attach(io: StringIO.open(pdf_data), + filename: "job_application_#{job_application.id}.pdf", + identify: false, + content_type: "application/pdf") + end +end diff --git a/app/models/job_application.rb b/app/models/job_application.rb index 1e2ccdeb58..04026936d5 100644 --- a/app/models/job_application.rb +++ b/app/models/job_application.rb @@ -58,6 +58,8 @@ class JobApplication < ApplicationRecord has_many :feedbacks, dependent: :destroy, inverse_of: :job_application + has_one_attached :pdf_version, service: :amazon_s3_documents + has_noticed_notifications scope :submitted_yesterday, -> { submitted.where("DATE(submitted_at) = ?", Date.yesterday) } @@ -80,6 +82,7 @@ def submit! submitted! Publishers::JobApplicationReceivedNotifier.with(vacancy: vacancy, job_application: self).deliver(vacancy.publisher) Jobseekers::JobApplicationMailer.application_submitted(self).deliver_later + MakeJobApplicationPdfJob.perform_later self end def for_a_teaching_role? diff --git a/config/application.rb b/config/application.rb index bc261bdbe2..e3057f02cb 100644 --- a/config/application.rb +++ b/config/application.rb @@ -57,6 +57,8 @@ class Application < Rails::Application config.active_storage.routes_prefix = "/attachments" config.active_storage.resolve_model_to_route = :rails_storage_proxy + # avoid default of :vips for now - we already have mini_magick + config.active_storage.variant_processor = :mini_magick config.log_level = ENV.fetch("RAILS_LOG_LEVEL", "info").to_sym diff --git a/lib/tasks/backfill_application_pdfs.rake b/lib/tasks/backfill_application_pdfs.rake new file mode 100644 index 0000000000..d1c6970524 --- /dev/null +++ b/lib/tasks/backfill_application_pdfs.rake @@ -0,0 +1,11 @@ +namespace :vacancy do + desc "backfill all submitted applications to have a PDF version" + + task backfill_application_pdfs: :environment do + Vacancy.expires_within_data_access_period.find_each do |vacancy| + vacancy.job_applications.after_submission.reject { |ja| ja.pdf_version.attached? }.each do |job_application| + MakeJobApplicationPdfJob.perform_later job_application + end + end + end +end diff --git a/spec/configuration/sidekiq_scheduled_jobs_spec.rb b/spec/configuration/sidekiq_scheduled_jobs_spec.rb index 4354e7a287..0d93d1f003 100644 --- a/spec/configuration/sidekiq_scheduled_jobs_spec.rb +++ b/spec/configuration/sidekiq_scheduled_jobs_spec.rb @@ -26,6 +26,7 @@ SetOrganisationSlugsJob SetOrganisationSlugsOfBatchJob ImportFromVacancySourceJob + MakeJobApplicationPdfJob ] end diff --git a/spec/jobs/make_job_application_pdf_job_spec.rb b/spec/jobs/make_job_application_pdf_job_spec.rb new file mode 100644 index 0000000000..f52dfe3fc1 --- /dev/null +++ b/spec/jobs/make_job_application_pdf_job_spec.rb @@ -0,0 +1,10 @@ +require "rails_helper" + +RSpec.describe MakeJobApplicationPdfJob do + let(:job_application) { create(:job_application, :status_submitted) } + + it "produces a PDF when called" do + described_class.perform_now job_application + expect(job_application.pdf_version).not_to be_nil + end +end diff --git a/spec/requests/publishers/vacancies/job_applications_spec.rb b/spec/requests/publishers/vacancies/job_applications_spec.rb index 61bce64e85..fbdc62dd77 100644 --- a/spec/requests/publishers/vacancies/job_applications_spec.rb +++ b/spec/requests/publishers/vacancies/job_applications_spec.rb @@ -107,6 +107,26 @@ end end + describe "download pdf" do + context "without cache" do + it "allows the PDF to be downloaded" do + get(organisation_job_job_application_download_pdf_path(vacancy.id, job_application.id)) + expect(response.body).to satisfy { |body| body.size > 128000 } + end + end + + context "with cache" do + before do + MakeJobApplicationPdfJob.perform_now job_application + end + + it "allows the PDF to be downloaded" do + get(organisation_job_job_application_download_pdf_path(vacancy.id, job_application.id)) + expect(response.body).to satisfy { |body| body.size > 128000 } + end + end + end + describe "GET #index" do context "when the vacancy does not belong to the current organisation" do let(:vacancy) { create(:vacancy, :published, organisations: [build(:school)]) } From 91074e6e35ac0ba2361485f4e1109ff25d1d144a Mon Sep 17 00:00:00 2001 From: starswan Date: Mon, 25 Nov 2024 14:03:41 +0000 Subject: [PATCH 2/2] linter --- .../publishers/vacancies/job_applications_controller.rb | 4 ++-- spec/requests/publishers/vacancies/job_applications_spec.rb | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/controllers/publishers/vacancies/job_applications_controller.rb b/app/controllers/publishers/vacancies/job_applications_controller.rb index 3e31cae89c..9924ea0fb4 100644 --- a/app/controllers/publishers/vacancies/job_applications_controller.rb +++ b/app/controllers/publishers/vacancies/job_applications_controller.rb @@ -29,7 +29,7 @@ def download_pdf filename: "job_application_#{job_application.id}.pdf", type: "application/pdf", disposition: "inline", - ) + ) else pdf = JobApplicationPdfGenerator.new(job_application, vacancy).generate pdf_data = pdf.render @@ -39,7 +39,7 @@ def download_pdf filename: "job_application_#{job_application.id}.pdf", type: "application/pdf", disposition: "inline", - ) + ) end end diff --git a/spec/requests/publishers/vacancies/job_applications_spec.rb b/spec/requests/publishers/vacancies/job_applications_spec.rb index fbdc62dd77..04eac0d157 100644 --- a/spec/requests/publishers/vacancies/job_applications_spec.rb +++ b/spec/requests/publishers/vacancies/job_applications_spec.rb @@ -111,7 +111,7 @@ context "without cache" do it "allows the PDF to be downloaded" do get(organisation_job_job_application_download_pdf_path(vacancy.id, job_application.id)) - expect(response.body).to satisfy { |body| body.size > 128000 } + expect(response.body).to(satisfy { |body| body.size > 128_000 }) end end @@ -122,7 +122,7 @@ it "allows the PDF to be downloaded" do get(organisation_job_job_application_download_pdf_path(vacancy.id, job_application.id)) - expect(response.body).to satisfy { |body| body.size > 128000 } + expect(response.body).to(satisfy { |body| body.size > 128_000 }) end end end