Class: Otu

Inherits:
ApplicationRecord show all
Includes:
Housekeeping, Shared::Citations, Shared::Confidences, Shared::DataAttributes, Shared::Depictions, Shared::HasPapertrail, Shared::Identifiers, Shared::IsData, Shared::Loanable, Shared::Notes, Shared::Tags, SoftValidation
Defined in:
app/models/otu.rb

Overview

An Otu (loosely, operational taxonomic unit) can be thought of as a unit of study. In most cases an otu is a taxon.

An Otu is defined by its underlying data and may be labeled with a name (TaxonName). Otus are used to represent rows in matrices, taxon pages, individuals or populations, or arbitrary clusters of organisms (e.g. 'unsorted specimens in this container'). Otus are a primary unit of work in TaxonWorks.

OTU is labeled with a name, either arbitrarily given or specifically linked to a taxon_name_id.

TODO Semantics vs. taxon_name_id

Constant Summary

Constant Summary

Constants included from SoftValidation

SoftValidation::ANCESTORS_WITH_SOFT_VALIDATIONS

Instance Attribute Summary (collapse)

Class Method Summary (collapse)

Instance Method Summary (collapse)

Methods included from SoftValidation

#clear_soft_validations, #fix_soft_validations, #soft_fixed?, #soft_valid?, #soft_validate, #soft_validated?, #soft_validations

Methods included from Housekeeping

#has_polymorphic_relationship?

Methods included from ActiverecordUtilities

#trim_attributes

Instance Attribute Details

- (String) name

A label for the OTU.

Returns:

  • (String)


25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'app/models/otu.rb', line 25

