Module: CollectionObject::DwcExtensions

Extended by:
ActiveSupport::Concern
Includes:
TaxonworksExtensions
Included in:
CollectionObject, BiologicalCollectionObject
Defined in:
app/models/collection_object/dwc_extensions.rb

Defined Under Namespace

Modules: TaxonworksExtensions

Instance Method Summary collapse

Instance Method Details

TODO: likeley a helper



214
215
216
217
218
219
220
221
222
223
# File 'app/models/collection_object/dwc_extensions.rb', line 214

def api_image_link(image)
  s = ENV['SERVER_NAME']
  if s.nil?
    s ||= 'http://127.0.0.1:3000'
  else
    s = 'https://' + s
  end

  s = s + '/api/v1/images/' + image.image_file_fingerprint # An experiment, use md5 as a proxy for id (also unique id)
end

#dwc_associated_mediaObject



204
205
206
# File 'app/models/collection_object/dwc_extensions.rb', line 204

def dwc_associated_media
  images.collect{|i| api_image_link(i) }.join(CollectionObject::DWC_DELIMITER).presence
end

#dwc_associated_taxaObject



209
210
211
# File 'app/models/collection_object/dwc_extensions.rb', line 209

def dwc_associated_taxa
  dwc_internal_attribute_for(:collection_object, :associatedTaxa)
end

#dwc_casteObject



428
429
430
431
# File 'app/models/collection_object/dwc_extensions.rb', line 428

def dwc_caste
  biocuration_classes.tagged_with_uri(::DWC_ATTRIBUTE_URIS[:caste])
    .pluck(:name)&.join(', ').presence #  TODO: Use delimeter!
end

#dwc_catalog_numberObject



296
297
298
# File 'app/models/collection_object/dwc_extensions.rb', line 296

def dwc_catalog_number
  catalog_number_cached # via delegation
end

#dwc_classObject



498
499
500
# File 'app/models/collection_object/dwc_extensions.rb', line 498

def dwc_class
  taxonomy['class']
end

#dwc_collection_codeObject

TODO: optimize by finding all relevant identifiers in one query, then looping through them



288
289
290
# File 'app/models/collection_object/dwc_extensions.rb', line 288

def dwc_collection_code
  catalog_number_namespace&.verbatim_short_name || catalog_number_namespace&.short_name
end

#dwc_coordinate_uncertainty_in_metersObject

TODO: extend to Georeferences when we understand how to describe spatial uncertainty



260
261
262
263
264
265
266
# File 'app/models/collection_object/dwc_extensions.rb', line 260

def dwc_coordinate_uncertainty_in_meters
  if georeference_attributes[:coordinateUncertaintyInMeters]
    georeference_attributes[:coordinateUncertaintyInMeters]
  else
    collecting_event&.verbatim_geolocation_uncertainty
  end
end

#dwc_countryObject



581
582
583
584
# File 'app/models/collection_object/dwc_extensions.rb', line 581

def dwc_country
  v = try(:collecting_event).try(:geographic_names)
  v[:country] if v
end

#dwc_countyObject



591
592
593
594
# File 'app/models/collection_object/dwc_extensions.rb', line 591

def dwc_county
  v = try(:collecting_event).try(:geographic_names)
  v[:county] if v
end

#dwc_date_identifiedObject

ISO 8601:2004(E).



277
278
279
# File 'app/models/collection_object/dwc_extensions.rb', line 277

def dwc_date_identified
  target_taxon_determination&.date.presence
end

#dwc_dayObject



658
659
660
661
# File 'app/models/collection_object/dwc_extensions.rb', line 658

def dwc_day
  return unless collecting_event
  collecting_event.start_date_day.presence
end

#dwc_decimal_latitudeObject



600
601
602
# File 'app/models/collection_object/dwc_extensions.rb', line 600

def dwc_decimal_latitude
  georeference_attributes[:decimalLatitude]
end

#dwc_decimal_longitudeObject



604
605
606
# File 'app/models/collection_object/dwc_extensions.rb', line 604

def dwc_decimal_longitude
  georeference_attributes[:decimalLongitude]
end

#dwc_end_day_of_yearObject



