Class: TaxonWorks::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.



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

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 `



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

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



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

def combination
  @combination
end

#disambiguated_combinationCombination (readonly)

Returns a memoized result of disambiguated_combination.

Returns:

  • (Combination)

    a memoized result of disambiguated_combination



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

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


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

def mode
  @mode
end

#nameObject

query string



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

def name
  @name
end

#nomenclature_codeObject

one of :iczn, :icn, :icnp



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

def nomenclature_code
  @nomenclature_code
end

#parse_resultObject

the result of a ScientificNameParser parse



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

def parse_result
  @parse_result
end

#parseableBoolean (readonly)

Returns:

  • (Boolean)


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

def parseable
  @parseable
end

#project_idObject

project to query against



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

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



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

def protonym_result
  @protonym_result
end

#resultHash (readonly)

Returns summary for rendering purposes.

Returns:

  • (Hash)

    summary for rendering purposes



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

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?!



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

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

#authorString?

Returns:

  • (String, nil)


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

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

#author_word_positionObject



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

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



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

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

#authorshipHash?

Returns the Biodiversity authorship hash.

Returns:

  • (Hash, nil)

    the Biodiversity authorship hash



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

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

#basic_scope(rank) ⇒ Scope

Parameters:

  • rank (Symbol)

    like ‘:genus` or `:species`

Returns:

  • (Scope)


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

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

#build_resultHash

Returns:

  • (Hash)


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

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



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

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

#detailHash

Returns:

  • (Hash)


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

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:



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

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`



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

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

#formString?

Returns:

  • (String, nil)


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

def form
  infraspecies('f.')
end

#genusString?

Returns:

  • (String, nil)


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

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)


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

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)


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

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

#is_authored?Boolean

Returns:

  • (Boolean)


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

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



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

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



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

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

#name_without_author_yearObject



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

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



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

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:



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

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)


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

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



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

def preparse
  name.split(' in ')
end

#protonyms(rank) ⇒ Scope

Parameters:

  • rank (Symbol)

    like ‘:genus` or `:species`

Returns:

  • (Scope)


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

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



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

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
    


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

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



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

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

#speciesString?

Returns:

  • (String, nil)


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

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



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

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

#subgenusString?

Returns:

  • (String, nil)


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

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

#subspeciesString?

Returns:

  • (String, nil)


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

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



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

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)


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

def variety
  infraspecies('var.')
end

#yearString?

Returns:

  • (String, nil)


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

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