class Otu < ApplicationRecord
  include Housekeeping
  include SoftValidation
  #include Shared::AlternateValues  # 1/26/15 with MJY - not going to allow alternate values in Burlap
  include Shared::Citations           # TODO: have to think hard about this vs. using Nico's framework
  include Shared::DataAttributes
  include Shared::Identifiers
  include Shared::Notes
  include Shared::Tags
  include Shared::Depictions
  include Shared::Loanable
  include Shared::Confidences
  include Shared::HasPapertrail
  include Shared::IsData

  belongs_to :taxon_name, inverse_of: :otus

  has_many :asserted_distributions, inverse_of: :otu

  has_many :taxon_determinations, inverse_of: :otu, dependent: :destroy
  has_many :collection_objects, through: :taxon_determinations, source: :biological_collection_object, inverse_of: :otus

  has_many :collecting_events, -> {distinct}, through: :collection_objects

  has_many :common_names, dependent: :destroy
  has_many :collection_profiles # @proceps dependent: what?
  has_many :contents, inverse_of: :otu, dependent: :destroy
  has_many :geographic_areas_from_asserted_distributions, through: :asserted_distributions, source: :geographic_area
  has_many :geographic_areas_from_collecting_events, through: :collecting_events, source: :geographic_area
  has_many :georeferences, through: :collecting_events

  has_many :observations, inverse_of: :otu
  has_many :descriptors, through: :observations

  has_many :content_topics, through: :contents, source: :topic

  scope :with_taxon_name_id, -> (taxon_name_id) { where(taxon_name_id: taxon_name_id) }
  scope :with_name, -> (name) { where(name: name) }

  # @return scope
  def self.self_and_descendants_of(otu_id)
    o = Otu.includes(:taxon_name).find(otu_id)
    if o && o.taxon_name
      with_taxon_name_id(Otu.find(otu_id).taxon_name.self_and_descendants)
    else
      Otu.where(id: otu_id)
    end
  end

  validate :check_required_fields

  soft_validate(:sv_taxon_name, set: :taxon_name)
  soft_validate(:sv_duplicate_otu, set: :duplicate_otu)

  accepts_nested_attributes_for :common_names, allow_destroy: true

  #region class methods

  # return [Scope] the otus bound to that taxon name and its descendants
  def self.for_taxon_name(taxon_name)
    if taxon_name.kind_of?(String) || taxon_name.kind_of?(Integer)
      tn = TaxonName.find(taxon_name)
    else
      tn = taxon_name
    end
    Otu.joins(taxon_name: [:ancestor_hierarchies]).where(taxon_name_hierarchies: {ancestor_id: tn.id} )
  end

  # TODO: This need to be renamed to reflect "simple" association
  def self.batch_preview(file: nil, ** args)
    # f     = CSV.read(file, headers: true, col_sep: "\t", skip_blanks: true, header_converters: :symbol)
    @otus = []
    File.open(file).each do |row|
      name = row.strip
      next if name.blank?
      @otus.push(Otu.new(name: row.strip))
    end
    @otus
  end

  def self.batch_create(otus: {}, ** args)
    new_otus = []
    begin
      Otu.transaction do
        otus.keys.each do |k|
          o = Otu.new(otus[k])
          o.save!
          new_otus.push(o)
        end
      end
    rescue
      return false
    end
    new_otus
  end

  #endregion

  #region instance methods
  # Hernán - this is extremely hacky, I'd like to
  # map core keys to procs, use yield:, use cached values,
  # add logic for has_many handling (e.g. identifiers) etc.
  # ultmately, each key maps to a proc that returns a value
  #
  # deprecated for new approach in CollectionObject, AssertedDistribution
  def dwca_core
    core = Dwca::GbifProfile::CoreTaxon.new

    core.nomenclaturalCode        = (taxon_name.rank_class.nomenclatural_code.to_s.upcase)
    core.taxonomicStatus          = (taxon_name.unavailable_or_invalid? ? nil : 'accepted')
    core.nomenclaturalStatus      = (taxon_name.classification_invalid_or_unavailable? ? nil : 'available') # needs tweaking
    core.scientificName           = taxon_name.cached
    core.scientificNameAuthorship = taxon_name.cached_author_year
    core.scientificNameID         = taxon_name.identifiers.first.identifier
    core.taxonRank                = taxon_name.rank
    core.namePublishedIn          = taxon_name.source.cached
    core
  end

  def otu_name
    if !name.blank?
      name
    elsif !self.taxon_name_id.nil?
      self.taxon_name.cached_html_name_and_author_year
    else
      nil
    end
  end

  def distribution_geoJSON
    a_ds   = Gis::GeoJSON.feature_collection(geographic_areas_from_asserted_distributions, :asserted_distributions)
    c_os   = Gis::GeoJSON.feature_collection(collecting_events, :collecting_events_georeferences)
    c_es   = Gis::GeoJSON.feature_collection(geographic_areas_from_collecting_events, :collecting_events_geographic_area)
    retval = Gis::GeoJSON.aggregation([a_ds, c_os, c_es], :distribution)
    retval
  end

  protected

  def check_required_fields
    if taxon_name_id.blank? && name.blank?
      errors.add(:taxon_name_id, 'and/or name should be selected')
      errors.add(:name, 'and/or taxon name should be selected')
    end
  end

  def sv_taxon_name
    soft_validations.add(:taxon_name_id, 'Taxon is not selected') if self.taxon_name_id.nil?
  end

  def sv_duplicate_otu
    unless Otu.with_taxon_name_id(self.taxon_name_id).with_name(self.name).not_self(self).with_project_id(self.project_id).empty?
      soft_validations.add(:taxon_name_id, 'Duplicate Taxon and Name combination')
      soft_validations.add(:name, 'Duplicate Taxon and Name combination')
    end
  end

  # def collecting_event_geoJSON
  #   Gis::GeoJSON.feature_collection(collecting_events, :collecting_events)
  # end
  #endregion
end

- (Integer) project_id

the project ID

Returns:

  • (Integer)


25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'app/models/otu.rb', line 25

