Class: Vendor::Biodiversity::Result

Inherits:
Object
  • Object
show all
Defined in:
lib/vendor/biodiversity.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(query_string: nil, project_id: nil, code: :iczn, match_mode: :groups) ⇒ Result

Returns a new instance of Result.



58
59
60
61
62
63
64
65
# File 'lib/vendor/biodiversity.rb', line 58

def initialize(query_string: nil, project_id: nil, code: :iczn, match_mode: :groups)
  @project_id = project_id
  @name = query_string
  @nomenclature_code = code
  @mode = match_mode

  parse if !query_string.blank?
end

Instance Attribute Details

#citationString (readonly)

Returns the bit after ‘ in `.

Returns:

  • (String)

    the bit after ‘ in `



40
41
42
# File 'lib/vendor/biodiversity.rb', line 40

def citation
  @citation
end

#combinationCombination (readonly)

Returns ranks that are unambiguous have their Protonym set.

Returns:

  • (Combination)

    ranks that are unambiguous have their Protonym set



56
57
58
# File 'lib/vendor/biodiversity.rb', line 56

def combination
  @combination
end

#disambiguated_combinationCombination (readonly)

Returns a memoized result of disambiguated_combination.

Returns:

  • (Combination)

    a memoized result of disambiguated_combination



52
53
54
# File 'lib/vendor/biodiversity.rb', line 52

def disambiguated_combination
  @disambiguated_combination
end

#modeObject

how to match

`ranked`: return names at that queried rank only (e.g. only match a subgenus to rank subgenus
`groups`: return names at Group level (species or genus), i.e. a subgenus name in query will match genus OR subgenus in database


20
21
22
# File 'lib/vendor/biodiversity.rb', line 20

def mode
  @mode
end

#nameObject

query string



15
16
17
# File 'lib/vendor/biodiversity.rb', line 15

def name
  @name
end

#nomenclature_codeObject

one of :iczn, :icn, :icnp



26
27
28
# File 'lib/vendor/biodiversity.rb', line 26

def nomenclature_code
  @nomenclature_code
end

#parse_resultObject

the result of a ScientificNameParser parse



29
30
31
# File 'lib/vendor/biodiversity.rb', line 29

def parse_result
  @parse_result
end

#parseableBoolean (readonly)

Returns:

  • (Boolean)


44
45
46
# File 'lib/vendor/biodiversity.rb', line 44

def parseable
  @parseable
end

#project_idObject

project to query against



23
24
25
# File 'lib/vendor/biodiversity.rb', line 23

def project_id
  @project_id
end

#protonym_resultHash (readonly)

Returns we inspect this internally, so it has to be decoupled.

Returns:

  • (Hash)

    we inspect this internally, so it has to be decoupled



48
49
50
# File 'lib/vendor/biodiversity.rb', line 48

def protonym_result
  @protonym_result
end

#resultHash (readonly)

Returns summary for rendering purposes.

Returns:

  • (Hash)

    summary for rendering purposes



37
38
39
# File 'lib/vendor/biodiversity.rb', line 37

def result
  @result
end

Instance Method Details

#ambiguous_ranksArray

Returns the ranks, as symbols, at which there are multiple (>1) Protonym matches !! subtly different than unambiguous_at, probably should use that?!.

Returns:

  • (Array)

    the ranks, as symbols, at which there are multiple (>1) Protonym matches !! subtly different than unambiguous_at, probably should use that?!



220
221
222
223
224
225
226
# File 'lib/vendor/biodiversity.rb', line 220

def ambiguous_ranks
  a = [ ]
  protonym_result.each do |k, v|
    a.push k if v.count > 1
  end
  a 
end

#authorString?

Returns:

  • (String, nil)


163
164
165
166
167
168
169
# File 'lib/vendor/biodiversity.rb', line 163

def author
  if a = authorship
    Utilities::Strings.authorship_sentence(a[:authors])
  else
    nil
  end
end

#author_word_positionObject



372
373
374
375
376
377
# File 'lib/vendor/biodiversity.rb', line 372

def author_word_position 
  if a = parse_result[:words]
    b = (a.detect { |v| v[:wordType] == 'AUTHOR_WORD'})&.dig(:start)
    p = [name.length, b].compact.min
  end
end

#author_yearObject



171
172
173
# File 'lib/vendor/biodiversity.rb', line 171

def author_year
  [author, year].compact.join(', ')
end

#authorshipHash?

Returns the Biodiversity authorship hash.

Returns:

  • (Hash, nil)

    the Biodiversity authorship hash



158
159
160
# File 'lib/vendor/biodiversity.rb', line 158

def authorship
  parse_result.dig(:authorship, :originalAuth)
end

#basic_scope(rank) ⇒ Scope

Parameters:

  • rank (Symbol)

    like ‘:genus` or `:species`

Returns:

  • (Scope)


257
258
259
260
261
262
# File 'lib/vendor/biodiversity.rb', line 257

def basic_scope(rank)
  Protonym.where(
    project_id: project_id,
    name: string(rank)
  )
end

#build_resultHash

Returns:

  • (Hash)


338
339
340
341
342
343
344
345
346
# File 'lib/vendor/biodiversity.rb', line 338

def build_result
  @result = {}
  @result[:protonyms] = protonym_result
  @result[:parse] = parse_values
  @result[:unambiguous] = is_unambiguous?
  @result[:existing_combination_id] = combination_exists?.try(:id)
  @result[:other_matches] = other_matches
  @result
end

#combination_exists?Combination, false

Returns the Combination, if it exists.

Returns:

  • (Combination, false)

    the Combination, if it exists



364
365
366
367
368
369
370
# File 'lib/vendor/biodiversity.rb', line 364

def combination_exists?
  if is_unambiguous?
    Combination.match_exists?(**combination.protonym_ids_params) # TODO: pass name?
  else
    false
  end
end

#detailHash

Returns:

  • (Hash)


103
104
105
# File 'lib/vendor/biodiversity.rb', line 103

def detail
  parse_result[:details] || {}
end

#disambiguate_combination(target_protonym_ids = {}) ⇒ Combination

Parameters:

  • target_protonym_ids (Hash) (defaults to: {})

    like like ‘123, species: 345` Given a targeted list of ids checks to see if

    a) there is an *ambiguous* result at the rank AND
    b) there is a Protonym with the id provided in the ambiguous result
    

    If a and b are both true then the combination once ambiguous result is set to the id provided in targeted_protonym_ids

