app/models/dataset_record/darwin_core/taxon.rb
class DatasetRecord::DarwinCore::Taxon < DatasetRecord::DarwinCore
KNOWN_KEYS_COMBINATIONS = [
%i{uninomial},
%i{uninomial rank parent},
%i{genus species},
%i{genus species infraspecies},
%i{genus subgenus species},
%i{genus subgenus species infraspecies}
].freeze
PARSE_DETAILS_KEYS = %i(uninomial genus species infraspecies).freeze
ORIGINAL_COMBINATION_RANKS = {
genus: 'TaxonNameRelationship::OriginalCombination::OriginalGenus',
subgenus: 'TaxonNameRelationship::OriginalCombination::OriginalSubgenus',
species: 'TaxonNameRelationship::OriginalCombination::OriginalSpecies',
subspecies: 'TaxonNameRelationship::OriginalCombination::OriginalSubspecies',
variety: 'TaxonNameRelationship::OriginalCombination::OriginalVariety',
form: 'TaxonNameRelationship::OriginalCombination::OriginalForm'
}.freeze
# rubocop:disable Metric/MethodLength
def import(dwc_data_attributes = {})
super
begin
DatasetRecord.transaction(requires_new: true) do
self.metadata.delete('error_data')
nomenclature_code = get_field_value('nomenclaturalCode')&.downcase&.to_sym || import_dataset.default_nomenclatural_code
unless Ranks::CODES.include?(nomenclature_code)
raise DarwinCore::InvalidData.new(
{ "nomenclaturalCode": ["Unrecognized nomenclatural code #{get_field_value('nomenclaturalCode')}"] }
)
end
# parse_results_details = Biodiversity::Parser.parse(get_field_value('scientificName') || '')[:details]&.values&.first
parse_results = Biodiversity::Parser.parse(get_field_value(:scientificName) || '')
parse_results_details = parse_results[:details]
parse_results_details = (parse_results_details.keys - PARSE_DETAILS_KEYS).empty? ? parse_results_details.values.first : nil if parse_results_details
raise DarwinCore::InvalidData.new({
"scientificName": parse_results[:qualityWarnings] ?
parse_results[:qualityWarnings].map { |q| q[:warning] } :
['Unable to parse scientific name. Please make sure it is correctly spelled.']
}) unless (1..3).include?(parse_results[:quality]) && parse_results_details&.is_a?(Hash)
raise 'UNKNOWN NAME DETAILS COMBINATION' unless KNOWN_KEYS_COMBINATIONS.include?(parse_results_details.keys - [:authorship])
name_key = parse_results_details[:uninomial] ? :uninomial : (parse_results_details.keys - [:authorship]).last
name_details = parse_results_details[name_key]
name = name_details.kind_of?(Array) ? name_details.first[:value] : name_details
authorship = parse_results_details.dig(:authorship, :normalized) || get_field_value('scientificNameAuthorship')
author_name = nil
year = nil
# split authorship into name and year
if authorship.present?
if nomenclature_code == :iczn
if (authorship_matchdata = authorship.match(/\(?(?<author>.+),? (?<year>\d{4})?\)?/))
# regex will include comma, no easy way around it
author_name = authorship_matchdata[:author].delete_suffix(',')
year = authorship_matchdata[:year]
# author name should be wrapped in parentheses if the verbatim authorship was
if authorship.start_with?('(') and authorship.end_with?(')')
author_name = '(' + author_name + ')'
end
end
else
# Fall back to simple name + date parsing if not iczn
author_name = Utilities::Strings.verbatim_author(authorship)
year = Utilities::Strings.year_of_publication(authorship)
end
end
if year && (name_published_in_year = get_field_value('namePublishedInYear')) &&
year != name_published_in_year
raise DarwinCore::InvalidData.new(
{ "namePublishedInYear": ["parsed year from scientificName or scientificNameAuthorship (#{year}) "\
"does not match namePublishedInYear (#{name_published_in_year})"]
})
end
year ||= get_field_value('namePublishedInYear')
# TODO validate that rank is a real rank, otherwise Combination will crash on find_or_initialize_by
rank = get_field_value('taxonRank')
is_hybrid = metadata['is_hybrid'] # TODO: NO...
if metadata['parent'].nil?
if self.import_dataset.use_existing_hierarchy?
protonym_attributes = { name:, #
cached: get_field_value(:scientificName),
rank_class: Ranks.lookup(nomenclature_code, rank),
verbatim_author: author_name,
year_of_publication: year}
potential_protonyms = TaxonName.where(protonym_attributes.merge({project:})) # merged project here so data is not leaked in error messages.
if potential_protonyms.count == 1
parent = potential_protonyms.first.parent
elsif potential_protonyms.count > 1
matching_protonyms = potential_protonyms.map { |proto| "[id: #{proto.id} #{proto.cached_html_name_and_author_year}]" }.join(', ')
raise DarwinCore::InvalidData.new(
{ "parentNameUsageID": ["parent ID is blank, 'use existing taxon hierarchy' is enabled in settings, " \
"and multiple TaxonNames matched #{protonym_attributes}: #{matching_protonyms}"] })
else
raise DarwinCore::InvalidData.new(
{ "parentNameUsageID": ["parent ID is blank, 'use existing taxon hierarchy' is enabled in settings, " \
"and no TaxonNames matched #{protonym_attributes}"] })
end
else
parent = project.root_taxon_name
end
else
parent = TaxonName.find(get_parent.metadata['imported_objects']['taxon_name']['id'])
end
if metadata['type'] == 'protonym'
# if the name is a synonym, we should use the valid taxon's rank and parent
# I *think* it's ok to do the same for homonyms, since we could have case where homonym's parent is a synonym,
# and it has been moved from species to subspecies rank.
# we fetch parent from the source file when calculating original combination, so it's ok to modify it here.
if metadata['has_external_accepted_name']
valid_name = get_taxon_name_from_taxon_id(get_field_value(:acceptedNameUsageID))
rank = valid_name.rank
parent = valid_name.parent
elsif parent.is_a? Combination # this can happen when the name is unavailable, it's not a synonym so it doesn't point to anything else
parent = parent.finest_protonym
end
taxon_name = Protonym.find_or_initialize_by({
name:,
parent:,
rank_class: Ranks.lookup(nomenclature_code, rank),
# also_create_otu: false,
verbatim_author: author_name,
year_of_publication: year,
project:
})
unless taxon_name.persisted?
taxon_name.taxon_name_classifications.build(type: TaxonNameClassification::Icn::Hybrid) if is_hybrid
taxon_name.data_attributes.build(import_predicate: 'DwC-A import metadata', type: 'ImportAttribute', value: {
scientificName: get_field_value('scientificName'),
scientificNameAuthorship: get_field_value('scientificNameAuthorship'),
taxonRank: get_field_value('taxonRank'),
metadata:
})
taxon_name.save!
end
# make OC relationships to OC ancestors
# can't make original combination with Root or if matching pre-existing taxon name
# Do not make original combination if we assumed the value.
if metadata['parent'].present? && (metadata.fetch('create_original_combination', true) == true)
# Loop through parents of original combination based on parentNameUsageID, not TW parent
# this way we get the name as intended, not with any valid/current names
original_combination_parents = [find_by_taxonID(get_original_combination.metadata['parent'])].compact
# build list of parent DatasetRecords
if original_combination_parents.size > 0
while (next_parent = find_by_taxonID(original_combination_parents[-1]&.metadata['parent']))
original_combination_parents << next_parent
end
end
# in cases where the taxon original combination is subgenus of self eg Sima (Sima), the first parent of the list
# should be dropped because it hasn't been imported yet
original_combination_parents = original_combination_parents.drop_while {|p| p.status != 'Imported' }
# convert DatasetRecords into list of Protonyms
original_combination_parents.map! do |p|
h = {}
h[:protonym] = TaxonName.find(p.metadata['imported_objects']['taxon_name']['id'])
h[:rank] = DatasetRecordField.where(dataset_record_id: p)
.at(get_field_mapping(:taxonRank))
.pick(:value)
.downcase
h
end
original_combination_parents.each do |ancestor|
ancestor_protonym = ancestor[:protonym]
ancestor_rank = ancestor[:rank]
# If OC parent is combination, need to create relationship for lowest element
if ancestor_protonym.is_a?(Combination)
ancestor_protonym = ancestor[:protonym].finest_protonym
end
if (rank_in_type = ORIGINAL_COMBINATION_RANKS[ancestor_rank&.downcase&.to_sym])
# if the subgenus is newer than taxon_name's authorship, skip it (the name must have been classified in the subgenus later)
next if ancestor_rank&.downcase&.to_sym == :subgenus &&
!ancestor_protonym.year_integer.nil? &&
!taxon_name.year_integer.nil? &&
ancestor_protonym.year_integer > taxon_name.year_integer
TaxonNameRelationship.find_or_create_by!(type: rank_in_type, subject_taxon_name: ancestor_protonym, object_taxon_name: taxon_name)
end
end
end
# don't create OC relationship with self if OC was assumed, default to true for any datasets
# created before this feature existed (since creating OC was always expected then)
if metadata.fetch('create_original_combination', true)
# when creating the OC record pointing to self,
# can't assume OC rank is same as valid rank, need to look at OC row to find real rank
# This is easier for the end-user than adding OC to protonym when importing the OC row,
# but might be more complex to code
# get OC dataset_record_id so we can pull the taxonRank from it.
oc_dataset_record_id = import_dataset.core_records_fields
.at(get_field_mapping(:taxonID))
.having_value(get_field_value(:originalNameUsageID))
.pick(:dataset_record_id)
oc_protonym_rank = import_dataset.core_records_fields
.where(dataset_record_id: oc_dataset_record_id)
.at(get_field_mapping(:taxonRank))
.pick(:value)
.downcase.to_sym
if ORIGINAL_COMBINATION_RANKS.has_key?(oc_protonym_rank)
TaxonNameRelationship.create_with(subject_taxon_name: taxon_name).find_or_create_by!(
type: ORIGINAL_COMBINATION_RANKS[oc_protonym_rank],
object_taxon_name: taxon_name)
# detect if current name rank is genus and original combination is with self at subgenus level, eg Aus (Aus)
# if so, generate OC relationship with genus (since oc_protonym_rank will be subgenus)
if oc_protonym_rank == :subgenus && get_field_value('taxonRank').downcase == 'genus' &&
(get_original_combination&.metadata['parent'] == get_field_value('taxonID'))
TaxonNameRelationship.create_with(subject_taxon_name: taxon_name).find_or_create_by!(
type: ORIGINAL_COMBINATION_RANKS[:genus],
object_taxon_name: taxon_name)
end
end
end
# if taxonomicStatus is a synonym or homonym, create the relationship to acceptedNameUsageID
if metadata['has_external_accepted_name']
valid_name = get_taxon_name_from_taxon_id(get_field_value(:acceptedNameUsageID))
synonym_classes = {
iczn: {
synonym: 'TaxonNameRelationship::Iczn::Invalidating::Synonym',
homonym: 'TaxonNameRelationship::Iczn::Invalidating::Synonym::Objective::ReplacedHomonym',
misspelling: 'TaxonNameRelationship::Iczn::Invalidating::Usage::Misspelling',
'original misspelling': 'TaxonNameRelationship::Iczn::Invalidating::Usage::IncorrectOriginalSpelling',
# invalid can be either a relationship or classification, depending on if 'has_external_accepted_name' is true or not
invalid: 'TaxonNameRelationship::Iczn::Invalidating'
},
# TODO support other nomenclatural codes
# icnp: {
# synonym: "TaxonNameRelationship::Icnp::Unaccepting::Synonym",
# homonym: "TaxonNameRelationship::Icnp::Unaccepting::Homonym"
# },
# icn: {
# synonym: "TaxonNameRelationship::Icn::Unaccepting::Synonym",
# homonym: "TaxonNameRelationship::Icn::Unaccepting::Homonym"
# }
}.freeze
if (status = get_field_value(:taxonomicStatus)&.downcase)
# workaround to handle cases where Protonym is a synonym, but row marked as synonym has different rank/parent
# so we use a row that does as the protonym instead. That row could have some other status, but
# we know it's a synonym.
if metadata['is_synonym']
status = :synonym
end
type = synonym_classes[nomenclature_code][status.to_sym]
raise DarwinCore::InvalidData.new(
{ "taxonomicStatus": ['acceptedNameUsageID refers to a different protonym, ' \
"but status #{status} did not match synonym, homonym, invalid, misspelling or original misspelling."] }) if type.nil?
taxon_name.taxon_name_relationships.find_or_initialize_by(object_taxon_name: valid_name, type:)
if status.to_s == 'synonym'
# if synonym and not same rank as valid, and not original combination,
# create a combination with the old parent and rank
if (old_rank = get_field_value('taxonRank').downcase) != rank.downcase &&
metadata['original_combination'] != get_field_value('taxonID') &&
ORIGINAL_COMBINATION_RANKS.has_key?(old_rank.downcase.to_sym)
# save taxon so we can create a combination
taxon_name.save!
# stolen from combination handling portion of code
parent_elements = create_parent_element_hash.transform_values {|v| v.is_a?(Combination) ? v.finest_protonym : v}
combination_attributes = { **parent_elements }
combination_attributes[old_rank.downcase] = taxon_name if old_rank
# Can't use find_or_initialize_by because of dynamic parameters, causes query to fail because ranks are not columns in db
# => PG::UndefinedTable: ERROR: missing FROM-clause entry for table "genus"
# LINE 1: ..."taxon_names" WHERE "taxon_names"."type" = $1 AND "genus"."i...
taxon_combination_name = Combination.matching_protonyms(**combination_attributes.transform_values { |v| v.id }).first
taxon_combination_name = Combination.create!(combination_attributes) if taxon_combination_name.nil?
end
end
# Add homonym status (if applicable)
if status == 'homonym'
taxon_name.taxon_name_classifications.find_or_initialize_by(type: 'TaxonNameClassification::Iczn::Available::Invalid::Homonym')
end
else
raise DarwinCore::InvalidData.new({ "taxonomicStatus": ['No taxonomic status, but acceptedNameUsageID has different protonym'] })
end
# if taxonomicStatus is a homonym, invalid, unavailable, excluded, create the status
# if it's incertae sedis, create the relationship
# TODO why have an OR with nil? shouldn't the first condition check that?
elsif get_field_value(:taxonomicStatus) != 'valid' || get_field_value(:taxonomicStatus).nil?
status_types = {
invalid: 'TaxonNameClassification::Iczn::Available::Invalid',
unavailable: 'TaxonNameClassification::Iczn::Unavailable',
excluded: 'TaxonNameClassification::Iczn::Unavailable::Excluded',
'nomen nudum': 'TaxonNameClassification::Iczn::Unavailable::NomenNudum',
ichnotaxon: 'TaxonNameClassification::Iczn::Fossil::Ichnotaxon',
fossil: 'TaxonNameClassification::Iczn::Fossil',
'nomen dubium': 'TaxonNameClassification::Iczn::Available::Valid::NomenDubium'
}.freeze
if (status = get_field_value(:taxonomicStatus)&.downcase)
# if name in incertae sedis, attach to finest level known (usually parent) and add TaxonNameRelationship
if status == 'incertae sedis'
# if user has provided a `TW:TaxonNameRelationship:incertae_sedis_in_rank` field, use that to determine
# which rank of parent should be used for I.S. relationship
if (verbatim_is_rank = get_field_value('TW:TaxonNameRelationship:incertae_sedis_in_rank'))
# must save here so that `ancestor_at_rank` works
# (otherwise could use `ancestors_through_parents` and check rank manually)
taxon_name.save
incertae_sedis_parent = taxon_name.ancestor_at_rank(verbatim_is_rank.downcase)
if incertae_sedis_parent.nil?
available_parent_ranks = taxon_name.ancestors.map { |a| "#{a.rank}: #{a.name}" }.join(', ')
raise DarwinCore::InvalidData.new({ "TW:TaxonNameRelationship:incertae_sedis_in_rank":
["Taxon #{taxon_name.name} does not have a parent at rank #{verbatim_is_rank}.
Available ancestors are #{available_parent_ranks}.".squish] })
end
# Parent should be same as incertae sedis object_taxon
# Supplying a parent taxonID with a different rank than the incertae sedis parent
# will an original combination relationship with the old parent, which
# the ui will render as [Aus] bus, (where the incertae sedis parent is Cus)
taxon_name.parent = incertae_sedis_parent
else
# if parent has uncertain placement in rank, taxon's parent should be changed to whichever taxon the parent's UncertainRelationship is with
incertae_sedis_parent = parent.iczn_uncertain_placement
# if parent doesn't have uncertain placement, make relationship with family or subfamily (FamilyGroup)
incertae_sedis_parent ||= taxon_name.ancestors.with_base_of_rank_class('NomenclaturalRank::Iczn::FamilyGroup').first
# Parent should be same as incertae sedis object_taxon
taxon_name.parent = incertae_sedis_parent
end
taxon_name.taxon_name_relationships.find_or_initialize_by(
object_taxon_name: incertae_sedis_parent,
type: 'TaxonNameRelationship::Iczn::Validating::UncertainPlacement')
else
type = status_types[status.to_sym]
raise DarwinCore::InvalidData.new(
{ "taxonomicStatus": ["Couldn't find a status that matched #{status}.",
"Possible statuses: [#{status_types.keys.join(", ")}]"] }) if type.nil?
taxon_name.taxon_name_classifications.find_or_initialize_by(type:)
end
end
end
# Taxon status might not be "fossil" if synonym, homonym, incertae sedis, etc.
if get_field_value('TW:TaxonNameClassification:Iczn:Fossil')
taxon_name.taxon_name_classifications.find_or_initialize_by(type: 'TaxonNameClassification::Iczn::Fossil')
end
# add gender or part of speech classification if given
if (gender = get_field_value('TW:TaxonNameClassification:Latinized:Gender'))
gender_types = {
masculine: 'TaxonNameClassification::Latinized::Gender::Masculine',
feminine: 'TaxonNameClassification::Latinized::Gender::Feminine',
neuter: 'TaxonNameClassification::Latinized::Gender::Neuter'
}.freeze
gender_classification = gender_types[gender.downcase.to_sym]
raise DarwinCore::InvalidData.new({ "TW:TaxonNameClassification:Latinized:Gender": ["Gender #{gender.downcase} is not one of: masculine, feminine, neuter."] }) if gender_classification.nil?
taxon_name.taxon_name_classifications.find_or_initialize_by(type: gender_classification)
elsif (part_of_speech = get_field_value('TW:TaxonNameClassification:Latinized:PartOfSpeech'))
parts_of_speech_types = {
adjective: 'TaxonNameClassification::Latinized::PartOfSpeech::Adjective',
participle: 'TaxonNameClassification::Latinized::PartOfSpeech::Participle',
'noun in apposition': 'TaxonNameClassification::Latinized::PartOfSpeech::NounInApposition',
'noun in genitive case': 'TaxonNameClassification::Latinized::PartOfSpeech::NounInGenitiveCase'
}.freeze
part_of_speech_classification = parts_of_speech_types[part_of_speech.downcase.to_sym]
raise DarwinCore::InvalidData.new({ "TW:TaxonNameClassification:Latinized:": ["PartOfSpeech #{part_of_speech.downcase} is not one of: adjective, participle, noun in apposition, noun in genitive case."] }) if part_of_speech_classification.nil?
taxon_name.taxon_name_classifications.find_or_initialize_by(type: part_of_speech_classification)
# if taxon has different original combination conjugation, and genus has gender, use OC name.
# It will be conjugated correctly with new genus and the original combination will use the correct conjugation
if oc_dataset_record_id != self.id &&
taxon_name.is_species_rank? &&
taxon_name.ancestor_at_rank('genus').gender_name
oc_name = import_dataset.core_records_fields
.where(dataset_record_id: oc_dataset_record_id)
.at(get_field_mapping(:scientificName))
.pick(:value)
finest_oc_name = oc_name.split.last
# check if OC name is conjugated differently, then see if current name can be conjugated into oc name
if finest_oc_name != name and taxon_name.predict_three_forms.values.include?(finest_oc_name)
taxon_name.name = finest_oc_name
end
end
end
elsif metadata['type'] == 'combination'
# get protonym from staging metadata
protonym_record = find_by_taxonID(metadata['protonym_taxon_id'])
# current_name_record = find_by_taxonID(get_field_value(:originalNameUsageID))
current_name = Protonym.find(protonym_record.metadata['imported_objects']['taxon_name']['id'])
# because Combination uses named arguments, we need to get the ranks of the parent names to create the combination
if parent.is_a?(Combination)
parent_elements = parent.combination_taxon_names.index_by { |protonym| protonym.rank }
else
# parent is a protonym, look at parents in checklist to build combination
parent_elements = create_parent_element_hash
end
combination_attributes = { **parent_elements }
combination_attributes[rank.downcase] = current_name if rank
# Can't use find_or_initialize_by because of dynamic parameters, causes query to fail because ranks are not columns in db
# => PG::UndefinedTable: ERROR: missing FROM-clause entry for table "genus"
# LINE 1: ..."taxon_names" WHERE "taxon_names"."type" = $1 AND "genus"."i...
taxon_name = Combination.matching_protonyms(**combination_attributes.transform_values { |v| v.id }).first
taxon_name = Combination.new(combination_attributes) if taxon_name.nil?
else
raise DarwinCore::InvalidData.new({ "originalNameUsageID": ['Could not determine if name is protonym or combination'] })
end
if taxon_name.save
# TODO add relationships and combinations to this hash
self.metadata[:imported_objects] = { taxon_name: { id: taxon_name.id } }
self.status = 'Imported'
else
self.status = 'Errored'
# if error exist with taxon_name_relationships, add their errors under the main attribute (:taxon_name_relationships)
# eg:
# original error: {:taxon_name_relationships=>["is invalid"]}
# TNR error: [{:object_taxon_name_id=>["The parent Miomyrmecini and the Incertae Sedis placement (Dolichoderinae) should match"]}]
# resulting message: {:taxon_name_relationships=>["is invalid", "The parent Miomyrmecini and the Incertae Sedis placement (Dolichoderinae) should match"]}
# TODO expand to other relationships, like classifications
if taxon_name.errors.messages[:taxon_name_relationships]
# skip relationships with no errors
errored_relationships = taxon_name.taxon_name_relationships.reject { |r| r.errors.empty? }
# add error messages to taxon_name.errors[:taxon_name_relationships]
errored_relationships.each { |r| r.errors.map { |error| taxon_name.errors.add(:taxon_name_relationships, message: error.message) } }
end
self.metadata[:error_data] = {
messages: taxon_name.errors.messages
}
end
save!
if self.status == 'Imported'
# loop over dependants, see if all other dependencies are met, if so mark them as ready
metadata['dependants'].each do |dependant_taxonID|
if dependencies_imported?(dependant_taxonID)
DatasetRecord::DarwinCore::Taxon.where(status: 'NotReady',
id: import_dataset.core_records_fields
.at(get_field_mapping(:taxonID))
.where(value: dependant_taxonID)
.select(:dataset_record_id)
).first&.update!(status: 'Ready')
end
end
end
end
rescue DarwinCore::InvalidData => invalid
self.status = 'Errored'
self.metadata['error_data'] = { messages: invalid.error_data }
rescue ActiveRecord::RecordInvalid => invalid
self.status = 'Errored'
self.metadata['error_data'] = {
messages: invalid.record.errors.messages
}
rescue StandardError => e
raise if Rails.env.development?
self.status = 'Failed'
self.metadata[:exception_data] = {
message: e.message,
backtrace: e.backtrace
}
ensure
save!
end
self
end
private
# Create a hash of parents from checklist
# @return [Hash{Symbol => TaxonName}] hash of ranks and TaxonNames of genus and species rank parents
def create_parent_element_hash
parent = get_parent
# if parent is root, return empty hash
return {} unless parent
parents = [parent]
while (next_parent = find_by_taxonID(parents[-1].metadata['parent']))
parents << next_parent
end
# convert DatasetRecords into hash of rank, protonym pairs
parent_elements = parents.to_h do |p|
[
# Key is rank (as set in checklist file)
DatasetRecordField.where(dataset_record: p)
.at(get_field_mapping(:taxonRank))
&.pick(:value)
&.downcase&.to_sym,
# value is Protonym
TaxonName.find(p.metadata['imported_objects']['taxon_name']['id'])
]
end
parent_elements.filter! { |p_rank, _| ORIGINAL_COMBINATION_RANKS.has_key?(p_rank) }
parent_elements
end
# @return Optional[DatasetRecord::DarwinCore::Taxon, Array<DatasetRecord::DarwinCore::Taxon>]
def get_parent
DatasetRecord::DarwinCore::Taxon.where(id: import_dataset.core_records_fields
.at(get_field_mapping(:taxonID))
.having_value(get_field_value(:parentNameUsageID))
.select(:dataset_record_id)
).first
end
# @return Optional[DatasetRecord::DarwinCore::Taxon, Array<DatasetRecord::DarwinCore::Taxon>]
def get_original_combination
DatasetRecord::DarwinCore::Taxon.where(id: import_dataset.core_records_fields
.at(get_field_mapping(:taxonID))
.having_value(get_field_value(:originalNameUsageID))
.select(:dataset_record_id)
).first
end
# @return Optional[DatasetRecord::DarwinCore::Taxon, Array<DatasetRecord::DarwinCore::Taxon>]
def find_by_taxonID(taxon_id)
DatasetRecord::DarwinCore::Taxon.where(id: import_dataset.core_records_fields
.at(get_field_mapping(:taxonID))
.having_value(taxon_id.to_s)
.select(:dataset_record_id)
).first
end
# @return [TaxonName]
def get_taxon_name_from_taxon_id(taxon_id)
TaxonName.find(DatasetRecord::DarwinCore::Taxon.where(id: import_dataset.core_records_fields
.at(get_field_mapping(:taxonID))
.having_value(taxon_id.to_s)
.select(:dataset_record_id)
).pick(:metadata)['imported_objects']['taxon_name']['id'])
end
# Check if all dependencies of a taxonID are imported
def dependencies_imported?(taxon_id)
dependency_taxon_ids = DatasetRecord::DarwinCore::Taxon.where(id: import_dataset.core_records_fields
.at(get_field_mapping(:taxonID))
.having_value(taxon_id.to_s)
.select(:dataset_record_id)
).pick(:metadata)['dependencies']
DatasetRecord::DarwinCore::Taxon.where(id: import_dataset.core_records_fields
.at(get_field_mapping(:taxonID))
.having_values(dependency_taxon_ids.map { |d| d.to_s })
.select(:dataset_record_id)
).where(status: 'Imported').count == dependency_taxon_ids.length
end
# TODO add restage button/trigger when relevant fields change. Changing an id here means recalculating dependencies
def data_field_changed(index, value)
# if index == get_field_mapping(:parentNameUsageID) && status == "NotReady"
# self.status = "Ready" if %w[Ready Imported].include? get_parent&.status
# end
end
end