sul-dlss/cocina-models

View on GitHub
lib/cocina/models/mapping/to_mods/related_resource.rb

Summary

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

module Cocina
  module Models
    module Mapping
      module ToMods
        # Maps relatedResource from cocina to MODS relatedItem
        class RelatedResource
          # see https://docs.google.com/spreadsheets/d/1d5PokzgXqNykvQeckG2ND43B6i9_CsjfIVwS_IsphS8/edit#gid=0
          TYPES = {
            'has original version' => 'original',
            'has other format' => 'otherFormat',
            'has part' => 'constituent',
            'has version' => 'otherVersion',
            'in series' => 'series',
            'part of' => 'host',
            'preceded by' => 'preceding',
            'related to' => nil, # 'related to' is a null type by design
            'reviewed by' => 'reviewOf',
            'referenced by' => 'isReferencedBy',
            'references' => 'references',
            'succeeded by' => 'succeeding'
          }.freeze

          DETAIL_TYPES = {
            'location within source' => 'part',
            'volume' => 'volume',
            'issue' => 'issue',
            'chapter' => 'chapter',
            'section' => 'section',
            'paragraph' => 'paragraph',
            'track' => 'track',
            'marker' => 'marker'
          }.freeze

          # @params [Nokogiri::XML::Builder] xml
          # @params [Array<Cocina::Models::RelatedResource>] related_resources
          # @param [string] druid
          # @param [IdGenerator] id_generator
          def self.write(xml:, related_resources:, druid:, id_generator:)
            new(xml: xml, related_resources: related_resources, druid: druid, id_generator: id_generator).write
          end

          def initialize(xml:, related_resources:, druid:, id_generator:)
            @xml = xml
            @related_resources = Array(related_resources)
            @druid = druid
            @id_generator = id_generator
          end

          def write
            filtered_related_resources.each do |(attributes, new_related, _orig_related)|
              xml.relatedItem attributes do
                ModsWriter.write(xml: xml, description: new_related, druid: druid,
                                 id_generator: id_generator)
              end
            end

            related_resources.filter(&:valueAt).each do |related_resource|
              xml.relatedItem nil, { 'xlink:href' => related_resource.valueAt }
            end
          end

          private

          attr_reader :xml, :related_resources, :druid, :id_generator

          def filtered_related_resources
            related_resources.filter_map do |related|
              next if related.valueAt

              other_type_note = other_type_note_for(related)

              # Filter notes
              related_hash = related.to_h
              new_notes = related_hash.fetch(:note, []).reject { |note| note[:type] == 'other relation type' }
              related_hash[:note] = new_notes.empty? ? nil : new_notes
              next if related_hash.empty?

              new_related = Cocina::Models::RelatedResource.new(related_hash.compact)

              [attributes_for(related, other_type_note), new_related, related]
            end
          end

          def attributes_for(related, other_type_note)
            {}.tap do |attrs|
              attrs[:type] = TYPES.fetch(related.type) if related.type
              attrs[:displayLabel] = related.displayLabel

              if other_type_note
                attrs[:otherType] = other_type_note.value
                attrs[:otherTypeURI] = other_type_note.uri
                attrs[:otherTypeAuth] = other_type_note.source&.value
              end
            end.compact
          end

          def other_type_note_for(related)
            return nil if related.note.blank?

            related.note.find { |note| note.type == 'other relation type' }
          end
        end
      end
    end
  end
end