Class: Autoselect::TaxonName::Levels::CatalogueOfLife

Inherits:
Level
  • Object
show all
Includes:
Levels::ColRecordInfo
Defined in:
lib/autoselect/taxon_name/levels/catalogue_of_life.rb

Constant Summary

Constants inherited from Level

Level::DEFAULT_FUSE_MS, Level::EXTERNAL_FUSE_MS, Level::MINIMUM_RESULTS

Instance Method Summary collapse

Methods included from Levels::ColRecordInfo

#record_info, #record_info_html

Methods inherited from Level

#fuse_ms, #metadata, #minimum_results, #model_key, #record_info, #record_info_html

Instance Method Details

#call(term:, operator: nil, project_id: nil, user_id: nil, dataset_id: nil, **_kwargs) ⇒ Array<OpenStruct>

Returns pseudo-records compatible with TaxonName autoselect format_results.

Parameters:

  • term (String)
  • project_id (Integer, nil) (defaults to: nil)
  • dataset_id (String, nil) (defaults to: nil)

    CoL dataset ID from user preferences; falls back to default when nil

Returns:

  • (Array<OpenStruct>)

    pseudo-records compatible with TaxonName autoselect format_results



39
40
41
42
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
# File 'lib/autoselect/taxon_name/levels/catalogue_of_life.rb', line 39

def call(term:, operator: nil, project_id: nil, user_id: nil, dataset_id: nil, **_kwargs)
  raw = ::Vendor::Colrapi.search(term, dataset_id:)
  results = raw['result'] || []
  return [] if results.empty?

  results.map do |col_result|
    # Flat nameusage structure: 'id', 'status', 'name' hash, 'label', 'labelHtml'
    col_name   = col_result.dig('name', 'scientificName') || col_result['label']
    col_rank   = col_result.dig('name', 'rank')&.downcase
    col_name   = ::Vendor::Colrapi.extract_subgenus_name(col_name) if col_rank == 'subgenus'
    col_status = col_result['status']
    col_key    = col_result['id']
    extension  = ::Vendor::Colrapi.build_extension(col_result, project_id, dataset_id:)

    # Annotate whether the target name already exists in this project so the
    # confirmation modal can offer "Select" instead of "Create" when nothing
    # needs to be created.
    if project_id.present?
      existing = ::TaxonName.where(project_id:, cached: col_name).first
      if existing
        extension = extension.merge(
          target_taxonworks_id: existing.id,
          target_global_id: existing.to_global_id.to_s
        )
      end
    end

    # Flag when the target's rank is not resolvable in TaxonWorks (e.g. 'domain').
    # ColCreator silently skips such rows, so the modal needs to know up-front.
    extension = extension.merge(target_rank_unknown: true) unless rank_resolvable?(col_rank)

    OpenStruct.new(
      id: nil,
      cached: col_name,
      name: col_name,
      rank_string: nil,
      cached_valid_taxon_name_id: nil,
      _col_extension: extension
    )
  end
end

#col_author_year(record) ⇒ String? (private)

The display author+year for a CoL result.

CoL renders a recombination's authorship as the original (basionym) author+year wrapped in parens, followed by the combination authorship, e.g. "(Chatton, 1925) Whittaker & Margulis, 1978". Per ICZN convention we show only the original author+year, keeping the parens that signal the recombination. A name described in its original genus has no leading parenthetical and is shown verbatim, e.g. "Linnaeus, 1758".

Returns:

  • (String, nil)


93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/autoselect/taxon_name/levels/catalogue_of_life.rb', line 93

def col_author_year(record)
  ext = record._col_extension
  return nil unless ext

  authorship = ext[:col_authorship].presence
  return nil unless authorship

  if (leading_parenthetical = authorship[/\A\([^)]*\)/])
    leading_parenthetical
  else
    authorship
  end
end

#descriptionObject



16
17
18
# File 'lib/autoselect/taxon_name/levels/catalogue_of_life.rb', line 16

def description
  'Search the Catalogue of Life for matching names. Returns external results with alignment data for confirmation.'
end

#external?Boolean

Returns:

  • (Boolean)


20
21
22
# File 'lib/autoselect/taxon_name/levels/catalogue_of_life.rb', line 20

def external?
  true
end

#keyObject



8
9
10
# File 'lib/autoselect/taxon_name/levels/catalogue_of_life.rb', line 8

def key
  :catalogue_of_life
end

#labelObject



12
13
14
# File 'lib/autoselect/taxon_name/levels/catalogue_of_life.rb', line 12

def label
  'Catalogue of Life'
end

#rank_resolvable?(rank_string) ⇒ Boolean (private)

Returns:

  • (Boolean)


107
108
109
110
111
112
113
# File 'lib/autoselect/taxon_name/levels/catalogue_of_life.rb', line 107

def rank_resolvable?(rank_string)
  return false if rank_string.blank?
  r = rank_string.to_s.downcase
  [:ICZN_LOOKUP, :ICN_LOOKUP, :ICNP_LOOKUP, :ICVCN_LOOKUP].any? do |key|
    Object.const_get("::#{key}")[r].present?
  end
end

#record_label(record) ⇒ Object



24
25
26
# File 'lib/autoselect/taxon_name/levels/catalogue_of_life.rb', line 24

def record_label(record)
  [record.cached.to_s.presence, col_author_year(record)].compact.join(' ')
end

#record_label_html(record) ⇒ Object



28
29
30
31
32
33
# File 'lib/autoselect/taxon_name/levels/catalogue_of_life.rb', line 28

def record_label_html(record)
  [record.cached.to_s.presence, col_author_year(record)]
    .compact
    .map { |part| ERB::Util.html_escape(part) }
    .join(' ')
end