class Otu < ApplicationRecord
  include Housekeeping
  include SoftValidation
  #include Shared::AlternateValues  # 1/26/15 with MJY - not going to allow alternate values in Burlap
  include Shared::Citations           # TODO: have to think hard about this vs. using Nico's framework
  include Shared::DataAttributes
  include Shared::Identifiers
  include Shared::Notes
  include Shared::Tags
  include Shared::Depictions
  include Shared::Loanable
  include Shared::Confidences
  include Shared::HasPapertrail
  include Shared::IsData

  belongs_to :taxon_name, inverse_of: :otus

  has_many :asserted_distributions, inverse_of: :otu

  has_many :taxon_determinations, inverse_of: :otu, dependent: :destroy
  has_many :collection_objects, through: :taxon_determinations, source: :biological_collection_object, inverse_of: :otus

  has_many :collecting_events, -> {distinct}, through: :collection_objects

  has_many :common_names, dependent: :destroy
  has_many :collection_profiles # @proceps dependent: what?
  has_many :contents, inverse_of: :otu, dependent: :destroy
  has_many :geographic_areas_from_asserted_distributions, through: :asserted_distributions, source: :geographic_area
  has_many :geographic_areas_from_collecting_events, through: :collecting_events, source: :geographic_area
  has_many :georeferences, through: :collecting_events

  has_many :observations, inverse_of: :otu
  has_many :descriptors, through: :observations

  has_many :content_topics, through: :contents, source: :topic

  scope :with_taxon_name_id, -> (taxon_name_id) { where(taxon_name_id: taxon_name_id) }
  scope :with_name, -> (name) { where(name: name) }

  # @return scope
  def self.self_and_descendants_of(otu_id)
    o = Otu.includes(:taxon_name).find(otu_id)
    if o && o.taxon_name
      with_taxon_name_id(Otu.find(otu_id).taxon_name.self_and_descendants)
    else
      Otu.where(id: otu_id)
    end
  end

  validate :check_required_fields

  soft_validate(:sv_taxon_name, set: :taxon_name)
  soft_validate(:sv_duplicate_otu, set: :duplicate_otu)

  accepts_nested_attributes_for :common_names, allow_destroy: true

  #region class methods

  # return [Scope] the otus bound to that taxon name and its descendants
  def self.for_taxon_name(taxon_name)
    if taxon_name.kind_of?(String) || taxon_name.kind_of?(Integer)
      tn = TaxonName.find(taxon_name)
    else
      tn = taxon_name
    end
    Otu.joins(taxon_name: [:ancestor_hierarchies]).where(taxon_name_hierarchies: {ancestor_id: tn.id} )
  end

  # TODO: This need to be renamed to reflect "simple" association
  def self.batch_preview(file: nil, ** args)
    # f     = CSV.read(file, headers: true, col_sep: "\t", skip_blanks: true, header_converters: :symbol)
    @otus = []
    File.open(file).each do |row|
      name = row.strip
      next if name.blank?
      @otus.push(Otu.new(name: row.strip))
    end
    @otus
  end

  def self.batch_create(otus: {}, ** args)
    new_otus = []
    begin
      Otu.transaction do
        otus.keys.each do |k|
          o = Otu.new(otus[k])
          o.save!
          new_otus.push(o)
        end
      end
    rescue
      return false
    end
    new_otus
  end

  #endregion

  #region instance methods
  # Hernán - this is extremely hacky, I'd like to
  # map core keys to procs, use yield:, use cached values,
  # add logic for has_many handling (e.g. identifiers) etc.
  # ultmately, each key maps to a proc that returns a value
  #
  # deprecated for new approach in CollectionObject, AssertedDistribution
  def dwca_core
    core = Dwca::GbifProfile::CoreTaxon.new

    core.nomenclaturalCode        = (taxon_name.rank_class.nomenclatural_code.to_s.upcase)
    core.taxonomicStatus          = (taxon_name.unavailable_or_invalid? ? nil : 'accepted')
    core.nomenclaturalStatus      = (taxon_name.classification_invalid_or_unavailable? ? nil : 'available') # needs tweaking
    core.scientificName           = taxon_name.cached
    core.scientificNameAuthorship = taxon_name.cached_author_year
    core.scientificNameID         = taxon_name.identifiers.first.identifier
    core.taxonRank                = taxon_name.rank
    core.namePublishedIn          = taxon_name.source.cached
    core
  end

  def otu_name
    if !name.blank?
      name
    elsif !self.taxon_name_id.nil?
      self.taxon_name.cached_html_name_and_author_year
    else
      nil
    end
  end

  def distribution_geoJSON
    a_ds   = Gis::GeoJSON.feature_collection(geographic_areas_from_asserted_distributions, :asserted_distributions)
    c_os   = Gis::GeoJSON.feature_collection(collecting_events, :collecting_events_georeferences)
    c_es   = Gis::GeoJSON.feature_collection(geographic_areas_from_collecting_events, :collecting_events_geographic_area)
    retval = Gis::GeoJSON.aggregation([a_ds, c_os, c_es], :distribution)
    retval
  end

  protected

  def check_required_fields
    if taxon_name_id.blank? && name.blank?
      errors.add(:taxon_name_id, 'and/or name should be selected')
      errors.add(:name, 'and/or taxon name should be selected')
    end
  end

  def sv_taxon_name
    soft_validations.add(:taxon_name_id, 'Taxon is not selected') if self.taxon_name_id.nil?
  end

  def sv_duplicate_otu
    unless Otu.with_taxon_name_id(self.taxon_name_id).with_name(self.name).not_self(self).with_project_id(self.project_id).empty?
      soft_validations.add(:taxon_name_id, 'Duplicate Taxon and Name combination')
      soft_validations.add(:name, 'Duplicate Taxon and Name combination')
    end
  end

  # def collecting_event_geoJSON
  #   Gis::GeoJSON.feature_collection(collecting_events, :collecting_events)
  # end
  #endregion
