mirror of
https://github.com/Growstuff/growstuff.git
synced 2026-05-30 03:36:23 -04:00
* Add GBIF cient * Add lookup * Add autocomplete for GBIF lookup * Add extra detail to scientific names * Autocomplete * Add routes * Rmeove mapping * Add autocomplete * Update GBIF data on save * db/schema * Style * Extract service * Add concern * Add concern * Save photos * Initial coverage * Coverage * Add coverage * Shut up, codeclimate * Shut up, codeclimate * Unused * Shut up, codeclimate * Apply suggestions from code review * Remove localhost * Fix rubocop * Fix rubocop * Add UI links * Add rake * Indent * Update Gemfile.lock * Update lib/tasks/gbif.rake * Update app/views/crops/_scientific_names.html.haml * Rubocop * Expand edit photo form * Fix error * Add model validations * Skip photos without backlinks * Fix tests * Add photo words * Allow blank * Rubocop and handle invalid legacy data * Apply suggestions from code review * Update lib/tasks/gbif.rake
194 lines
6.2 KiB
Ruby
194 lines
6.2 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'English'
|
|
class GbifService
|
|
def initialize
|
|
@cropbot = Member.find_by(login_name: 'cropbot')
|
|
@species = Gbif::Species
|
|
end
|
|
|
|
def suggest(term)
|
|
# Query the GBIF name autocomplete and discover the scientific name.
|
|
# [
|
|
# {
|
|
# "key": 2932942,
|
|
# "nameKey": 1970347,
|
|
# "kingdom": "Plantae",
|
|
# "phylum": "Tracheophyta",
|
|
# "order": "Solanales",
|
|
# "family": "Solanaceae",
|
|
# "genus": "Capsicum",
|
|
# "species": "Capsicum chinense",
|
|
# "kingdomKey": 6,
|
|
# "phylumKey": 7707728,
|
|
# "classKey": 220,
|
|
# "orderKey": 1176,
|
|
# "familyKey": 7717,
|
|
# "genusKey": 2932937,
|
|
# "speciesKey": 2932942,
|
|
# "parent": "Capsicum",
|
|
# "parentKey": 2932937,
|
|
# "nubKey": 2932942,
|
|
# "scientificName": "Capsicum chinense Jacq.",
|
|
# "canonicalName": "Capsicum chinense",
|
|
# "rank": "SPECIES",
|
|
# "status": "ACCEPTED",
|
|
# "synonym": false,
|
|
# "higherClassificationMap": {
|
|
# "6": "Plantae",
|
|
# "220": "Magnoliopsida",
|
|
# "1176": "Solanales",
|
|
# "7717": "Solanaceae",
|
|
# "2932937": "Capsicum",
|
|
# "7707728": "Tracheophyta"
|
|
# },
|
|
# "class": "Magnoliopsida"
|
|
# },
|
|
# {
|
|
# "key": 12079498,
|
|
# "nameKey": 81778754,
|
|
# "kingdom": "Plantae",
|
|
# "phylum": "Tracheophyta",
|
|
# "order": "Solanales",
|
|
# "family": "Solanaceae",
|
|
# "genus": "Capsicum",
|
|
# "species": "Capsicum chinense",
|
|
# "kingdomKey": 6,
|
|
# "phylumKey": 7707728,
|
|
# "classKey": 220,
|
|
# "orderKey": 1176,
|
|
# "familyKey": 7717,
|
|
# "genusKey": 2932937,
|
|
# "speciesKey": 2932942,
|
|
# "parent": "Capsicum",
|
|
# "parentKey": 2932937,
|
|
# "nubKey": 12079498,
|
|
# "scientificName": "Capsicum annuum var. chinense (Jacq.) Alef.",
|
|
# "canonicalName": "Capsicum annuum chinense",
|
|
# "rank": "VARIETY",
|
|
# "status": "SYNONYM",
|
|
# "synonym": true,
|
|
# "higherClassificationMap": {
|
|
# "6": "Plantae",
|
|
# "220": "Magnoliopsida",
|
|
# "1176": "Solanales",
|
|
# "7717": "Solanaceae",
|
|
# "2932937": "Capsicum",
|
|
# "2932942": "Capsicum chinense",
|
|
# "7707728": "Tracheophyta"
|
|
# },
|
|
# "class": "Magnoliopsida"
|
|
# }
|
|
# ]
|
|
|
|
@species.name_suggest(q: term)
|
|
end
|
|
|
|
def import!
|
|
Crop.order(updated_at: :desc).each do |crop|
|
|
Rails.logger.debug { "#{crop.id}, #{crop.name}" }
|
|
update_crop(crop) if crop.valid?
|
|
rescue ActiveRecord::RecordInvalid
|
|
Rails.logger.error($ERROR_INFO.message)
|
|
end
|
|
end
|
|
|
|
def update_crop(crop)
|
|
# Attempt to resolve the scientific names via /species/match.
|
|
gbif_usage_key = crop.scientific_names.detect { |sn| sn.gbif_key.present? }&.gbif_key
|
|
unless gbif_usage_key
|
|
crop.scientific_names.each do |sn|
|
|
result = @species.name_backbone(name: sn.name) # , higherTaxonKey: 6, nameType: 'SCIENTIFIC')
|
|
next unless result["confidence"] > 95 && result["matchType"] == "EXACT"
|
|
|
|
sn.gbif_key = result["usageKey"]
|
|
sn.gbif_rank = result["rank"]
|
|
sn.gbif_status = result["status"]
|
|
sn.save!
|
|
end
|
|
|
|
gbif_usage_key = crop.scientific_names.detect { |sn| sn.gbif_key.present? }&.gbif_key
|
|
end
|
|
|
|
# No match? Fall back to common names
|
|
unless gbif_usage_key
|
|
query_results = @species.name_lookup(q: crop.name, higherTaxonKey: 6)
|
|
|
|
# We only want one result, otherwise it needs human.
|
|
return unless query_results["results"].length == 1
|
|
|
|
query_result = query_results["results"].first
|
|
|
|
gbif_usage_key = query_result["key"]
|
|
|
|
crop.scientific_names.create!(gbif_key: gbif_usage_key, name: query_result["canonicalName"], creator: @cropbot)
|
|
end
|
|
|
|
gbif_record = fetch(gbif_usage_key)
|
|
if gbif_record.present?
|
|
# crop.update! openfarm_data: gbif_record.fetch('data', false)
|
|
# save_companions(crop, gbif_record)
|
|
save_photos(crop, gbif_usage_key)
|
|
else
|
|
Rails.logger.debug "\tcrop not found on GBIF"
|
|
# crop.update!(openfarm_data: false)
|
|
end
|
|
end
|
|
|
|
def save_photos(crop, key)
|
|
# https://api.gbif.org/v1/occurrence/search?taxon_key=3084850
|
|
|
|
occurrences = Gbif::Occurrences.search(taxonKey: key, mediatype: 'StillImage', limit: 3, hasCoordinate: true)
|
|
occurrences["results"].each do |result|
|
|
next unless result["media"]
|
|
|
|
media = result["media"].first
|
|
next unless media["identifier"]
|
|
|
|
# Example: "https://inaturalist-open-data.s3.amazonaws.com/photos/250226497/original.jpg"
|
|
url = media["identifier"]
|
|
md5 = Digest::MD5.hexdigest(url)
|
|
width = 200
|
|
thumbnail = "https://api.gbif.org/v1/image/cache/#{width}x/occurrence/#{result['key']}/media/#{md5}"
|
|
|
|
next unless url.start_with? 'http'
|
|
next if Photo.find_by(source_id: result["key"], source: 'gbif')
|
|
next if media["references"].blank?
|
|
|
|
photo = Photo.new(
|
|
# This is for the overall observation which may technically have multiple media. However, we're only taking the first.
|
|
source_id: result["key"],
|
|
source: 'gbif',
|
|
owner: @cropbot,
|
|
thumbnail_url: thumbnail,
|
|
fullsize_url: url,
|
|
title: "Photo by #{media['creator']} via #{media['publisher']} (Copyright #{media['rightsHolder']})",
|
|
license_name: case media["license"]
|
|
when "http://creativecommons.org/licenses/by/4.0/"
|
|
"CC BY 4.0"
|
|
when "http://creativecommons.org/licenses/by-nc/4.0/"
|
|
"CC BY-NC 4.0"
|
|
else
|
|
media["license"]
|
|
end,
|
|
license_url: media["license"],
|
|
link_url: media["references"]
|
|
)
|
|
photo.date_taken = DateTime.parse(media["created"]) if media["created"]
|
|
if photo.valid?
|
|
Photo.transaction do
|
|
photo.save
|
|
PhotoAssociation.find_or_create_by! photo:, photographable: crop
|
|
end
|
|
Rails.logger.debug { "\t saved photo #{photo.id} #{photo.source_id}" }
|
|
else
|
|
Rails.logger.warn "Photo not valid"
|
|
end
|
|
end
|
|
end
|
|
|
|
def fetch(key)
|
|
Gbif::Request.new("species/#{key}", nil, nil, nil).perform
|
|
end
|
|
end
|