Skip to content

Commit

Permalink
Merge pull request #3177 from archivesspace/ANW-1075
Browse files Browse the repository at this point in the history
ANW-1075: Better error messages for PUI PDF errors
  • Loading branch information
thimios committed May 3, 2024
2 parents eab2802 + d933f96 commit 6370c77
Show file tree
Hide file tree
Showing 2 changed files with 161 additions and 49 deletions.
192 changes: 143 additions & 49 deletions public/app/models/finding_aid_pdf.rb
@@ -1,5 +1,12 @@
require 'tempfile'

class PDFRenderErrorHeader < StandardError; end
class PDFRenderErrorTitlePage < StandardError; end
class PDFRenderErrorTOC < StandardError; end
class PDFRenderErrorResource < StandardError; end
class PDFRenderErrorArchivalObject < StandardError; end
class PDFRenderErrorFooter < StandardError; end

class FindingAidPDF

DEPTH_1_LEVELS = ['collection', 'recordgrp', 'series']
Expand Down Expand Up @@ -35,81 +42,168 @@ def short_title
end

def source_file
# We'll use the original controller so we can find and render the PDF
# partials, but just for its ERB rendering.
renderer = PdfController.new
start_time = Time.now

@repo_code = @resource.repository_information.fetch('top').fetch('repo_code')
begin
# We'll use the original controller so we can find and render the PDF
# partials, but just for its ERB rendering.
renderer = PdfController.new
start_time = Time.now

# .length == 1 would be just the resource itself.
has_children = @ordered_records.entries.length > 1
@repo_code = @resource.repository_information.fetch('top').fetch('repo_code')

out_html = Tempfile.new
# .length == 1 would be just the resource itself.
has_children = @ordered_records.entries.length > 1

# Use a NokogiriPushParser-based
writer = Nokogiri::XML::SAX::PushParser.new(XMLCleaner.new(out_html))
writer.write(renderer.render_to_string partial: 'header', layout: false, :locals => {:record => @resource})
out_html = Tempfile.new

writer.write(renderer.render_to_string partial: 'titlepage', layout: false, :locals => {:record => @resource})
# Use a NokogiriPushParser-based
writer = Nokogiri::XML::SAX::PushParser.new(XMLCleaner.new(out_html))

# Drop the resource and filter the AOs
toc_aos = @ordered_records.entries.drop(1).select {|entry|
if entry.depth == 1
DEPTH_1_LEVELS.include?(entry.level)
elsif entry.depth == 2
DEPTH_2_LEVELS.include?(entry.level)
else
false
begin
writer.write(renderer.render_to_string partial: 'header', layout: false, :locals => {:record => @resource})
rescue => e
raise PDFRenderErrorHeader, "#{e.class};#{e.message}"
end
}

writer.write(renderer.render_to_string partial: 'toc', layout: false, :locals => {:resource => @resource, :has_children => has_children, :ordered_aos => toc_aos})
begin
writer.write(renderer.render_to_string partial: 'titlepage', layout: false, :locals => {:record => @resource})
rescue => e
raise PDFRenderErrorTitlePage, "#{e.class};#{e.message}"
end

writer.write(renderer.render_to_string partial: 'resource', layout: false, :locals => {:record => @resource, :has_children => has_children})
# Drop the resource and filter the AOs
toc_aos = @ordered_records.entries.drop(1).select {|entry|
if entry.depth == 1
DEPTH_1_LEVELS.include?(entry.level)
elsif entry.depth == 2
DEPTH_2_LEVELS.include?(entry.level)
else
false
end
}

page_size = 50
begin
writer.write(renderer.render_to_string partial: 'toc', layout: false, :locals => {:resource => @resource, :has_children => has_children, :ordered_aos => toc_aos})
rescue => e
raise PDFRenderErrorTOC, "#{e.class};#{e.message}"
end

@ordered_records.entries.drop(1).each_slice(page_size) do |entry_set|
if AppConfig[:pui_pdf_timeout] && AppConfig[:pui_pdf_timeout] > 0 && (Time.now.to_i - start_time.to_i) >= AppConfig[:pui_pdf_timeout]
raise TimeoutError.new("PDF generation timed out. Sorry!")
begin
writer.write(renderer.render_to_string partial: 'resource', layout: false, :locals => {:record => @resource, :has_children => has_children})
rescue => e
raise PDFRenderErrorResource, "#{e.class};#{e.message}"
end

uri_set = entry_set.map(&:uri)
record_set = archivesspace.search_records(uri_set, {}, true).records
page_size = 50

@ordered_records.entries.drop(1).each_slice(page_size) do |entry_set|
if AppConfig[:pui_pdf_timeout] && AppConfig[:pui_pdf_timeout] > 0 && (Time.now.to_i - start_time.to_i) >= AppConfig[:pui_pdf_timeout]
raise TimeoutError.new("PDF generation timed out. Sorry!")
end

