unepwcmc/SAPI

View on GitHub
app/models/m_taxon_concept.rb

Summary

Maintainability
A
2 hrs
Test Coverage
# == Schema Information
#
# Table name: taxon_concepts_mview
#
#  id                                       :integer          primary key
#  parent_id                                :integer
#  taxonomy_id                              :integer
#  taxonomy_is_cites_eu                     :boolean
#  full_name                                :string(255)
#  name_status                              :string(255)
#  rank_id                                  :integer
#  rank_name                                :string(255)
#  rank_display_name_en                     :string(255)
#  rank_display_name_es                     :string(255)
#  rank_display_name_fr                     :string(255)
#  spp                                      :boolean
#  cites_accepted                           :boolean
#  kingdom_position                         :integer
#  taxonomic_position                       :string(255)
#  kingdom_name                             :string(255)
#  phylum_name                              :string(255)
#  class_name                               :string(255)
#  order_name                               :string(255)
#  family_name                              :string(255)
#  subfamily_name                           :string(255)
#  genus_name                               :string(255)
#  species_name                             :string(255)
#  subspecies_name                          :string(255)
#  kingdom_id                               :integer
#  phylum_id                                :integer
#  class_id                                 :integer
#  order_id                                 :integer
#  family_id                                :integer
#  subfamily_id                             :integer
#  genus_id                                 :integer
#  species_id                               :integer
#  subspecies_id                            :integer
#  cites_i                                  :boolean
#  cites_ii                                 :boolean
#  cites_iii                                :boolean
#  cites_listed                             :boolean
#  cites_listed_descendants                 :boolean
#  cites_show                               :boolean
#  cites_status                             :string(255)
#  cites_listing_original                   :string(255)
#  cites_listing                            :string(255)
#  cites_listing_updated_at                 :datetime
#  ann_symbol                               :string(255)
#  hash_ann_symbol                          :string(255)
#  hash_ann_parent_symbol                   :string(255)
#  eu_listed                                :boolean
#  eu_show                                  :boolean
#  eu_status                                :string(255)
#  eu_listing_original                      :string(255)
#  eu_listing                               :string(255)
#  eu_listing_updated_at                    :datetime
#  cms_listed                               :boolean
#  cms_show                                 :boolean
#  cms_status                               :string(255)
#  cms_listing_original                     :string(255)
#  cms_listing                              :string(255)
#  cms_listing_updated_at                   :datetime
#  species_listings_ids                     :string
#  species_listings_ids_aggregated          :string
#  author_year                              :string(255)
#  created_at                               :datetime
#  updated_at                               :datetime
#  dependents_updated_at                    :datetime
#  taxon_concept_id_com                     :integer
#  english_names_ary                        :string
#  spanish_names_ary                        :string
#  french_names_ary                         :string
#  taxon_concept_id_syn                     :integer
#  synonyms_ary                             :string
#  synonyms_author_years_ary                :string
#  countries_ids_ary                        :string
#  all_distribution_iso_codes_ary           :string
#  all_distribution_ary_en                  :string
#  native_distribution_ary_en               :string
#  introduced_distribution_ary_en           :string
#  introduced_uncertain_distribution_ary_en :string
#  reintroduced_distribution_ary_en         :string
#  extinct_distribution_ary_en              :string
#  extinct_uncertain_distribution_ary_en    :string
#  uncertain_distribution_ary_en            :string
#  all_distribution_ary_es                  :string
#  native_distribution_ary_es               :string
#  introduced_distribution_ary_es           :string
#  introduced_uncertain_distribution_ary_es :string
#  reintroduced_distribution_ary_es         :string
#  extinct_distribution_ary_es              :string
#  extinct_uncertain_distribution_ary_es    :string
#  uncertain_distribution_ary_es            :string
#  all_distribution_ary_fr                  :string
#  native_distribution_ary_fr               :string
#  introduced_distribution_ary_fr           :string
#  introduced_uncertain_distribution_ary_fr :string
#  reintroduced_distribution_ary_fr         :string
#  extinct_distribution_ary_fr              :string
#  extinct_uncertain_distribution_ary_fr    :string
#  uncertain_distribution_ary_fr            :string
#  show_in_species_plus                     :boolean
#  dirty                                    :boolean
#  expiry                                   :datetime
#

