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



33
34
35
# File 'app/models/dataset_record/darwin_core/occurrence.rb', line 33

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

.match_existingObject



37
38
39
# File 'app/models/dataset_record/darwin_core/occurrence.rb', line 37

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

Instance Method Details

#execute(origins, parent, name) ⇒ Object



41
42
43
44
45
# File 'app/models/dataset_record/darwin_core/occurrence.rb', line 41

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:



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
# File 'app/models/dataset_record/darwin_core/occurrence.rb', line 51

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
        # potential_protonym_strings = potential_protonyms.map { |proto| "[id: #{proto.id} #{proto.cached_html_name_and_author_year}]" }
        # raise DatasetRecord::DarwinCore::InvalidData.new(
        #   { "scientificName" => ["Intermediate name not present, and multiple matches found: #{potential_protonym_strings.join(', ')}"] }
        # )
      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