unprocessed_record_list = record_set.zip(entry_set)
ao_list = []
uri_set = entry_set.map(&:uri)
record_set = archivesspace.search_records(uri_set, {}, true).records

# tuple looks like [ArchivalObject, Entry]
unprocessed_record_list.each_with_index do |tuple, i|
record = tuple[0]
next_record = unprocessed_record_list[i + 1][0] rescue nil

next unless record.is_a?(ArchivalObject)
unprocessed_record_list = record_set.zip(entry_set)
ao_list = []

if next_record && record.uri == next_record.parent_for_md_mapping
has_children = true
else
has_children = false
# tuple looks like [ArchivalObject, Entry]
unprocessed_record_list.each_with_index do |tuple, i|
record = tuple[0]
next_record = unprocessed_record_list[i + 1][0] rescue nil

next unless record.is_a?(ArchivalObject)

if next_record && record.uri == next_record.parent_for_md_mapping
has_children = true
else
has_children = false
end

tuple[2] = has_children

ao_list.push(tuple)
end

tuple[2] = has_children

ao_list.push(tuple)
ao_list.each do |record, entry, is_parent|
begin
writer.write(renderer.render_to_string partial: 'archival_object', layout: false, :locals => {:record => record, :level => entry.depth, :is_parent => is_parent})

rescue => e
message = e.message + " (while processing Archival Object '#{record['title']}')"
raise PDFRenderErrorArchivalObject, "#{e.class};#{message}"
end
end
end

begin
writer.write(renderer.render_to_string partial: 'footer', layout: false, :locals => {:record => @resource})
rescue => e
raise PDFRenderErrorFooter, "#{e.class};#{e.message}"
end
out_html.close

out_html
rescue => e
out_html = Tempfile.new

writer = Nokogiri::XML::SAX::PushParser.new(XMLCleaner.new(out_html))

location = case e.class.to_s
when "PDFRenderErrorHeader"
I18n.t('pdf_error.location.location_header')
when "PDFRenderErrorTitlePage"
I18n.t('pdf_error.location.location_title_page')
when "PDFRenderErrorTOC"
I18n.t('pdf_error.location.location_toc')
when "PDFRenderErrorResource"
I18n.t('pdf_error.location.location_resource')
when "PDFRenderErrorArchivalObject"
I18n.t('pdf_error.location.location_archival_object')
when "PDFRenderErrorFooter"
I18n.t('pdf_error.location.location_footer')
else
nil
end

# nil means some other error occured
if location == nil
message = e.message
orig_class = e.class.to_s
else
orig_class, message = e.message.split(';')
end

ao_list.each do |record, entry, is_parent|
writer.write(renderer.render_to_string partial: 'archival_object', layout: false, :locals => {:record => record, :level => entry.depth, :is_parent => is_parent})
error_html = ""
error_html += "<body>"
error_html += "<h1>#{I18n.t('pdf_error.title')}</h1>"
error_html += "<p>#{I18n.t('pdf_error.description')}</p>"

unless location == nil
error_html += "<p><b>#{I18n.t('pdf_error.headings.location')}</b></p>"
error_html += "<p>#{location}</p>"
end
end

writer.write(renderer.render_to_string partial: 'footer', layout: false, :locals => {:record => @resource})
out_html.close
error_html += "<p><b>#{I18n.t('pdf_error.headings.message')}</b></p>"
error_html += "<p>#{message}</p>"

error_html += "<p><b>#{I18n.t('pdf_error.headings.type')}</b></p>"
error_html += "<p>#{orig_class}</p>"

out_html
if orig_class == "Nokogiri::XML::SyntaxError"
error_html += "<p><b>#{I18n.t('pdf_error.headings.additional_info')}</b></p>"
error_html += "<p>#{I18n.t('pdf_error.additional_info.invalid_markup')}</p>"
end

error_html += "</body>"

writer.write(error_html)

out_html.close

out_html
end
end

def generate
Expand Down
18 changes: 18 additions & 0 deletions public/config/locales/en.yml
Expand Up @@ -565,3 +565,21 @@ en:
fr: French
de: German
ja: Japanese

pdf_error:
title: There was an error generating this PDF.
description: "We're sorry, there was an error generating this PDF finding aid. Here is some diagnostic information to help determine what went wrong."
headings:
location: Error Location
message: Error Message
type: Error Type
additional_info: Additional Information
location:
location_header: "The error occurred while generating the header."
location_title_page: "The error occurred while generating the title page."
location_toc: "The error occurred while generating the table of contents."
location_resource: "The error occurred while generating sections relating to the Resource record, which includes Summary Information, Controlled Access Headings, and the Notes sections."
location_archival_object: "The error occurred while generating the Collection Inventory."
location_footer: "The error occurred while generating the footer."
additional_info:
invalid_markup: "This error can result from invalid markup present in a note or subrecord."

0 comments on commit 6370c77

Please sign in to comment.