class MTaxonConcept < ApplicationRecord
  extend Mobility
  self.table_name = :taxon_concepts_mview
  self.primary_key = :id

  belongs_to :taxon_concept, :foreign_key => :id, optional: true
  has_many :cites_listing_changes, :foreign_key => :taxon_concept_id, :class_name => 'MCitesListingChange'
  has_many :historic_cites_listing_changes_for_downloads, -> {
    where(
      show_in_downloads: true
    ).order(
      Arel.sql(
        <<-SQL
          effective_at,
          CASE
          WHEN change_type_name = 'ADDITION' THEN 0
          WHEN change_type_name = 'RESERVATION' THEN 1
          WHEN change_type_name = 'RESERVATION_WITHDRAWAL' THEN 2
          WHEN change_type_name = 'DELETION' THEN 3
          END
        SQL
      )
    )
  }, :foreign_key => :taxon_concept_id,
    :class_name => 'MCitesListingChange'
  has_many :current_cites_additions, -> { where(is_current: true, change_type_name: ChangeType::ADDITION).order('effective_at DESC, species_listing_name ASC') },
    :foreign_key => :taxon_concept_id,
    :class_name => 'MCitesListingChange'
  has_many :current_cms_additions, -> { where(is_current: true, change_type_name: ChangeType::ADDITION).order('effective_at DESC, species_listing_name ASC') },
    :foreign_key => :taxon_concept_id,
    :class_name => 'MCmsListingChange'
  has_many :cites_processes
  scope :by_cites_eu_taxonomy, -> { where(:taxonomy_is_cites_eu => true) }
  scope :by_cms_taxonomy, -> { where(:taxonomy_is_cites_eu => false) }

  scope :without_non_accepted, -> { where(:name_status => ['A', 'H']) }

  scope :without_hidden, -> { where("#{table_name}.cites_show = 't'") }

  scope :by_name, lambda { |name, match_options|
    MTaxonConceptFilterByScientificNameWithDescendants.new(
      self, name, match_options
    ).relation
  }

  scope :by_scientific_name, lambda { |scientific_name|
    MTaxonConceptFilterByScientificNameWithDescendants.new(
      self,
      scientific_name,
      { :synonyms => true, :common_names => true, :subspecies => false }
    ).relation
  }

  scope :at_level_of_listing, -> { where(:cites_listed => 't') }

  scope :taxonomic_layout, -> { order('taxonomic_position') }
  scope :alphabetical_layout, -> { order(['kingdom_position', 'full_name']) }
  translates :rank_display_name,
    :all_distribution_ary, :native_distribution_ary,
    :introduced_distribution_ary, :introduced_uncertain_distribution_ary,
    :reintroduced_distribution_ary, :extinct_distribution_ary,
    :extinct_uncertain_distribution_ary, :uncertain_distribution_ary

  # leftover from old Checklist code, this field is used in returned json
  def current_listing
    cites_listing
  end

  def self.descendants_ids(taxon_concept)
    query = <<-SQL
    WITH RECURSIVE descendents AS (
      SELECT id, rank_id, full_name
      FROM #{self.table_name}
      WHERE parent_id = #{taxon_concept.to_i}
      UNION ALL
      SELECT taxon_concepts.id, taxon_concepts.rank_id, taxon_concepts.full_name
      FROM #{self.table_name} taxon_concepts
      JOIN descendents h ON h.id = taxon_concepts.parent_id
    )
    SELECT id FROM descendents
    ORDER BY rank_id ASC, full_name
    SQL
    res = ApplicationRecord.connection.execute(query)
    res.ntuples.zero? ? [taxon_concept.to_i] : res.map(&:values).flatten << taxon_concept.to_i
  end

  def spp
    if ['GENUS', 'FAMILY', 'SUBFAMILY', 'ORDER'].include?(rank_name)
      'spp.' unless name_status == 'H' # Hybrids are not species groups
    else
      nil
    end
  end

  ['English', 'Spanish', 'French'].each do |lng|
    define_method("#{lng.downcase}_names") do
      sym = :"#{lng.downcase}_names_ary"
      db_ary_to_array(sym)
    end
  end

  def synonyms
    db_ary_to_array :synonyms_ary
  end

  def synonyms_author_years
    db_ary_to_array :synonyms_author_years_ary
  end

  def synonyms_with_authors
    synonyms.each_with_index.map { |syn, idx| "#{syn} #{synonyms_author_years[idx]}" }
  end

  def db_ary_to_array(ary)
    if respond_to?(ary)
      attr = send(ary)
      return [] unless attr.present?
      attr.map(&:to_s)
    else
      []
    end
  end

  def countries_ids
    if respond_to?(:countries_ids_ary) && countries_ids_ary?
      countries_ids_ary || []
    elsif respond_to? :tc_countries_ids_ary
      tc_countries_ids_ary || []
    else
      []
    end
  end

  def countries_iso_codes
    all_distribution_iso_codes
  end

  def countries_full_names
    all_distribution
  end

  def all_distribution
    all_distribution_ary || []
  end

  def all_distribution_iso_codes
    all_distribution_iso_codes_ary || []
  end

  def native_distribution
    native_distribution_ary || []
  end

  def introduced_distribution
    introduced_distribution_ary || []
  end

  def introduced_uncertain_distribution
    introduced_uncertain_distribution_ary || []
  end

  def reintroduced_distribution
    reintroduced_distribution_ary || []
  end

  def extinct_distribution
    extinct_distribution_ary || []
  end

  def extinct_uncertain_distribution
    extinct_uncertain_distribution_ary || []
  end

  def uncertain_distribution
    uncertain_distribution_ary || []
  end

  def recently_changed
    return (cites_listing_updated_at ? cites_listing_updated_at > 8.year.ago : false)
  end

  # the methods below are for checklist downloads only and a bad idea as well

  ['en', 'es', 'fr'].each do |lng|
    ["hash_full_note_#{lng.downcase}", "full_note_#{lng.downcase}", "short_note_#{lng.downcase}"].each do |method_name|
      define_method(method_name) do
        current_cites_additions.map do |lc|
          note = lc.send(method_name) || ''
          note && "Appendix #{lc.species_listing_name}:" + (note || '') + (" #{lc.nomenclature_note}" || '')
        end.join("\n")
      end
    end
  end

  # returns the ids of parties associated with current listing changes
  # used only for CITES Checklist atm, therefore a designation filter is applied
  def current_parties_ids
    current_cites_additions.map(&:party_id).compact.uniq
  end

  def current_parties_iso_codes
    CountryDictionary.instance.get_iso_codes_by_ids(current_parties_ids).compact
  end

  def current_parties_full_names
    CountryDictionary.instance.get_names_by_ids(current_parties_ids).compact
  end

end