end

- (Integer) taxon_name_id

The id of the nomenclatural name for this OTU. The presence of a nomenclatural name carries no biological meaning, it is simply a means to organize concepts within a nomenclatural system.

Returns:

  • (Integer)


25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'app/models/otu.rb', line 25

class Otu < ApplicationRecord
  include Housekeeping
  include SoftValidation
  #include Shared::AlternateValues  # 1/26/15 with MJY - not going to allow alternate values in Burlap
  include Shared::Citations           # TODO: have to think hard about this vs. using Nico's framework
  include Shared::DataAttributes
  include Shared::Identifiers
  include Shared::Notes
  include Shared::Tags
  include Shared::Depictions
  include Shared::Loanable
  include Shared::Confidences
  include Shared::HasPapertrail
  include Shared::IsData

  belongs_to :taxon_name, inverse_of: :otus

  has_many :asserted_distributions, inverse_of: :otu

  has_many :taxon_determinations, inverse_of: :otu, dependent: :destroy
  has_many :collection_objects, through: :taxon_determinations, source: :biological_collection_object, inverse_of: :otus

  has_many :collecting_events, -> {distinct}, through: :collection_objects

  has_many :common_names, dependent: :destroy
  has_many :collection_profiles # @proceps dependent: what?
  has_many :contents, inverse_of: :otu, dependent: :destroy
  has_many :geographic_areas_from_asserted_distributions, through: :asserted_distributions, source: :geographic_area
  has_many :geographic_areas_from_collecting_events, through: :collecting_events, source: :geographic_area
  has_many :georeferences, through: :collecting_events

  has_many :observations, inverse_of: :otu
  has_many :descriptors, through: :observations

  has_many :content_topics, through: :contents, source: :topic

  scope :with_taxon_name_id, -> (taxon_name_id) { where(taxon_name_id: taxon_name_id) }
  scope :with_name, -> (name) { where(name: name) }

  # @return scope
  def self.self_and_descendants_of(otu_id)
    o = Otu.includes(:taxon_name).find(otu_id)
    if o && o.taxon_name
      with_taxon_name_id(Otu.find(otu_id).taxon_name.self_and_descendants)
    else
      Otu.where(id: otu_id)
    end
  end

  validate :check_required_fields

  soft_validate(:sv_taxon_name, set: :taxon_name)
  soft_validate(:sv_duplicate_otu, set: :duplicate_otu)

  accepts_nested_attributes_for :common_names, allow_destroy: true

  #region class methods

  # return [Scope] the otus bound to that taxon name and its descendants
  def self.for_taxon_name(taxon_name)
    if taxon_name.kind_of?(String) || taxon_name.kind_of?(Integer)
      tn = TaxonName.find(taxon_name)
    else
      tn = taxon_name
    end
    Otu.joins(taxon_name: [:ancestor_hierarchies]).where(taxon_name_hierarchies: {ancestor_id: tn.id} )
  end

  # TODO: This need to be renamed to reflect "simple" association
  def self.batch_preview(file: nil, ** args)
    # f     = CSV.read(file, headers: true, col_sep: "\t", skip_blanks: true, header_converters: :symbol)
    @otus = []
    File.open(file).each do |row|
      name = row.strip
      next if name.blank?
      @otus.push(Otu.new(name: row.strip))
    end
    @otus
  end

  def self.batch_create(otus: {}, ** args)
    new_otus = []
    begin
      Otu.transaction do
        otus.keys.each do |k|
          o = Otu.new(otus[k])
          o.save!
          new_otus.push(o)
        end
      end
    rescue
      return false
    end
    new_otus
  end

  #endregion

  #region instance methods
  # Hernán - this is extremely hacky, I'd like to
  # map core keys to procs, use yield:, use cached values,
  # add logic for has_many handling (e.g. identifiers) etc.
  # ultmately, each key maps to a proc that returns a value
  #
  # deprecated for new approach in CollectionObject, AssertedDistribution
  def dwca_core
    core = Dwca::GbifProfile::CoreTaxon.new

    core.nomenclaturalCode        = (taxon_name.rank_class.nomenclatural_code.to_s.upcase)
    core.taxonomicStatus          = (taxon_name.unavailable_or_invalid? ? nil : 'accepted')
    core.nomenclaturalStatus      = (taxon_name.classification_invalid_or_unavailable? ? nil : 'available') # needs tweaking
    core.scientificName           = taxon_name.cached
    core.scientificNameAuthorship = taxon_name.cached_author_year
    core.scientificNameID         = taxon_name.identifiers.first.identifier
    core.taxonRank                = taxon_name.rank
    core.namePublishedIn          = taxon_name.source.cached
    core
  end

  def otu_name
    if !name.blank?
      name
    elsif !self.taxon_name_id.nil?
      self.taxon_name.cached_html_name_and_author_year
    else
      nil
    end
  end

  def distribution_geoJSON
    a_ds   = Gis::GeoJSON.feature_collection(geographic_areas_from_asserted_distributions, :asserted_distributions)
    c_os   = Gis::GeoJSON.feature_collection(collecting_events, :collecting_events_georeferences)
    c_es   = Gis::GeoJSON.feature_collection(geographic_areas_from_collecting_events, :collecting_events_geographic_area)
    retval = Gis::GeoJSON.aggregation([a_ds, c_os, c_es], :distribution)
    retval
  end

  protected

  def check_required_fields
    if taxon_name_id.blank? && name.blank?
      errors.add(:taxon_name_id, 'and/or name should be selected')
      errors.add(:name, 'and/or taxon name should be selected')
    end
  end

  def sv_taxon_name
    soft_validations.add(:taxon_name_id, 'Taxon is not selected') if self.taxon_name_id.nil?
  end

  def sv_duplicate_otu
    unless Otu.with_taxon_name_id(self.taxon_name_id).with_name(self.name).not_self(self).with_project_id(self.project_id).empty?
      soft_validations.add(:taxon_name_id, 'Duplicate Taxon and Name combination')
      soft_validations.add(:name, 'Duplicate Taxon and Name combination')
    end
  end

  # def collecting_event_geoJSON
  #   Gis::GeoJSON.feature_collection(collecting_events, :collecting_events)
  # end
  #endregion