668
669
670
671
# File 'app/models/collection_object/dwc_extensions.rb', line 668

def dwc_end_day_of_year
  return unless collecting_event
  collecting_event.end_day_of_year.presence
end

#dwc_event_dateObject



633
634
635
636
637
638
639
640
641
642
643
644
645
# File 'app/models/collection_object/dwc_extensions.rb', line 633

def dwc_event_date
  return unless collecting_event
  return if collecting_event.start_date_year.blank? # don't need to check end, it requires start in model

  %w{start_date end_date}
    .map { |d| %w{year month day}
    .map { |p| collecting_event["#{d}_#{p}"] }
    .map { |p| '%02d' % p if p } # At least two digits
    }
      .map { |d| d.compact.join('-') }
      .reject(&:blank?)
      .join('/').presence
end

#dwc_event_idObject



326
327
328
329
# File 'app/models/collection_object/dwc_extensions.rb', line 326

def dwc_event_id
  return nil unless collecting_event
  collecting_event.identifiers.where(type: 'Identifier::Local::Event').first&.cached.presence
end

#dwc_event_remarksObject



199
200
201
# File 'app/models/collection_object/dwc_extensions.rb', line 199

def dwc_event_remarks
  collecting_event&.notes&.collect {|n| n.text}&.join(CollectionObject::DWC_DELIMITER).presence
end

#dwc_event_timeObject



616
617
618
619
620
621
622
623
624
625
626
627
# File 'app/models/collection_object/dwc_extensions.rb', line 616

def dwc_event_time
  return unless collecting_event

  %w{start_time end_time}
    .map { |t| %w{hour minute second}
    .map { |p| collecting_event["#{t}_#{p}"] }
    .map { |p| '%02d' % p if p } # At least two digits
    }
      .map { |t| t.compact.join(':') }
      .reject(&:blank?)
      .join('/').presence
end

#dwc_familyObject



512
513
514
# File 'app/models/collection_object/dwc_extensions.rb', line 512

def dwc_family
  taxonomy['family']
end

#dwc_field_numberObject

Prioritize the formalized version of the identifier for data-sharing purposes



321
322
323
324
# File 'app/models/collection_object/dwc_extensions.rb', line 321

def dwc_field_number
  return nil unless collecting_event
  collecting_event.identifiers.where(type: 'Identifier::Local::FieldNumber').first&.cached || collecting_event&.verbatim_field_number
end

#dwc_footprint_wktObject



233
234
235
# File 'app/models/collection_object/dwc_extensions.rb', line 233

def dwc_footprint_wkt
  georeference_attributes[:footprintWKT]
end

#dwc_genusObject



532
533
534
# File 'app/models/collection_object/dwc_extensions.rb', line 532

def dwc_genus
  taxonomy['genus'] && taxonomy['genus'].compact.join(' ').presence
end

#dwc_geodetic_datumObject



245
246
247
# File 'app/models/collection_object/dwc_extensions.rb', line 245

def dwc_geodetic_datum
  georeference_attributes[:geodeticDatum]
end

#dwc_georeference_protocolObject



677
678
679
# File 'app/models/collection_object/dwc_extensions.rb', line 677

def dwc_georeference_protocol
  georeference_attributes[:georeferenceProtocol]
end

#dwc_georeference_remarksObject



229
230
231
# File 'app/models/collection_object/dwc_extensions.rb', line 229

def dwc_georeference_remarks
  georeference_attributes[:georeferenceRemarks]
end

#dwc_georeference_sourcesObject



225
226
227
# File 'app/models/collection_object/dwc_extensions.rb', line 225

def dwc_georeference_sources
  georeference_attributes[:georeferenceSources]
end

#dwc_georeferenced_byObject



237
238
239
# File 'app/models/collection_object/dwc_extensions.rb', line 237

def dwc_georeferenced_by
  georeference_attributes[:georeferencedBy]
end

#dwc_georeferenced_dateObject

georeferenceDate technically could look at papertrail to see when geographic_area_id appeared



255
256
257
# File 'app/models/collection_object/dwc_extensions.rb', line 255

def dwc_georeferenced_date
  georeference_attributes[:georeferencedDate]
end

