Class: Autoselect::TaxonName::CatalogueOfLifeCreator
- Inherits:
-
Object
- Object
- Autoselect::TaxonName::CatalogueOfLifeCreator
- Defined in:
- lib/autoselect/taxon_name/catalogue_of_life_creator.rb
Overview
Creates TaxonName records (and CoL URI identifiers) from a Catalog of Life selection.
Receives an ordered list of rows (distal → proximal, i.e. kingdom first, target last). Each row represents one name to be created or an existing TaxonName to be used as a parent. Rows with a taxonworks_id are already in the project; if the row immediately above them was just inserted (a rank CoL reports that TaxonWorks' real chain skipped), the existing record is reparented onto it. Otherwise it's used as-is as a parent anchor and left untouched, and it must already be under the row above it - if not, the rows don't describe TaxonWorks' real lineage and the request is refused.
Defined Under Namespace
Classes: CreationError
Instance Method Summary collapse
-
#call ⇒ Hash
{ taxon_name_id: Integer, created_ids: Array
}. -
#create_col_identifier(taxon_name, row) ⇒ Object
private
Records the CoL taxon id as a ChecklistBank URI identifier on the freshly-created name.
-
#initialize(rows:, project_id:, user_id:, col_code: nil) ⇒ CatalogueOfLifeCreator
constructor
A new instance of CatalogueOfLifeCreator.
-
#project_root_id ⇒ Object
private
Returns the TaxonName id of the project's Root node.
-
#refuse_non_accepted ⇒ Object
private
CoL gives a non-accepted name (synonym, misapplied, ...) only its accepted name's classification, nothing of its own, so there's no sound place to create it.
- #refuse_unknown_target_rank ⇒ Object private
-
#resolve_rank_class(rank_string) ⇒ Object
private
Maps a human-readable CoL rank string to a TaxonWorks rank_class string.
Constructor Details
#initialize(rows:, project_id:, user_id:, col_code: nil) ⇒ CatalogueOfLifeCreator
Returns a new instance of CatalogueOfLifeCreator.
49 50 51 52 53 54 |
# File 'lib/autoselect/taxon_name/catalogue_of_life_creator.rb', line 49 def initialize(rows:, project_id:, user_id:, col_code: nil) @rows = rows @project_id = project_id.to_i @user_id = user_id.to_i @col_code = col_code.to_s.downcase.presence end |
Instance Method Details
#call ⇒ Hash
Returns { taxon_name_id: Integer, created_ids: Array
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 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
# File 'lib/autoselect/taxon_name/catalogue_of_life_creator.rb', line 59 def call parent_id = project_root_id # Whether parent_id is a row created in this call (a skipped # unknown-rank row leaves it as it was). parent_created = false created_ids = [] matched_ids = @rows.filter_map { |row| row[:taxonworks_id].presence&.to_i } current_parent_ids = ::TaxonName .where(id: matched_ids, project_id: @project_id) .pluck(:id, :parent_id).to_h unknown_ids = matched_ids - current_parent_ids.keys if unknown_ids.any? raise CreationError.new("Unknown TaxonName id(s) for this project: #{unknown_ids.join(', ')}") end refuse_non_accepted() # An ancestor at a rank TaxonWorks doesn't have is skipped below, but # the target can't be: the result is whatever was created last, so # skipping it would hand back its parent as the record. refuse_unknown_target_rank() ::ActiveRecord::Base.transaction do @rows.each do |row| if row[:taxonworks_id].present? existing_id = row[:taxonworks_id].to_i if current_parent_ids[existing_id] != parent_id existing = ::TaxonName.where(project_id: @project_id).find(existing_id) # Only a row just created directly above (a rank CoL reports # that TaxonWorks' real chain skipped) reattaches this existing # record. Any other mismatch means the rows don't describe # TaxonWorks' real lineage (a stale modal, a hand-made # request) - refuse rather than move a curated record. if !parent_created raise CreationError.new( "#{existing.cached} isn't under the record above it in " \ 'this request; the classification may have changed - ' \ 'reopen and try again.', col_name: row[:col_name], col_id: row[:col_id] ) end begin existing.update!(parent_id:) rescue ::ActiveRecord::RecordInvalid => e raise CreationError.new( "Reparenting #{existing.cached}: #{e.record.errors..join(', ')}", col_name: row[:col_name], col_id: row[:col_id] ) end end parent_id = existing_id parent_created = false next end rank_class = resolve_rank_class(row[:col_rank]) # Skip rows whose rank is entirely unknown to TaxonWorks — we cannot # create a valid Protonym without a rank_class (e.g. CoL-only ranks # like 'domain'). next if rank_class.nil? , year = ::Vendor::Colrapi::NameTranslation .(row[:col_authorship], row[:col_year]) begin tn = ::TaxonName.create!( type: 'Protonym', name: ::Vendor::Colrapi::NameTranslation .protonym_name(row[:col_name], row[:col_rank]), parent_id:, rank_class:, verbatim_author: , year_of_publication: year, project_id: @project_id, by: @user_id, ) rescue ::ActiveRecord::RecordInvalid => e raise CreationError.new( e.record.errors..join(', '), col_name: row[:col_name], col_id: row[:col_id] ) end create_col_identifier(tn, row) if row[:col_id].present? created_ids << tn.id parent_id = tn.id parent_created = true end end { taxon_name_id: parent_id, created_ids: } end |
#create_col_identifier(taxon_name, row) ⇒ Object (private)
Records the CoL taxon id as a ChecklistBank URI identifier on the freshly-created name.
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
# File 'lib/autoselect/taxon_name/catalogue_of_life_creator.rb', line 173 def create_col_identifier(taxon_name, row) ::Identifier::Global::Uri::ChecklistBank.create!( taxon_id: row[:col_id], dataset_id: row[:dataset_id], identifier_object: taxon_name, project_id: @project_id, by: @user_id ) rescue ::ActiveRecord::RecordInvalid => e raise CreationError.new( "CoL identifier for #{row[:col_name]}: " \ "#{e.record.errors..join(', ')}", col_name: row[:col_name], col_id: row[:col_id] ) end |
#project_root_id ⇒ Object (private)
Returns the TaxonName id of the project's Root node.
167 168 169 |
# File 'lib/autoselect/taxon_name/catalogue_of_life_creator.rb', line 167 def project_root_id ::Project.find(@project_id).root_taxon_name.id end |
#refuse_non_accepted ⇒ Object (private)
CoL gives a non-accepted name (synonym, misapplied, ...) only its accepted name's classification, nothing of its own, so there's no sound place to create it. The modal already disables Create for it; this is the server-side guard. A row with a taxonworks_id isn't created, so an existing name is still used.
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 |
# File 'lib/autoselect/taxon_name/catalogue_of_life_creator.rb', line 195 def refuse_non_accepted row = @rows.find do |r| r[:taxonworks_id].blank? && r[:col_status].present? && !::Vendor::Colrapi::COL_ACCEPTED_STATUSES .include?(r[:col_status].to_s.downcase) end return if row.nil? raise CreationError.new( "#{row[:col_name]} is a #{row[:col_status]} in Catalogue of Life, " \ "which has no classification of its own for it - it can't be " \ 'created from here.', col_name: row[:col_name], col_id: row[:col_id] ) end |
#refuse_unknown_target_rank ⇒ Object (private)
212 213 214 215 216 217 218 219 220 221 222 223 |
# File 'lib/autoselect/taxon_name/catalogue_of_life_creator.rb', line 212 def refuse_unknown_target_rank target = @rows.last return if target.nil? || target[:taxonworks_id].present? return if resolve_rank_class(target[:col_rank]) raise CreationError.new( "Rank #{target[:col_rank]} isn't a TaxonWorks rank - " \ "#{target[:col_name]} can't be created.", col_name: target[:col_name], col_id: target[:col_id] ) end |
#resolve_rank_class(rank_string) ⇒ Object (private)
Maps a human-readable CoL rank string to a TaxonWorks rank_class string. Returns nil for unknown ranks.
227 228 229 |
# File 'lib/autoselect/taxon_name/catalogue_of_life_creator.rb', line 227 def resolve_rank_class(rank_string) ::Vendor::Colrapi.rank_class_for(rank_string, col_code: @col_code) end |