end

Class Method Details

+ (Object) batch_create(otus: {}, **args)



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'app/models/otu.rb', line 105

def self.batch_create(otus: {}, ** args)
  new_otus = []
  begin
    Otu.transaction do
      otus.keys.each do |k|
        o = Otu.new(otus[k])
        o.save!
        new_otus.push(o)
      end
    end
  rescue
    return false
  end
  new_otus
end

+ (Object) batch_preview(file: nil, **args)

TODO: This need to be renamed to reflect “simple” association



94
95
96
97
98
99
100
101
102
103
# File 'app/models/otu.rb', line 94

def self.batch_preview(file: nil, ** args)
  # f     = CSV.read(file, headers: true, col_sep: "\t", skip_blanks: true, header_converters: :symbol)
  @otus = []
  File.open(file).each do |row|
    name = row.strip
    next if name.blank?
    @otus.push(Otu.new(name: row.strip))
  end
  @otus
end

+ (Object) for_taxon_name(taxon_name)

return [Scope] the otus bound to that taxon name and its descendants



84
85
86
87
88
89
90
91
# File 'app/models/otu.rb', line 84

def self.for_taxon_name(taxon_name)
  if taxon_name.kind_of?(String) || taxon_name.kind_of?(Integer)
    tn = TaxonName.find(taxon_name)
  else
    tn = taxon_name
  end
  Otu.joins(taxon_name: [:ancestor_hierarchies]).where(taxon_name_hierarchies: {ancestor_id: tn.id} )