#dwc_higher_classificationObject



482
483
484
485
486
487
488
# File 'app/models/collection_object/dwc_extensions.rb', line 482

def dwc_higher_classification
  v = taxonomy.values.collect{|a| a.kind_of?(Array) ? a.second : a}
  v.shift
  v.pop
  v.compact
  v.join(CollectionObject::DWC_DELIMITER).presence
end

#dwc_identification_remarksObject



347
348
349
# File 'app/models/collection_object/dwc_extensions.rb', line 347

def dwc_identification_remarks
  target_taxon_determination&.notes&.collect{ |n| n.text }&.join(CollectionObject::DWC_DELIMITER).presence
end

#dwc_identified_byObject



281
282
283
284
# File 'app/models/collection_object/dwc_extensions.rb', line 281

def dwc_identified_by
  # TaxonWorks allows for groups of determiners to collaborate on a single determination if they collectively came to a conclusion.
  target_taxon_determination&.determiners&.map(&:name)&.join(CollectionObject::DWC_DELIMITER).presence
end

#dwc_identified_by_idObject



306
307
308
309
310
311
312
313
314
315
316
317
318
# File 'app/models/collection_object/dwc_extensions.rb', line 306

def dwc_identified_by_id
  # TaxonWorks allows for groups of determiners to collaborate on a single determination if they collectively came to a conclusion.
  if target_taxon_determination
    target_taxon_determination&.determiners
      .joins(:identifiers)
      .where(identifiers: {type: ['Identifier::Global::Orcid', 'Identifier::Global::Wikidata']})
      .select('identifiers.identifier_object_id, identifiers.cached')
      .unscope(:order)
      .distinct
      .pluck('identifiers.cached')
      .join(CollectionObject::DWC_DELIMITER)&.presence
  end
end

#dwc_individual_countObject

TODO: handle ranged lots



577
578
579
# File 'app/models/collection_object/dwc_extensions.rb', line 577

def dwc_individual_count
  total
end

#dwc_infraspecific_epithetObject



464
465
466
467
468
469
# File 'app/models/collection_object/dwc_extensions.rb', line 464

def dwc_infraspecific_epithet
  %w{variety form subspecies}.each do |n| # add more as observed
    return taxonomy[n].last if taxonomy[n]
  end
  nil
end

#dwc_institution_codeObject

we assert custody, NOT ownership



566
567
568
# File 'app/models/collection_object/dwc_extensions.rb', line 566

def dwc_institution_code
  repository_acronym
end

#dwc_institution_idObject

we assert custody, NOT ownership



571
572
573
574
# File 'app/models/collection_object/dwc_extensions.rb', line 571

def dwc_institution_id
  # TODO: identifiers on Repositories
  repository_url || repository_institutional_LSID
end

#dwc_internal_attribute_for(target = :collection_object, dwc_term_name) ⇒ Object



357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
# File 'app/models/collection_object/dwc_extensions.rb', line 357

def dwc_internal_attribute_for(target = :collection_object, dwc_term_name)
  return nil if dwc_term_name.nil?

  case target
  when  :collecting_event
    return nil unless collecting_event
    collecting_event.internal_attributes.includes(:predicate)
      .where(
        controlled_vocabulary_terms: {uri: ::DWC_ATTRIBUTE_URIS[dwc_term_name.to_sym] })
      .pluck(:value)&.join(', ').presence
  when :collection_object
    internal_attributes.includes(:predicate)
      .where(
        controlled_vocabulary_terms: {uri: ::DWC_ATTRIBUTE_URIS[dwc_term_name.to_sym] })
      .pluck(:value)&.join(', ').presence
  else
    nil
  end
end

#dwc_kingdomObject



490
491
492
# File 'app/models/collection_object/dwc_extensions.rb', line 490

def dwc_kingdom
  taxonomy['kingdom']
end

#dwc_life_stageObject

TODO: consider CVT attributes with Predicates linked to URIs



417
418
419
420
# File 'app/models/collection_object/dwc_extensions.rb', line 417

def dwc_life_stage
  biocuration_classes.tagged_with_uri(::DWC_ATTRIBUTE_URIS[:lifeStage])
    .pluck(:name)&.join(', ').presence # `.presence` is a Rails extension
