Added pdf analyzer
Analyzer should be moved to ActiveStorage::Analyzer First there are many bugs with ActiveStorage that needs to be fixed: Files are not deleted if job is destroyed (dependent: :purge is set) If a new file is uploaded the old file is not deleted (has_one_attechment)
This commit is contained in:
2
Gemfile
2
Gemfile
@@ -71,3 +71,5 @@ group :test do
|
||||
end
|
||||
|
||||
gem 'inline_svg', '~> 1.9'
|
||||
|
||||
gem "pdf-reader", "~> 2.12"
|
||||
|
||||
13
Gemfile.lock
13
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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -5,13 +5,16 @@
|
||||
<td class="p-3 text-sm text-hsrm-gray whitespace-nowrap"> <%= job.fullname %> </td>
|
||||
<td class="p-3 text-sm text-hsrm-gray whitespace-nowrap">
|
||||
<% if job.pdf.attached? %>
|
||||
<%= job.pdf.filename %><span class="p-1.5 bg-gray-300 font-medium rounded-lg ml-2"><%=number_to_human_size job.pdf.blob.byte_size%></span>
|
||||
<%#= link_to job.pdf.filename, rails_blob_path(job.pdf, disposition: "attachment") %>
|
||||
<%= link_to job.pdf.filename, job.pdf, download:true %>
|
||||
<span class="p-1.5 bg-gray-300 font-medium rounded-lg ml-2"><%=number_to_human_size job.pdf.blob.byte_size%></span>
|
||||
<% end %>
|
||||
</td>
|
||||
<td class="p-3 text-sm text-left text-hsrm-gray whitespace-nowrap"><span class="p-1.5 bg-gray-300 bg-opacity-50 font-medium rounded-lg"><%= job.number_of_plans_a0 %></span></td>
|
||||
<td class="p-3 text-sm text-left text-hsrm-gray whitespace-nowrap"><span class="p-1.5 bg-gray-300 bg-opacity-50 font-medium rounded-lg"><%= job.number_of_plans_a1 %></span></td>
|
||||
<td class="p-3 text-sm text-left text-hsrm-gray whitespace-nowrap"><span class="p-1.5 bg-gray-300 bg-opacity-50 font-medium rounded-lg"><%= job.number_of_plans_a2 %></span></td>
|
||||
<td class="p-3 text-sm text-left text-hsrm-gray whitespace-nowrap"><span class="p-1.5 bg-gray-300 bg-opacity-50 font-medium rounded-lg"><%= job.number_of_plans_a3 %></span></td>
|
||||
<td class="p-3 text-sm text-left text-hsrm-gray whitespace-nowrap"><span class="p-1.5 bg-gray-300 bg-opacity-50 font-medium rounded-lg"><%= job.costum_qm_plan.round(2) %></span></td>
|
||||
<td class="p-3 text-sm text-left text-hsrm-gray whitespace-nowrap">
|
||||
<span class="p-1.5 text-xs font-medium uppercase tracking-wider bg-opacity-50 text-status-<%= job.status.to_sym %> bg-status-<%= job.status %>-light rounded-lg"><%= job.status %></span>
|
||||
</td>
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
<th class="p-3 text-sm font-semibold tracking-wide text-left w-1"> A1 </th>
|
||||
<th class="p-3 text-sm font-semibold tracking-wide text-left w-1"> A2 </th>
|
||||
<th class="p-3 text-sm font-semibold tracking-wide text-left w-1"> A3 </th>
|
||||
<th class="p-3 text-sm font-semibold tracking-wide text-left w-1"> qm </th>
|
||||
<th class="p-3 text-sm font-semibold tracking-wide text-left w-1"> Status </th>
|
||||
<th class="p-3 text-sm font-semibold tracking-wide text-left w-1"> Actions </th>
|
||||
</tr>
|
||||
|
||||
16
db/seeds.rb
16
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!
|
||||
|
||||
80
lib/services/pdf_analyzer.rb
Normal file
80
lib/services/pdf_analyzer.rb
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user