diff --git a/Gemfile b/Gemfile index 502c80e..169cbfd 100644 --- a/Gemfile +++ b/Gemfile @@ -71,3 +71,5 @@ group :test do end gem 'inline_svg', '~> 1.9' + +gem "pdf-reader", "~> 2.12" diff --git a/Gemfile.lock b/Gemfile.lock index 6ba0249..0772e33 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,6 +1,7 @@ GEM remote: https://rubygems.org/ specs: + Ascii85 (1.1.1) actioncable (7.1.3.4) actionpack (= 7.1.3.4) activesupport (= 7.1.3.4) @@ -77,6 +78,7 @@ GEM tzinfo (~> 2.0) addressable (2.8.7) public_suffix (>= 2.0.2, < 7.0) + afm (0.2.2) base64 (0.2.0) bigdecimal (3.1.8) bindex (0.8.1) @@ -105,6 +107,7 @@ GEM i18n (>= 1.8.11, < 2) globalid (1.2.1) activesupport (>= 6.1) + hashery (2.1.2) i18n (1.14.5) concurrent-ruby (~> 1.0) importmap-rails (2.0.1) @@ -158,6 +161,12 @@ GEM racc (~> 1.4) nokogiri (1.16.7-x86_64-linux) racc (~> 1.4) + pdf-reader (2.12.0) + Ascii85 (~> 1.0) + afm (~> 0.2.1) + hashery (~> 2.0) + ruby-rc4 + ttfunk psych (5.1.2) stringio public_suffix (6.0.1) @@ -213,6 +222,7 @@ GEM io-console (~> 0.5) rexml (3.3.4) strscan + ruby-rc4 (0.1.5) rubyzip (2.3.2) selenium-webdriver (4.23.0) base64 (~> 0.2) @@ -251,6 +261,8 @@ GEM railties (>= 7.0.0) thor (1.3.1) timeout (0.4.1) + ttfunk (1.8.0) + bigdecimal (~> 3.1) turbo-rails (2.0.6) actionpack (>= 6.0.0) activejob (>= 6.0.0) @@ -287,6 +299,7 @@ DEPENDENCIES importmap-rails inline_svg (~> 1.9) jbuilder + pdf-reader (~> 2.12) puma (>= 5.0) rails (~> 7.1.3, >= 7.1.3.4) redis (>= 4.0.1) diff --git a/app/models/job.rb b/app/models/job.rb index 25b786a..8a520f0 100644 --- a/app/models/job.rb +++ b/app/models/job.rb @@ -2,7 +2,7 @@ class Job < ApplicationRecord belongs_to :operator, class_name: 'User', optional: true belongs_to :costumer, class_name: 'User', optional: true - has_one_attached :pdf, dependent: :destroy + has_one_attached :pdf, dependent: :purge validates_presence_of :costumer_firstname, :costumer_lastname, :privacy_policy_accepted, :pdf validate :acceptable_pdf @@ -11,6 +11,9 @@ class Job < ApplicationRecord before_save :update_paid_at, if: :will_save_change_to_status? before_save :update_status_changed_at, if: :will_save_change_to_status? + # TODO: works only when job is created. Should move analyzer to activestorage :https://discuss.rubyonrails.org/t/active-storage-in-production-lessons-learned-and-in-depth-look-at-how-it-works/83289 + after_create_commit :analyze_pdf + # NOTE: Multiple status if paing before brinting? enum status: { open: 0, @@ -53,12 +56,10 @@ class Job < ApplicationRecord def acceptable_pdf return unless pdf.attached? - errors.add(:pdf, 'is too big') unless pdf.blob.byte_size <= 100.megabyte - acceptable_types = ['application/pdf'] - return if acceptable_types.include?(pdf.content_type) - errors.add(:pdf, 'must be a PDF') + errors.add(:pdf, 'is too big') unless pdf.blob.byte_size <= 100.megabyte + errors.add(:pdf, 'must be a PDF') unless acceptable_types.include?(pdf.content_type) end def able_to_cancel? @@ -84,4 +85,29 @@ class Job < ApplicationRecord def update_status_changed_at self.status_changed_at = Time.now end + + def analyze_pdf + # return unless pdf.attached? && pdf.new_record? + + # TODO: add any check if attachment has changed + + # pdfs.each do |pdf| + pdf.blob.open do |file| + self.number_of_plans_a0 = 0 + self.number_of_plans_a1 = 0 + self.number_of_plans_a2 = 0 + self.number_of_plans_a3 = 0 + self.costum_qm_plan = 0 + # file = ActiveStorage::Blob.service.path_for(pdf.key).to_s + pdf_analyzer = Services::PdfAnalyzer.new(file) + pdf_analyzer.analyze + self.number_of_plans_a0 += pdf_analyzer.pages_a0 + self.number_of_plans_a1 += pdf_analyzer.pages_a1 + self.number_of_plans_a2 += pdf_analyzer.pages_a2 + self.number_of_plans_a3 += pdf_analyzer.pages_a3 + self.costum_qm_plan += pdf_analyzer.costum_qm + save + end + # end + end end diff --git a/app/views/jobs/_job_tr.html.erb b/app/views/jobs/_job_tr.html.erb index 2e23249..bef53dc 100644 --- a/app/views/jobs/_job_tr.html.erb +++ b/app/views/jobs/_job_tr.html.erb @@ -5,13 +5,16 @@ <%= job.fullname %> <% if job.pdf.attached? %> - <%= job.pdf.filename %><%=number_to_human_size job.pdf.blob.byte_size%> + <%#= link_to job.pdf.filename, rails_blob_path(job.pdf, disposition: "attachment") %> + <%= link_to job.pdf.filename, job.pdf, download:true %> + <%=number_to_human_size job.pdf.blob.byte_size%> <% end %> <%= job.number_of_plans_a0 %> <%= job.number_of_plans_a1 %> <%= job.number_of_plans_a2 %> <%= job.number_of_plans_a3 %> + <%= job.costum_qm_plan.round(2) %> <%= job.status %> diff --git a/app/views/jobs/index.html.erb b/app/views/jobs/index.html.erb index abc0735..0dfc387 100644 --- a/app/views/jobs/index.html.erb +++ b/app/views/jobs/index.html.erb @@ -17,6 +17,7 @@ A1 A2 A3 + qm Status Actions diff --git a/db/seeds.rb b/db/seeds.rb index 2c7fe15..adbe0c7 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -17,18 +17,18 @@ end ['GanzWichtig.pdf', 'IchBinIn5MinDran.pdf', 'DerPlanDerImmerProblemeMacht.pdf', 'DieFarbenGefallenMirNicht.pdf', 'MachHinIchHabsEilig.pdf', 'WarumDauertDasSoLange.pdf', 'DenPlanBezahleIchNicht.pdf', 'IchWarAlsErstesDran.pdf', 'WarumIstDerPlotterDefekt.pdf', 'DasNächsteMalGeheIchWoAndersHin.pdf'].shuffle.each do |pdf| - a0 = rand(0...7) - a1 = rand(0...7) - a2 = rand(0...7) - a3 = rand(0...7) - a0.zero? || a1 = 0 && a2 = 0 && a3 = 0 - a1.zero? || a2 = 0 && a3 = 0 - a2.zero? || a3 = 0 + # a0 = rand(0...7) + # a1 = rand(0...7) + # a2 = rand(0...7) + # a3 = rand(0...7) + # a0.zero? || a1 = 0 && a2 = 0 && a3 = 0 + # a1.zero? || a2 = 0 && a3 = 0 + # a2.zero? || a3 = 0 status = %i[open open open open open open open open open open open open open open printing ready_for_pickup paid canceled].sample job = Job.new(costumer_firstname: Faker::Name.unique.first_name, costumer_lastname: Faker::Name.unique.last_name, - number_of_plans_a0: a0, number_of_plans_a1: a1, number_of_plans_a2: a2, number_of_plans_a3: a3, + # number_of_plans_a0: a0, number_of_plans_a1: a1, number_of_plans_a2: a2, number_of_plans_a3: a3, status:, privacy_policy_accepted: true) job.pdf = File.open(Rails.root.join('db/pdfs/', pdf)) job.save! diff --git a/lib/services/pdf_analyzer.rb b/lib/services/pdf_analyzer.rb new file mode 100644 index 0000000..6ba94ee --- /dev/null +++ b/lib/services/pdf_analyzer.rb @@ -0,0 +1,80 @@ +module Services + # Class to analyze PDF to extract number of pages from a0 to a3 and costum qm + class PdfAnalyzer + require 'pdf/reader' + require 'bigdecimal' + + attr_reader :costum_qm, :pages_a0, :pages_a1, :pages_a2, :pages_a3 + + def initialize(file) + @reader = PDF::Reader.new(file) + + @pages_a0 = 0 + @pages_a1 = 0 + @pages_a2 = 0 + @pages_a3 = 0 + @costum_qm = 0 + + @page_width = 0 + @page_height = 0 + end + + def analyze + @reader.pages.each do |page| + format = extract_format(page) + case format + when 'a0' + @pages_a0 += 1 + when 'a1' + @pages_a1 += 1 + when 'a2' + @pages_a2 += 1 + when 'a3' + @pages_a3 += 1 + else + @costum_qm += calculate_qm + end + end + end + + private + + def calculate_qm + (@page_width / 1000) * (@page_height / 1000) + end + + def extract_format(page) + # debugger + extract_size(page) + return 'a0' if @page_width.round == 841 && @page_height.round == 1189 + return 'a1' if @page_width.round == 594 && @page_height.round == 841 + return 'a2' if @page_width.round == 420 && @page_height.round == 594 + return 'a3' if @page_width.round == 297 && @page_height.round == 420 + + 'costum' + end + + def extract_size(page) + # debugger + bbox = page.attributes[:MediaBox] # CropBox + width = bbox[2] - bbox[0] + height = bbox[3] - bbox[1] + width_in_mm = pt2mm(width) + height_in_mm = pt2mm(height) + width_in_mm, height_in_mm = height_in_mm, width_in_mm if width_in_mm > height_in_mm + + @page_width = width_in_mm + @page_height = height_in_mm + + nil + end + + def pt2mm(pt) + (pt2in(pt) * BigDecimal('25.4')).round(2) + end + + def pt2in(pt) + (pt / BigDecimal('72')).round(2) + end + end +end