end

#dwc_localityObject



596
597
598
# File 'app/models/collection_object/dwc_extensions.rb', line 596

def dwc_locality
  collecting_event.try(:verbatim_locality)
end

#dwc_maximum_depth_in_metersObject



385
386
387
# File 'app/models/collection_object/dwc_extensions.rb', line 385

def dwc_maximum_depth_in_meters
  dwc_internal_attribute_for(:collecting_event, :maximumDepthInMeters)
end

#dwc_maximum_elevation_in_metersObject



446
447
448
# File 'app/models/collection_object/dwc_extensions.rb', line 446

def dwc_maximum_elevation_in_meters
  collecting_event&.maximum_elevation
end

#dwc_minimum_depth_in_metersObject



381
382
383
# File 'app/models/collection_object/dwc_extensions.rb', line 381

def dwc_minimum_depth_in_meters
  dwc_internal_attribute_for(:collecting_event, :minimumDepthInMeters)
end

#dwc_minimum_elevation_in_metersObject



450
451
452
# File 'app/models/collection_object/dwc_extensions.rb', line 450

def dwc_minimum_elevation_in_meters
  collecting_event&.minimum_elevation
end

#dwc_monthObject



652
653
654
655
656
# File 'app/models/collection_object/dwc_extensions.rb', line 652

def dwc_month
  return unless collecting_event
  return if collecting_event.start_date_month.present? && collecting_event.end_date_month.present?
  collecting_event.start_date_month.presence
end

#dwc_nomenclatural_codeObject



612
613
614
# File 'app/models/collection_object/dwc_extensions.rb', line 612

def dwc_nomenclatural_code
  current_otu.try(:taxon_name).try(:nomenclatural_code)
end

#dwc_occurrence_remarksObject



195
196
197
# File 'app/models/collection_object/dwc_extensions.rb', line 195

def dwc_occurrence_remarks
  notes.collect{|n| n.text}&.join(CollectionObject::DWC_DELIMITER).presence
end

#dwc_occurrence_statusObject



190
191
192
# File 'app/models/collection_object/dwc_extensions.rb', line 190

def dwc_occurrence_status
  'present'
end

#dwc_orderObject



502
503
504
# File 'app/models/collection_object/dwc_extensions.rb', line 502

def dwc_order
  taxonomy['order']
end

#dwc_other_catalog_numbersObject



300
301
302
303
304
# File 'app/models/collection_object/dwc_extensions.rb', line 300

def dwc_other_catalog_numbers
  i = identifiers.where.not('type ilike ?', 'Identifier::Global::Uuid%').order(:position).to_a
  i.shift
  i.map(&:cached).join(CollectionObject::DWC_DELIMITER).presence
end

#dwc_phylumObject



494
495
496
# File 'app/models/collection_object/dwc_extensions.rb', line 494

def dwc_phylum
  taxonomy['phylum']
end

#dwc_preparationsObject



673
674
675
# File 'app/models/collection_object/dwc_extensions.rb', line 673

def dwc_preparations
  preparation_type_name
end

#dwc_previous_identificationsObject



351
352
353
354
355
# File 'app/models/collection_object/dwc_extensions.rb', line 351

def dwc_previous_identifications
  a = taxon_determinations.order(:position).to_a
  a.shift
  a.collect{|d| ApplicationController.helpers.label_for_taxon_determination(d)}.join(CollectionObject::DWC_DELIMITER).presence
end

#dwc_record_numberObject



292
293
294
# File 'app/models/collection_object/dwc_extensions.rb', line 292

def dwc_record_number
  record_number_cached # via delegation
end

#dwc_recorded_byObject

Definition: A list (concatenated and separated) of names of people, groups, or organizations responsible for recording the original Occurrence. The primary collector or observer, especially one who applies a personal identifier (recordNumber), should be listed first.

This was interpreted as collectors (in the field in this context), not those who recorded other aspects of the data.



552
553
554
555
556
557
558
559
560
561
562
563
# File 'app/models/collection_object/dwc_extensions.rb', line 552

