Class: DatasetRecord::DarwinCore::Occurrence::ImportProtonym

Inherits:
Object
  • Object
show all
Defined in:
app/models/dataset_record/darwin_core/occurrence.rb

Direct Known Subclasses

CreateIfNotExists, MatchExisting

Defined Under Namespace

Classes: CreateIfNotExists, MatchExisting

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.create_if_not_existsObject



95
96
97
# File 'app/models/dataset_record/darwin_core/occurrence.rb', line 95

def self.create_if_not_exists
  @create_if_not_exists ||= CreateIfNotExists.new
end

.match_existingObject



99
100
101
# File 'app/models/dataset_record/darwin_core/occurrence.rb', line 99

def self.match_existing
  @match_existing ||= MatchExisting.new
end

Instance Method Details

#execute(origins, parent, name) ⇒ Object



103
104
105
106
107
# File 'app/models/dataset_record/darwin_core/occurrence.rb', line 103

def execute(origins, parent, name)
  protonym = get_protonym(parent, name)
  raise DatasetRecord::DarwinCore::InvalidData.new(exception_args(origins, parent, name, protonym)) unless protonym&.persisted?
  protonym
end

#get_protonym(parent, name) ⇒ Protonym?

Parameters:

Returns:



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
187
188
189
190
191
192
193
194
195
# File 'app/models/dataset_record/darwin_core/occurrence.rb', line 113

def get_protonym(parent, name)
  name = name.except(:rank_class) if name[:rank_class].nil?

  %I(name masculine_name feminine_name neuter_name).inject(nil) do |protonym, field|
    break protonym unless protonym.nil?

    potential_protonyms = Protonym.where(name.slice(:rank_class).merge({ field => name[:name], :parent => parent }))

    # if multiple potential protonyms, this is a homonym situation
    if potential_protonyms.count > 1
      # verbatim author field (if present) applies to finest name only
      if (cached_author = name[:verbatim_author])
        # remove surrounding parentheses if present
        if cached_author.start_with?('(') && cached_author.end_with?(')')
          cached_author = cached_author.delete_prefix('(').delete_suffix(')')
          potential_protonyms_narrowed = potential_protonyms.is_not_original_name
        else
          potential_protonyms_narrowed = potential_protonyms.is_original_name
        end

        potential_protonyms_narrowed = potential_protonyms_narrowed.where(cached_author:)

        if name[:year_of_publication]
          potential_protonyms_narrowed = potential_protonyms_narrowed.where(year_of_publication: name[:year_of_publication])
        end

        # if only one result, everything's ok. Safe to take it as the protonym
        if potential_protonyms_narrowed.count == 1
          potential_protonyms = potential_protonyms_narrowed
        elsif potential_protonyms_narrowed.count == 0
          potential_protonym_strings = potential_protonyms.map { |proto| "[id: #{proto.id} #{proto.cached_html_name_and_author_year}]" }.join(', ')
          error_message =
            ["Multiple matches found for name #{name[:name]}, rank #{name[:rank_class]}, parent #{parent.id} #{parent.cached_html_name_and_author_year}: #{potential_protonym_strings}",
             "No names matched author name #{name[:verbatim_author]}#{(', year ' + name[:year_of_publication].to_s) if name[:year_of_publication]}: "]
          raise DatasetRecord::DarwinCore::InvalidData.new({ 'scientificName' => error_message })
        else
          potential_protonym_strings = potential_protonyms_narrowed.map { |proto| "[id: #{proto.id} #{proto.cached_html_name_and_author_year}]" }.join(', ')
          raise DatasetRecord::DarwinCore::InvalidData.new(
            { 'scientificName' => ["Multiple matches found for name #{name[:name]} and author name #{name[:verbatim_author]}, year #{name[:year_of_publication]}: #{potential_protonym_strings}"] }
          )
        end
      else
        # for intermediate homonyms, skip it, we don't have any info
        return parent
      end
    end

    p = potential_protonyms.first

    # Protonym might not exist, or might have intermediate parent not listed in file
    # if it exists, run more expensive query to see if it has an ancestor matching parent name and rank
    if p.nil? && Protonym.where(name.slice(:rank_class).merge({ field => name[:name] })).where(project_id: parent.project_id).exists?
      if (cached_author = name[:verbatim_author])
        # remove surrounding parentheses if present
        if cached_author.start_with?('(') && cached_author.end_with?(')')
          cached_author = cached_author.delete_prefix('(').delete_suffix(')')
        end
      end

      potential_protonyms = Protonym.where(name.slice(:rank_class, :year_of_publication).merge({ field => name[:name], cached_author: }).compact).with_ancestor(parent)
      if potential_protonyms.count > 1
        return parent
      end
      p = potential_protonyms.first

      # check parent.cached_valid_taxon_name_id if not valid, can have obsolete subgenus Aus (Aus) bus -> Aus bus, bus won't have ancestor (Aus)
      if p.nil? && !parent.cached_is_valid
        p = Protonym.where(name.slice(:rank_class).merge!({ field => name[:name] })).with_ancestor(parent.valid_taxon_name).first
      end

    end

    if p&.cached_misspelling && p.has_misspelling_relationship?
      correct_spelling = TaxonNameRelationship.where_subject_is_taxon_name(p)
                                              .with_type_array(TAXON_NAME_RELATIONSHIP_NAMES_MISSPELLING_ONLY)
                                              .first&.object_taxon_name
      if correct_spelling&.values_at(:name, :masculine_name, :feminine_name, :neuter_name).include?(name[:name])
        return correct_spelling
      end
    end
    p
  end
end