Returns:



234
235
236
237
238
239
240
241
242
243
244
245
# File 'lib/vendor/biodiversity.rb', line 234

def disambiguate_combination(target_protonym_ids = {})
  return nil unless target_protonym_ids.any?
  c = combination
  b = ambiguous_ranks

  target_protonym_ids.each do |rank, id|
    if b.include?(rank)
      c.send("#{rank}_id=", id) if protonym_result[rank].map(&:id).include?(id)
    end
  end
  @disambiguated_combination = c
end

#finest_rankSymbol?

Returns like ‘:genus`.

Returns:

  • (Symbol, nil)

    like ‘:genus`



149
150
151
152
153
154
# File 'lib/vendor/biodiversity.rb', line 149

def finest_rank
  RANKS.reverse_each do |k|
    return k if send(k)
  end
  nil
end

#formString?

Returns:

  • (String, nil)


133
134
135
# File 'lib/vendor/biodiversity.rb', line 133

def form
  infraspecies('f.')
end

#genusString?

Returns:

  • (String, nil)


108
109
110
# File 'lib/vendor/biodiversity.rb', line 108

def genus
  parse_result[:words]&.detect { |w| %w{UNINOMIAL GENUS}.include?(w[:wordType]) }&.dig(:normalized)
end

#grouped_protonyms(rank) ⇒ Scope

