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



27
28
29
# File 'app/models/dataset_record/darwin_core/occurrence.rb', line 27

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

.match_existingObject



31
32
33
# File 'app/models/dataset_record/darwin_core/occurrence.rb', line 31

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

Instance Method Details

#execute(origins, parent, name) ⇒ Object



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

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:



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

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