UNC-Libraries/hy-c

View on GitHub
app/services/affiliation_remediation_service.rb

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
# frozen_string_literal: true
# This class is to remediate People Objects where the affiliation is not controlled.
# The metadata librarian has provided a one-time mapping from uncontrolled terms to controlled terms
class AffiliationRemediationService
  def initialize(id_csv_path)
    @id_csv_path = id_csv_path
  end

  def person_fields
    [:advisors, :arrangers, :composers, :contributors, :creators, :project_directors,
     :researchers, :reviewers, :translators]
  end

  def remediate_all_affiliations
    id_list.each do |id|
      object = object_by_id(id)
      next unless object

      update_affiliations(object)
      Rails.logger.info("Updated affiliations for object id: #{object.id}")
    end
  end

  def update_affiliations(object)
    person_fields.each do |person_field|
      next unless needs_updated_people?(object, person_field)

      update_affiliation_by_person_field(object, person_field)
    end
  end

  def update_affiliation_by_person_field(object, person_field)
    people_objects = object.try(person_field)

    new_attributes = []
    people_objects.each do |person|
      new_attributes << map_person_attributes(person.attributes)
    end
    object[person_field.to_s] = nil
    object.save!
    object.update("#{person_field}_attributes" => new_attributes)
    object.save!
  end

  def id_list
    @id_list ||= begin
      csv = CSV.parse(File.read(@id_csv_path), headers: true)
      csv.map { |row| row['object_id'] }
    end
  end

  def object_by_id(identifier)
    ActiveFedora::Base.find(identifier)
  rescue ActiveFedora::ObjectNotFoundError
    Rails.logger.warn("Object not found. Object identifier: #{identifier}")
    nil
  end

  def affiliation_map
    @affiliation_map ||= begin
      csv_path = 'spec/fixtures/files/umappable-affiliations-mapped-final.csv'
      csv = CSV.parse(File.read(csv_path), headers: true)
      csv.map { |row| { original_affiliation: row['original_affiliation'], new_affiliation: row['new_affiliation'] } }
    end
  end

  def map_to_new_affiliation(unmappable_affiliation)
    target_hash = affiliation_map.find { |affil| affil[:original_affiliation] == unmappable_affiliation }
    new_affiliation = target_hash.try(:[], :new_affiliation)
    return nil if new_affiliation.nil?

    if new_affiliation.include?('|')
      new_affiliation.split('|')
    else
      new_affiliation
    end
  end

  def map_person_attributes(attributes)
    # Do a json round trip to force any ActiveTriples into an array
    attributes_hash = JSON.parse(attributes.to_json)
    attributes_hash.delete('id')
    original_affiliation = attributes_hash['affiliation'].first
    new_affiliation = []
    if mappable_affiliation?(original_affiliation)
      new_affiliation << original_affiliation
      Rails.logger.info("Keeping original affiliation: #{original_affiliation}")
    elsif map_to_new_affiliation(original_affiliation) == 'n/a'
      attributes_hash.delete('other_affiliation')
      attributes_hash['other_affiliation'] = [original_affiliation]
      Rails.logger.info("Moved affiliation #{original_affiliation} to other_affiliation")
    elsif map_to_new_affiliation(original_affiliation)
      new_affiliation << map_to_new_affiliation(original_affiliation)
      Rails.logger.info("Mapped affiliation from: #{original_affiliation} to: #{new_affiliation.flatten}")
    end
    attributes_hash.delete('affiliation')
    attributes_hash['affiliation'] = new_affiliation.flatten
    attributes_hash
  end

  def mappable_affiliation?(affiliation)
    # No need to check service for empty strings
    return false unless affiliation

    return false if affiliation.empty?

    mapping = DepartmentsService.term(affiliation)
    mapping ? true : false
  end

  def contains_unmappable_affiliation?(person_objects)
    affiliations = person_objects.map { |person| person.attributes['affiliation'].first }
    affiliations.map { |affiliation| mappable_affiliation?(affiliation) }.include?(false)
  end

  def needs_updated_people?(object, person_field)
    return false unless object.attributes.keys.member?(person_field.to_s)

    person_objects = object.try(person_field)
    return false if person_objects.blank?

    return false unless contains_unmappable_affiliation?(person_objects)

    true
  end
end