sul-dlss/argo

View on GitHub
app/models/track_sheet.rb

Summary

Maintainability
A
0 mins
Test Coverage
A
97%
# frozen_string_literal: true

class TrackSheet
  attr_reader :druids

  # @param [Array<String>] druids a list of non-namespaced druids
  def initialize(druids)
    @druids = druids
  end

  # @FIXME: This code needs to separate data and query logic from presentation.

  # @param [Array<String>] druids unqualified DRUID identifiers
  # @return [Prawn::Document] PDF document for the DRUIDs
  def generate_tracking_pdf
    # FIXME: why not search for all druids in one query?
    # FIXME: how about a MAX number of druids? And/Or a chunky use of enumeration?
    druids.each { |druid| find_or_create_in_solr_by_id(druid) }
    pdf = Prawn::Document.new(page_size: [5.5.in, 8.5.in])
    pdf.font('Courier')
    druids.each_with_index do |druid, i|
      generate_tracking_sheet(druid, pdf)
      pdf.start_new_page unless i + 1 == druids.length
    end
    pdf
  end

  protected

  # Adds one DRUID page to the PDF document
  # @param [String] druid unqualified DRUID identifier
  # @param [Prawn::Document] pdf document being built (document is modified)
  # @return [Prawn::Document] the same document
  def generate_tracking_sheet(druid, pdf)
    bc_width = 2.25.in
    bc_height = 0.75.in

    top_margin = pdf.page.size[1] - pdf.bounds.absolute_top

    doc = nil
    begin
      doc = find_or_create_in_solr_by_id(druid)
    rescue StandardError
      pdf.text "DRUID #{druid} not found in index", size: 15, style: :bold, align: :center
      return pdf
    end

    barcode = Barby::Code128B.new(druid.delete_prefix('druid:'))
    barcode.annotate_pdf(
      pdf,
      width: bc_width,
      height: bc_height,
      x: (pdf.bounds.width / 2) - (bc_width / 2),
      y: (pdf.bounds.height - bc_height)
    )

    pdf.y -= (bc_height + 0.25.in)
    pdf.text druid.delete_prefix('druid:'), size: 15, style: :bold, align: :center
    pdf.y -= 0.5.in

    pdf.font('Courier', size: 10)
    pdf.table(doc_to_table(doc), column_widths: [100, 224], cell_style: { borders: [], padding: 0.pt })

    pdf.y -= 0.5.in

    pdf.font_size = 14
    pdf.text 'Tracking:'
    pdf.text ' '

    baseline = pdf.y - top_margin - pdf.font.ascender
    pdf.rectangle([0, baseline + pdf.font.ascender], pdf.font.ascender, pdf.font.ascender)
    pdf.indent(pdf.font.ascender + 4.pt) do
      pdf.text 'Scanned by:'
      pdf.indent(pdf.width_of('Scanned by:') + 0.125.in) do
        pdf.line 0, baseline, pdf.bounds.width, baseline
      end
    end
    pdf.stroke

    pdf.y -= 0.5.in
    pdf.text('Notes:')
    pdf.stroke do
      while pdf.y >= pdf.bounds.absolute_bottom
        baseline = pdf.y - top_margin - pdf.font.height
        pdf.line 0, baseline, pdf.bounds.width, baseline
        pdf.y -= pdf.font.height * 1.5
      end
    end
    pdf
  end

  # @param [Solr Document] doc Solr document
  # @return [Array<Array<String>>] Complex array suitable for pdf.table()
  def doc_to_table(solr_doc)
    table_data = []
    label = solr_doc.title_display.truncate(110)
    table_data.push(['Object Label:', label])
    table_data.push(['Project Name:', solr_doc.project_tag]) if solr_doc.project_tag

    tags = solr_doc.tags.filter_map do |tag|
      /^Project\s*:/.match?(tag) ? nil : tag.gsub(/\s+/, Prawn::Text::NBSP)
    end
    table_data.push(['Tags:', tags.join("\n")]) unless tags.empty?
    table_data.push(["#{CatalogRecordId.label}:", solr_doc.catalog_record_id]) if solr_doc.catalog_record_id.present?
    table_data.push(['Source ID:', solr_doc.source_id]) if solr_doc.source_id.present?
    table_data.push(['Barcode:', solr_doc.barcode]) if solr_doc.barcode.present?
    table_data.push(['Date Printed:', Time.zone.now.strftime('%c')])
    table_data
  end

  # @param [String] druid unqualified DRUID identifier
  # @return [SolrDocument] SolrDocument build from hash of solr query
  # @note To the extent we use Solr input filters or copyField(s), the Solr version will differ from the to_solr hash.
  # @note That difference shouldn't be important for the few known fields we use here.
  def find_or_create_in_solr_by_id(druid)
    namespaced_druid = Druid.new(druid).with_namespace
    doc = SearchService.query(%(id:"#{namespaced_druid}"), rows: 1)['response']['docs'].first
    return SolrDocument.new(doc) unless doc.nil?

    Dor::Services::Client.object(namespaced_druid).reindex
    SolrDocument.new(SearchService.query(%(id:"#{namespaced_druid}"), rows: 1)['response']['docs'].first)
  end
end