def dwc_recorded_by
  v = nil
  if collecting_event
    v = collecting_event.collectors
      .order('roles.position')
      .map(&:name)
      .join(CollectionObject::DWC_DELIMITER)
      .presence
    v = collecting_event.verbatim_collectors.presence if v.blank?
  end
  v
end

#dwc_recorded_by_idObject

See dwc_recorded_by TODO: Expand to any GlobalIdentifier



333
334
335
336
337
338
339
340
341
342
343
344
# File 'app/models/collection_object/dwc_extensions.rb', line 333

def dwc_recorded_by_id
  if collecting_event
    collecting_event.collectors
      .joins(:identifiers)
      .where(identifiers: {type: ['Identifier::Global::Orcid', 'Identifier::Global::Wikidata']})
      .select('identifiers.identifier_object_id, identifiers.cached')
      .unscope(:order)
      .distinct
      .pluck('identifiers.cached')
      .join(CollectionObject::DWC_DELIMITER)&.presence
  end
end

#dwc_sampling_protocolObject

TODO: Reconcile with Protocol (capital P) assignments



455
456
457
# File 'app/models/collection_object/dwc_extensions.rb', line 455

def dwc_sampling_protocol
  collecting_event&.verbatim_method
end

#dwc_scientific_nameObject



541
542
543
# File 'app/models/collection_object/dwc_extensions.rb', line 541

def dwc_scientific_name
  target_taxon_name&.cached_name_and_author_year
end

#dwc_sexObject

TODO: consider CVT attributes with Predicates linked to URIs



423
424
425
426
# File 'app/models/collection_object/dwc_extensions.rb', line 423

def dwc_sex
  biocuration_classes.tagged_with_uri(::DWC_ATTRIBUTE_URIS[:sex])
    .pluck(:name)&.join(', ').presence # TODO: Use delimeter!
end

#dwc_specific_epithetObject



537
538
539
# File 'app/models/collection_object/dwc_extensions.rb', line 537

def dwc_specific_epithet
  taxonomy['species'] && taxonomy['species'].compact.join(' ').presence
end

#dwc_start_day_of_yearObject



663
664
665
666
# File 'app/models/collection_object/dwc_extensions.rb', line 663

def dwc_start_day_of_year
  return unless collecting_event
  collecting_event.start_day_of_year.presence
end

#dwc_state_provinceObject



586
587
588
589
# File 'app/models/collection_object/dwc_extensions.rb', line 586

def dwc_state_province
  v = try(:collecting_event).try(:geographic_names)
  v[:state] if v
end

#dwc_subfamilyObject



517
518
519
# File 'app/models/collection_object/dwc_extensions.rb', line 517

def dwc_subfamily
  taxonomy['subfamily']
end

#dwc_subtribeObject



527
528
529
# File 'app/models/collection_object/dwc_extensions.rb', line 527

def dwc_subtribe
  taxonomy['subtribe']
end

#dwc_superfamilyObject



507
508
509
# File 'app/models/collection_object/dwc_extensions.rb', line 507

def dwc_superfamily
  taxonomy['superfamily']
end

#dwc_taxon_name_authorshipObject



545
546
547
# File 'app/models/collection_object/dwc_extensions.rb', line 545

def dwc_taxon_name_authorship
  target_taxon_name&.cached_author_year
end

#dwc_taxon_rankObject



471
472
473
# File 'app/models/collection_object/dwc_extensions.rb', line 471

def dwc_taxon_rank
  target_taxon_name&.rank
end

#dwc_tribeObject



522
523
524
# File 'app/models/collection_object/dwc_extensions.rb', line 522

def dwc_tribe
  taxonomy['tribe']
end

#dwc_type_statusObject

holotype of Ctenomys sociabilis. Pearson O. P., and M. I. Christie. 1985. Historia Natural, 5(37):388, holotype of Pinus abies | holotype of Picea abies



476
477
478
479
480
# File 'app/models/collection_object/dwc_extensions.rb', line 476

def dwc_type_status
  type_materials.all.collect{|t|
    ApplicationController.helpers.label_for_type_material(t)
  }.join(CollectionObject::DWC_DELIMITER).presence
end

#dwc_verbatim_coordinatesObject