end

+ (Object) self_and_descendants_of(otu_id)

Returns scope

Returns:

  • scope



65
66
67
68
69
70
71
72
# File 'app/models/otu.rb', line 65

def self.self_and_descendants_of(otu_id)
  o = Otu.includes(:taxon_name).find(otu_id)
  if o && o.taxon_name
    with_taxon_name_id(Otu.find(otu_id).taxon_name.self_and_descendants)
  else
    Otu.where(id: otu_id)
  end
end

Instance Method Details

- (Object) check_required_fields (protected)



164
165
166
167
168
169
# File 'app/models/otu.rb', line 164

def check_required_fields
  if taxon_name_id.blank? && name.blank?
    errors.add(:taxon_name_id, 'and/or name should be selected')
    errors.add(:name, 'and/or taxon name should be selected')
  end
end

- (Object) distribution_geoJSON



154
155
156
157
158
159
160
# File 'app/models/otu.rb', line 154

def distribution_geoJSON
  a_ds   = Gis::GeoJSON.feature_collection(geographic_areas_from_asserted_distributions, :asserted_distributions)
  c_os   = Gis::GeoJSON.feature_collection(collecting_events, :collecting_events_georeferences)
  c_es   = Gis::GeoJSON.feature_collection(geographic_areas_from_collecting_events, :collecting_events_geographic_area)
  retval = Gis::GeoJSON.aggregation([a_ds, c_os, c_es], :distribution)
  retval
end

- (Object) dwca_core

region instance methods Hernán - this is extremely hacky, I'd like to map core keys to procs, use yield:, use cached values, add logic for has_many handling (e.g. identifiers) etc. ultmately, each key maps to a proc that returns a value

deprecated for new approach in CollectionObject, AssertedDistribution



130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'app/models/otu.rb', line 130

def dwca_core
  core = Dwca::GbifProfile::CoreTaxon.new

  core.nomenclaturalCode        = (taxon_name.rank_class.nomenclatural_code.to_s.upcase)
  core.taxonomicStatus          = (taxon_name.unavailable_or_invalid? ? nil : 'accepted')
  core.nomenclaturalStatus      = (taxon_name.classification_invalid_or_unavailable? ? nil : 'available') # needs tweaking
  core.scientificName           = taxon_name.cached
  core.scientificNameAuthorship = taxon_name.cached_author_year
  core.scientificNameID         = taxon_name.identifiers.first.identifier
  core.taxonRank                = taxon_name.rank
  core.namePublishedIn          = taxon_name.source.cached
  core
end

- (Object) otu_name



144
145
146
147
148
149
150
151
152
# File 'app/models/otu.rb', line 144

def otu_name
  if !name.blank?
    name
  elsif !self.taxon_name_id.nil?
    self.taxon_name.cached_html_name_and_author_year
  else
    nil
  end
end

- (Object) sv_duplicate_otu (protected)



175
176
177
178
179
180
# File 'app/models/otu.rb', line 175

def sv_duplicate_otu
  unless Otu.with_taxon_name_id(self.taxon_name_id).with_name(self.name).not_self(self).with_project_id(self.project_id).empty?
    soft_validations.add(:taxon_name_id, 'Duplicate Taxon and Name combination')
    soft_validations.add(:name, 'Duplicate Taxon and Name combination')
  end
end

- (Object) sv_taxon_name (protected)



171
172
173
# File 'app/models/otu.rb', line 171

def sv_taxon_name
  soft_validations.add(:taxon_name_id, 'Taxon is not selected') if self.taxon_name_id.nil?
end