Class: InaturalistImportJob

Inherits:
ApplicationJob show all
Defined in:
app/jobs/inaturalist_import_job.rb

Instance Method Summary collapse

Instance Method Details

#import_observation(result, project_id:, match_otu_by_name:, use_community_taxon:, import_images:, import_sounds:) ⇒ Object (private)



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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
80
81
82
83
84
85
86
87
88
89
90
# File 'app/jobs/inaturalist_import_job.rb', line 22

def import_observation(result, project_id:, match_otu_by_name:, use_community_taxon:, import_images:, import_sounds:)
  ApplicationRecord.transaction do
    # Save the OTU first so otu.id is available for the TaxonDetermination nested
    # attributes — reject_taxon_determinations rejects entries with a blank otu_id
    # and a blank otu.id, which is the case for any new (unsaved) OTU object.
    otu = ::Vendor::Nasturtium.stub_otu(result, project_id:, match_by_name: match_otu_by_name, use_community_taxon:)
    unless otu
      Rails.logger.warn("InaturalistImportJob: skipping observation #{result['id']} — no taxon name")
      return
    end
    otu.save! if otu.new_record?

    # Save the CE (and its nested georeference) before the FO so that
    # collecting_event_id is set when the FO is created.
    ce = ::Vendor::Nasturtium.stub_collecting_event(result)
    ce.save!

    if (georef = ce.georeferences.first)
      georeferencer = ::Vendor::Nasturtium.stub_observer_person(result)
      georeferencer.save! if georeferencer.new_record?
      georef.georeferencer_roles.create!(person: georeferencer)
    end

    d = result['observed_on_details']
    fo = FieldOccurrence.new(
      total: 1,
      collecting_event: ce,
      taxon_determinations_attributes: [{
        otu_id: otu.id,
        year_made: d['year'],
        month_made: d['month'],
        day_made: d['day'],
      }],
      identifiers: [::Vendor::Nasturtium.stub_identifier(result)].compact,
    )
    fo.save!

    ::Vendor::Nasturtium.stub_biocuration_classes(result, project_id:).each do |biocuration_class|
      BiocurationClassification.create!(biocuration_class:, biocuration_classification_object: fo)
    end

    unless use_community_taxon
      determiner = ::Vendor::Nasturtium.stub_observer_person(result)
      determiner.save! if determiner.new_record?
      td = fo.taxon_determinations.first
      td.determiner_roles.create!(person: determiner)

      if (ident_uuid = ::Vendor::Nasturtium.observer_identification_uuid(result))
        Identifier::Global::Uuid::InaturalistIdentification.create!(
          identifier_object: td,
          identifier: ident_uuid
        )
      end
    end

    if result['description'].present?
      Note.create!(note_object: fo, text: result['description'])
    end

    import_photos(result, fo:) if import_images
    import_sounds(result, fo:) if import_sounds
  end
rescue => e
  # Failures are logged but do not interrupt the remaining imports
  Rails.logger.error(
    "InaturalistImportJob: failed to import observation #{result['id']}: " \
    "#{e.class}: #{e.message}\n#{e.backtrace&.first(5)&.join("\n")}"
  )
end

#import_photos(result, fo:) ⇒ Object (private)



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'app/jobs/inaturalist_import_job.rb', line 111

def import_photos(result, fo:)
  observed_year = result.dig('observed_on_details', 'year')

  ::Vendor::Nasturtium.permitted_photos(result).each do |obs_photo|
    ApplicationRecord.transaction do
      image = ::Vendor::Nasturtium.build_image!(obs_photo, result:, observed_year:)
      Depiction.create!(image:, depiction_object: fo)
    end
  rescue => e
    Rails.logger.error(
      "InaturalistImportJob: failed to import photo #{obs_photo['uuid']} " \
      "for observation #{result['uuid']}: #{e.class}: #{e.message}\n" \
      "#{e.backtrace&.first(5)&.join("\n")}"
    )
  end
end

#import_sounds(result, fo:) ⇒ Object (private)

For each CC/PD-licensed sound on the observation, create a Sound with full licensing metadata and attach it as a Conveyance on the FO.



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'app/jobs/inaturalist_import_job.rb', line 94

def import_sounds(result, fo:)
  observed_year = result.dig('observed_on_details', 'year')

  ::Vendor::Nasturtium.permitted_sounds(result).each do |obs_sound|
    ApplicationRecord.transaction do
      sound = ::Vendor::Nasturtium.build_sound!(obs_sound, result:, observed_year:)
      Conveyance.create!(sound:, conveyance_object: fo)
    end
  rescue => e
    Rails.logger.error(
      "InaturalistImportJob: failed to import sound #{obs_sound['uuid']} " \
      "for observation #{result['uuid']}: #{e.class}: #{e.message}\n" \
      "#{e.backtrace&.first(5)&.join("\n")}"
    )
  end
end

#perform(results:, project_id:, user_id:, match_otu_by_name: false, use_community_taxon: true, import_images: false, import_sounds: false) ⇒ Object

Parameters:

  • results (Array<Hash>)

    pre-fetched Nasturtium observation results

  • project_id (Integer)
  • user_id (Integer)
  • match_otu_by_name (Boolean) (defaults to: false)
  • use_community_taxon (Boolean) (defaults to: true)
  • import_images (Boolean) (defaults to: false)
  • import_sounds (Boolean) (defaults to: false)


11
12
13
14
15
16
17
18
# File 'app/jobs/inaturalist_import_job.rb', line 11

def perform(results:, project_id:, user_id:, match_otu_by_name: false, use_community_taxon: true, import_images: false, import_sounds: false)
  Current.project_id = project_id
  Current.user_id = user_id

  results.each do |result|
    import_observation(result, project_id:, match_otu_by_name:, use_community_taxon:, import_images:, import_sounds:)
  end
end