Parameters:

  • rank (Symbol)

    like ‘:genus` or `:species` Protonyms grouped by nomenclatural group, for a rank

Returns:

  • (Scope)


286
287
288
289
290
291
292
293
294
295
296
297
# File 'lib/vendor/biodiversity.rb', line 286

def grouped_protonyms(rank)
  s = case rank
      when :genus, :subgenus
        basic_scope(rank).is_genus_group
      when :species, :subspecies, :variety, :form
        basic_scope(rank).is_species_group
      else
        Protonym.none
      end

  (is_authored? && finest_rank == rank) ? scope_to_author_year(s) : s
end

#infraspecies(biodiversity_rank) ⇒ String?

Returns:

  • (String, nil)


138
139
140
# File 'lib/vendor/biodiversity.rb', line 138

def infraspecies(biodiversity_rank)
  detail.dig(:infraspecies, :infraspecies)&.detect { |e| e[:rank] == biodiversity_rank }&.dig(:value)
end

#is_authored?Boolean

Returns:

  • (Boolean)


199
200
201
# File 'lib/vendor/biodiversity.rb', line 199

def is_authored?
  author_year.size > 0
end

#is_unambiguous?Boolean

Returns true if for each parsed piece of there name there is 1 and only 1 result.

Returns:

  • (Boolean)

    true if for each parsed piece of there name there is 1 and only 1 result



189
190
191
192
193
194
195
196
# File 'lib/vendor/biodiversity.rb', line 189

def is_unambiguous?
  RANKS.each do |r|
    if !send(r).nil?
      return false unless !send(r).nil? && !unambiguous_at?(r).nil?
    end
  end
  true
end

#name_countInteger

Returns the total monominals in the epithet.

Returns:

  • (Integer)

    the total monominals in the epithet



144
145
146
# File 'lib/vendor/biodiversity.rb', line 144

def name_count 
  (detail[detail.keys.first].keys - [:authorship]).count
end

#name_without_author_yearObject



379
380
381
382
383
384
385
# File 'lib/vendor/biodiversity.rb', line 379

def name_without_author_year
  pos = author_word_position
  # author_word doesn't point to parens if any
  offset = pos > 0 && '(' == name[pos-1] ? 2 : 1

  name[0..pos - offset].strip
end

#other_matchesHash

Returns ‘:verbatim` - names that have verbatim supplied, these should be the only names NOT parsed that user is interested in `:subgenus` - names that exactly match a subgenus, these are potential new combinations as Genus alone `:original_combination` - names that exactly match the original combination.

Returns:

  • (Hash)

    ‘:verbatim` - names that have verbatim supplied, these should be the only names NOT parsed that user is interested in `:subgenus` - names that exactly match a subgenus, these are potential new combinations as Genus alone `:original_combination` - names that exactly match the original combination



391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
# File 'lib/vendor/biodiversity.rb', line 391

def other_matches
  h = { 
    verbatim: [],
    subgenus: [], 
    original: []
  }

  h[:verbatim] = TaxonName.where(project_id: project_id, cached: name_without_author_year).
    where('verbatim_name is not null').order(:cached).all.to_a if parseable
  
  h[:subgenus] = Protonym.where(
    project_id: project_id, 
    name: genus, 
    rank_class: Ranks.lookup(nomenclature_code, :subgenus)
  ).all.to_a

  h[:original_combination] = Protonym.where(project_id: project_id). 
    where( cached_original_combination: name_without_author_year
          ).all.to_a if parseable

  h
end

#parse@parse_result

Returns a Biodiversity name parser result.

Returns:



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/vendor/biodiversity.rb', line 69

def parse
  @combination = nil
  @disambiguated_combination = nil

  n, @citation = preparse

  begin
    @parse_result ||= ::Biodiversity::Parser.parse(n)
  rescue NoMethodError => e
    case e.message
    when /canonical/
      @parseable = false 
    else
      raise
    end
  end

  @parse_result[:scientificName] = parse_result[:normalized]
  @parse_result
end

#parse_valuesHash

Returns:

  • (Hash)


320
321
322
323
324
325
326
327
328
329
# File 'lib/vendor/biodiversity.rb', line 320

def parse_values
  h = {
    author: author,
    year: year
  }
  RANKS.each do |r|
    h[r] = send(r)
  end
  h
end

#preparseArray

Returns TODO: deprecate.

Returns:

  • (Array)

    TODO: deprecate



98
99
100
# File 'lib/vendor/biodiversity.rb', line 98

def preparse
  name.split(' in ')
end

#protonyms(rank) ⇒ Scope

Parameters:

  • rank (Symbol)

    like ‘:genus` or `:species`

