SpeciesFileGroup/taxonworks

View on GitHub
app/models/source/human.rb

Summary

Maintainability
A
0 mins
Test Coverage
# A human source can be either a single individual person or a group of people (e.g. Tom, Dick and
# Harry decided that this species is the same as that but haven't written it up yet.)
class Source::Human < Source

  IGNORE_IDENTICAL = [:serial_id, :address, :annote, :booktitle, :chapter, :crossref,
                      :edition, :editor, :howpublished, :institution, :journal, :key,
                      :month, :note, :number, :organization, :pages, :publisher, :school,
                      :series, :title, :volume, :doi, :abstract, :copyright, :language,
                      :stated_year, :verbatim, :bibtex_type, :day, :year, :isbn, :issn,
                      :verbatim_contents, :verbatim_keywords, :language_id, :translator,
                      :year_suffix, :url, :author, :cached, :cached_author_string,
                      :cached_nomenclature_date].freeze

  IGNORE_SIMILAR = IGNORE_IDENTICAL.dup.freeze

  has_many :source_source_roles, class_name: 'SourceSource', as: :role_object, dependent: :destroy, inverse_of: :role_object
  has_many :people, -> { order('roles.position ASC') }, through: :source_source_roles, source: :person, inverse_of: :authored_sources

  accepts_nested_attributes_for :people, :source_source_roles, allow_destroy: true

  validate :at_least_one_person_is_provided

  # @return [String]
  def authority_name
    Utilities::Strings.authorship_sentence(
      people.pluck(:last_name)
    )
  end

  # TODO: Special case of Source::Human needs to check for roles of 'SourceSource' matching.
  # @param [Hash] attr of matchable attributes
  # @return [Scope]
  def self.similar(attr)
    Source::Human.none
  end

  # @param [Hash] attr of matchable attributes
  # @return [Scope]
  def self.identical(attr)
    Source::Human.none
  end

  # @return [Scope]
  def similar
    Source::Human.none
  end

  # @return [Scope]
  def identical
    Source::Human.none
  end

  def self.by_person(person_ids = [ ], table_alias = nil)
    return Source::Human.none if person_ids.empty?

    s  = Source::Human.arel_table
    sr = Role.arel_table

    a = s.alias("a_#{table_alias}")

    b = s.project(a[Arel.star]).from(a)
      .join(sr)
      .on(sr['role_object_id'].eq(a['id']))

    i = 0
    person_ids.each_with_index do |person_id, i|
      sr_a = sr.alias("#{table_alias}_#{i}")
      b = b.join(sr_a).on(
        sr_a['role_object_id'].eq(a['id']),
        sr_a['person_id'].eq(person_id),
        sr_a['type'].eq('SourceSource')
      )
      i += 1
    end

    b = b.group(a['id']).having(sr['role_object_id'].count.eq(person_ids.count))
    b = b.as("z_#{table_alias}")

    Source::Human.joins(Arel::Nodes::InnerJoin.new(b, Arel::Nodes::On.new(b['id'].eq(s['id']))))
  end

  protected

  def get_cached
    [authority_name, year].compact.join(', ')
  end

  # @return [Ignored]
  def set_cached
    update_column(:cached, get_cached)
  end

  # @return [Ignored]
  def at_least_one_person_is_provided
    if people.size < 1 && source_source_roles.size < 1 && roles.size < 1 # size not count
      errors.add(:base, 'at least one person must be provided')
    end
  end

  def sv_cached_names # this cannot be moved to soft_validation_extensions
    soft_validations.add(
      :base, 'Cached values should be updated',
      success_message: 'Cached values were updated',
      failure_message:  'Failed to update cached values') if cached != get_cached
  end
end