437
438
439
440
# File 'app/models/collection_object/dwc_extensions.rb', line 437

def dwc_verbatim_coordinates
  return nil unless collecting_event
  [collecting_event.verbatim_latitude, collecting_event.verbatim_longitude].compact.join(' ').presence
end

#dwc_verbatim_depthObject



389
390
391
# File 'app/models/collection_object/dwc_extensions.rb', line 389

def dwc_verbatim_depth
  dwc_internal_attribute_for(:collecting_event, :verbatimDepth)
end

#dwc_verbatim_elevationObject



442
443
444
# File 'app/models/collection_object/dwc_extensions.rb', line 442

def dwc_verbatim_elevation
  collecting_event&.verbatim_elevation
end

#dwc_verbatim_event_dateObject



629
630
631
# File 'app/models/collection_object/dwc_extensions.rb', line 629

def dwc_verbatim_event_date
  collecting_event&.verbatim_date
end

#dwc_verbatim_habitatObject



460
461
462
# File 'app/models/collection_object/dwc_extensions.rb', line 460

def dwc_verbatim_habitat
  collecting_event&.verbatim_habitat
end

#dwc_verbatim_labelObject

use buffered if any if not check CE verbatim_label



184
185
186
187
188
# File 'app/models/collection_object/dwc_extensions.rb', line 184

def dwc_verbatim_label
  b = [buffered_collecting_event, buffered_determinations, buffered_other_labels].compact
  return  b.join("\n\n") if b.present?
  collecting_event&.verbatim_label.presence
end

#dwc_verbatim_latitudeObject



268
269
270
# File 'app/models/collection_object/dwc_extensions.rb', line 268

def dwc_verbatim_latitude
  collecting_event&.verbatim_latitude
end

#dwc_verbatim_localityObject



608
609
610
# File 'app/models/collection_object/dwc_extensions.rb', line 608

def dwc_verbatim_locality
  collecting_event.try(:verbatim_locality)
end

#dwc_verbatim_longitudeObject



272
273
274
# File 'app/models/collection_object/dwc_extensions.rb', line 272

def dwc_verbatim_longitude
  collecting_event&.verbatim_longitude
end

#dwc_verbatim_srsObject



249
250
251
# File 'app/models/collection_object/dwc_extensions.rb', line 249

def dwc_verbatim_srs
  georeference_attributes[:dwcVerbatimSrs]
end

#dwc_water_bodyObject



377
378
379
# File 'app/models/collection_object/dwc_extensions.rb', line 377

def dwc_water_body
  dwc_internal_attribute_for(:collecting_event, :waterBody)
end

#dwc_yearObject



647
648
649
650
# File 'app/models/collection_object/dwc_extensions.rb', line 647

def dwc_year
  return unless collecting_event
  collecting_event.start_date_year.presence
end

#is_fossil?Boolean

Returns:

  • (Boolean)


433
434
435
# File 'app/models/collection_object/dwc_extensions.rb', line 433

def is_fossil?
  biocuration_classes.where(uri: DWC_FOSSIL_URI).any?
end

#set_georeference_attributesHash

Returns:

  • (Hash)


149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'app/models/collection_object/dwc_extensions.rb', line 149

def set_georeference_attributes
  case collecting_event&.dwc_georeference_source
  when :georeference
    collecting_event.preferred_georeference.dwc_georeference_attributes
  when :verbatim
    h = collecting_event.dwc_georeference_attributes

    # Our interpretation is now that georeferencedBy is the person who "computed" the
    # values, not transcribed the values.
    #
    #     if a = collecting_event&.attribute_updater(:verbatim_latitude)
    #       h[:georeferencedBy] = User.find(a).name
    #     end

    # verbatim_longitude could technically be different, but...
    h[:georeferencedDate] = collecting_event&.attribute_updated(:verbatim_latitude)

    h

  when :geographic_area
    h = collecting_event.geographic_area.dwc_georeference_attributes
    if a = collecting_event&.attribute_updater(:geographic_area_id)
      h[:georeferencedBy] = User.find(a).name
    end

    h[:georeferencedDate] = collecting_event&.attribute_updated(:geographic_area_id)

    h
  else
    {}
  end
end