Returns:

  • (Scope)


266
267
268
269
270
271
272
273
274
275
# File 'lib/vendor/biodiversity.rb', line 266

def protonyms(rank)
  case mode
  when :ranked
    ranked_protonyms(rank)
  when :groups
    grouped_protonyms(rank)
  else
    Protonym.none
  end
end

#ranked_protonyms(rank) ⇒ Scope

Returns Protonyms at a given rank.

Returns:

  • (Scope)

    Protonyms at a given rank



279
280
281
# File 'lib/vendor/biodiversity.rb', line 279

def ranked_protonyms(rank)
  basic_scope(rank).where(rank_class: Ranks.lookup(nomenclature_code, rank))
end

#scope_to_author_year(scope) ⇒ Scope

Returns if there is an exact author year match scope it to that match, otherwise

ignore the author year.

Returns:

  • (Scope)

    if there is an exact author year match scope it to that match, otherwise

    ignore the author year
    


302
303
304
305
# File 'lib/vendor/biodiversity.rb', line 302

def scope_to_author_year(scope)
  t = scope.where('(cached_author_year = ? OR cached_author_year = ?)', author_year, author_year.gsub(' & ', ' and '))
  t.count > 0 ? t : scope
end

#set_combinationObject



354
355
356
357
358
359
360
# File 'lib/vendor/biodiversity.rb', line 354

def set_combination
  c = Combination.new
  RANKS.each do |r|
    c.send("#{r}=", unambiguous_at?(r))
  end
  c
end

#speciesString?

Returns:

  • (String, nil)


118
119
120
# File 'lib/vendor/biodiversity.rb', line 118

def species
  parse_result[:words]&.detect { |w| 'SPECIES' == w[:wordType] }&.dig(:normalized)
end

#string(rank = nil) ⇒ String, false

Returns a wrapper on string returning methods.

Parameters:

  • rank (Symbol, String) (defaults to: nil)

    rank is one of ‘genus`, `subgenus`, `species, `subspecies`, `variety`, `form`

Returns:

  • (String, false)

    a wrapper on string returning methods



251
252
253
# File 'lib/vendor/biodiversity.rb', line 251

def string(rank = nil)
  send(rank)
end

#subgenusString?

Returns:

  • (String, nil)


113
114
115
# File 'lib/vendor/biodiversity.rb', line 113

def subgenus
  (parse_result[:words] || [])[1..]&.detect { |w| %w{UNINOMIAL INFRA_GENUS}.include?(w[:wordType]) }&.dig(:normalized)
end

#subspeciesString?

Returns:

  • (String, nil)


123
124
125
# File 'lib/vendor/biodiversity.rb', line 123

def subspecies
  infraspecies(nil)
end

#unambiguous_at?(rank) ⇒ Protonym?

Returns true if there is a single matching result or nominotypical subs.

Parameters:

  • rank (Symbol)

    like ‘:genus` or `:species`

Returns:

  • (Protonym, nil)

    true if there is a single matching result or nominotypical subs



206
207
208
209
210
211
212
213
214
215
# File 'lib/vendor/biodiversity.rb', line 206

def unambiguous_at?(rank)
  return protonym_result[rank].first if protonym_result[rank].size == 1
  if protonym_result[rank].size == 2
    n1 = protonym_result[rank].first
    n2 = protonym_result[rank].last
    return n2 if n2.nominotypical_sub_of?(n1) 
    return n1 if n1.nominotypical_sub_of?(n2) 
  end
  nil 
end

#varietyString?

Returns:

  • (String, nil)


128
129
130
# File 'lib/vendor/biodiversity.rb', line 128

def variety
  infraspecies('var.')
end

#yearString?

Returns:

  • (String, nil)


176
177
178
179
180
# File 'lib/vendor/biodiversity.rb', line 176

def year
  if a = authorship
    return a.dig(:year, :year)
  end
end