20. oauth2 package for Python, body=str(paramsXML)

I don't know about using the oauth2 package for Python,
body=str(paramsXML) seems a bit suspicious, and I wouldn't be
surprised if the oauth2 package provides a more abstracted interface
to retrieving an access token then specifying all those POST params
like you're doing.

I don't know if it will help, but here's a Python version of one of
our Ruby examples from the docs:
https://gist.github.com/kueda/53ef93159c64de96ddc2. The Resource Owner
Password Credentials flow might be more appropriate for scripts like
the one you're writing. The Authorization Code Flow is more for
contexts in which your application interacts with iNat on behalf of
the user, and it needs to get permission to do so without knowing
confidential info like a password.

de link is niet naar de waarneming maar naar de waarnemingen van de gebruiker van een bepaalde soort.
nu is de link bijvoorbeeld
https://waarneming.nl/user/view/295/?sp=140
dan kom je op de oude site bij je waarnemingen.
Wil je nu op de site naar je waarnemingen van de deze soort dan is dit de link
https://waarneming.nl/users/295/observations/?species=140
dat is de nieuwe site.

It's still in an early stage of development, but pyinaturalist (https://pyinaturalist.readthedocs.io/en/latest/?badge=latest) can be used to easily upload observations from Python to iNaturalist. See "For authenticated API calls, you first need to obtain a token for the user:" and "Create a new observation" from the link above.

Don't hesitate to ask if you encounter any issue or need a better example (I'm the develope

Publicado el 21 de enero de 2019 por ahospers ahospers

Comentarios

I've been working with a PhD. student, collecting plants to inventory a complete flora of a natural area. We have a spreadsheet with herbarium-appropriate details of the 1003 specimens from last year. I took photos with my DSLR, and have an average of over 5 field photos per specimen. Almost all specimens have been keyed, so we're ready to upload to iNaturalist. From what I can tell, the most automated workflow would be:

Use a script and exiftool to get metadata into the photos -- GPS coordinates in the appropriate fields, taxon name and everything else as IPTC "subject" keywords
Crank through the web uploader, uploading batches, manually clicking on images to group them and specify them as single observations

I'm pretty sure I can figure out the first step, but the second step seems overly tedious and prone to error, so I'd much rather have it fully automated. I've found similar requests, but no workflow that would avoid the manual assignment of photos to observations. Is this possible?

Alternatively, I can think of another workflow that could work well:

upload the observations as a csv
export the observations from iNaturalist as a csv (to get the iNaturalist observation numbers)
tag (or rename) the images with the iNaturalist observation number
upload all of the images

Is there any way to automatically group photos as observations when uploading?

Joe

p.s. I have seen pyinaturalist and I think that I could potentially use that (and would use it instead of steps 3 and 4), but I was hoping for a solution not requiring another 3rd-party tool besides an exif tag editor. This pretty much seems the use case for pyinaturalist. While I have yet to try it, doing all the steps as a python script seems credible. How do you have stored which photos are of the same plant? If it is data is consistent it should be possible to get a script to pull the data from the spreadsheet and exif then submit the observations with that data.

Publicado por ahospers hace más de 5 años

Long before the CV was implemented, I discovered that you only really need to enter the first three or four letters of each part of the name. For example, the Kereru has the scientific name Hemiphaga novaeseelandiae, so I only need to enter “hem nov” and it is shortlisted to something like 5 taxa, of which the one I want is the top one. Of course, for this taxa I could just type “kereru” and it would come up as the only shortlisted taxa, but that is just from having an unusual common name!

The joy of this method, is you are using monicors that help you remember the scientific names, and also it works every time, not just when the CV can recognise it.

https://www.inaturalist.org/observations/19811789
The AI badge is quite useful. It doesn’t have to be perfect to be of use, so as long as it is not putting a processing load on the system, I say keep it! https://www.inaturalist.org/observations/19811789

Publicado por ahospers hace más de 5 años

I asked essentially the same question https://groups.google.com/d/topic/inaturalist/fgxNGDP3HBE/discussion
There might be useful information for you in that thread. I didn't see your post because I kinda took my time finishing mine up, and you posted after I'd looked for similar topics.

The alternative to the workflow already outlined there is to use exiftool http://owl.phy.queensu.ca/~phil/exiftool/ to add your data as IPTC keywords. The complexity of photo tagging is pretty daunting, and the most detail I've found specific to iNaturalist is in this thread: https://groups.google.com/forum/#!searchin/inaturalist/exiftool|sort:date/inaturalist/2Hg7FzhQ-TI/p3YDEr47AwAJ

You would use your data to create thousands of these commands, each one pointing to one image filename, and then you could save all that text in a script (batch file on Windows, bash script on Mac or Linux). You would then use the iNaturalist upload page to upload many photos at a time (I've seen forum threads that say that once they added 150 at once, it got very slow), and you would have to manually group photos into observations, for any observations with multiple photos.

Publicado por ahospers hace más de 5 años

But you're welcome to write your own script that posts through our API
https://www.inaturalist.org/pages/developers

Here's a snippet of posting users and obs over the API written in Ruby

require 'rubygems'
require 'rest_client'
require 'json'

First, enter your app_id, app_secret, and redirect_uri from

http://gorilla.inaturalist.org/oauth/applications/206

site = "http://gorilla.inaturalist.org"
app_id = '308714d38eaf78ed57c11c0790f639d7d05e86cb7564f641629116e5b3bea024'
app_secret = '8be0ee61e1b7858a14c050b982eb3e6447b2d674075f3d8c58c8759ed2ee02a6'
redirect_uri = 'http://www.bd.dix/utils/migratelanding.cfm'

Next, visit this link on your browser while logged in as 'tegenligger'

http://gorilla.inaturalist.org/oauth/authorize?client_id=308714d38eaf78ed57c11c0790f639d7d05e86cb7564f641629116e5b3bea024&redirect_uri=http%3A%2F%2Fwww.bd.dix%2Futils%2Fmigratelanding.cfm&response_type=code

and get your auth code

auth_code = "d9c5335b17c0ec05f7444b1673c675b16a9a4d77f2d499b852778888503760a3"

Next, get a token for tegenligger

payload = {
:client_id => app_id,
:client_secret => app_secret,
:code => auth_code,
:redirect_uri => redirect_uri,
:grant_type => "authorization_code"
}
response = RestClient.post("#{site}/oauth/token", payload)
token = JSON.parse(response)["access_token"]
headers = {"Authorization" => "Bearer #{token}"}

Now make a user using tegenligger's token

username = 'testuser1'
email = 'testuser1@bar.net'
password = 'testuser1password'

results = RestClient.post("#{site}/users.json", {"user[login]" =>
username, "user[email]" => email, "user[password]" => password,
"user[password_confirmation]" => password}, headers)
puts "created http://gorilla.inaturalist.org/users/#{JSON.parse(results)["id"]}"

Now get a token for testuser1

payload = {
:client_id => app_id,
:client_secret => app_secret,
:grant_type => "password",
:username => username,
:password => password
}
puts "POST #{site}/oauth/token, payload: #{payload.inspect}"
response_for_user1 = RestClient.post("#{site}/oauth/token", payload)
token_for_user1 = JSON.parse(response)["access_token"]
headers_for_user1 = {"Authorization" => "Bearer #{token}"}

Now make a observation on behalf of testuser1

results = RestClient.post("#{site}/observations.json",{
"observation[species_guess]" => "Northern Cardinal",
"observation[taxon_id]" => 9083,
"observation[observed_on_string]" => "2013-01-03",
"observation[time_zone]" => "Eastern Time (US %26 Canada)",
"observation[description]" => "what a cardinal",
"observation[tag_list]" => "foo,bar",
"observation[place_guess]" => "clinton, ct",
"observation[latitude]" => 41.27872259999999,
"observation[longitude]" => -72.5276073,
"observation[map_scale]" => 11,
"observation[location_is_exact]" => false,
"observation[positional_accuracy]" => 7798,
"observation[geoprivacy]" => "obscured"
}, headers_for_user1)

puts "created http://gorilla.inaturalist.org/observations/#{JSON.parse(results)[0]["id"]}"

Now make a another user using tegenligger's token

username = 'testuser2'
email = 'testuser2@bar.net'
password = 'testuser2password'

results = RestClient.post("#{site}/users.json", {"user[login]" =>
username, "user[email]" => email, "user[password]" => password,
"user[password_confirmation]" => password}, headers)
puts "created http://gorilla.inaturalist.org/users/#{JSON.parse(results)["id"]}"

Now get a token for testuser2

payload = {
:client_id => app_id,
:client_secret => app_secret,
:grant_type => "password",
:username => username,
:password => password
}
puts "POST #{site}/oauth/token, payload: #{payload.inspect}"
response_for_user2 = RestClient.post("#{site}/oauth/token", payload)
token_for_user2 = JSON.parse(response)["access_token"]
headers_for_user2 = {"Authorization" => "Bearer #{token}"}

Now make a observation on behalf of testuser2

results = RestClient.post("#{site}/observations.json",{
"observation[species_guess]" => "Northern Cardinal",
"observation[taxon_id]" => 9083,
"observation[observed_on_string]" => "2013-01-03",
"observation[time_zone]" => "Eastern Time (US %26 Canada)",
"observation[description]" => "what a cardinal",
"observation[tag_list]" => "foo,bar",
"observation[place_guess]" => "clinton, ct",
"observation[latitude]" => 41.27872259999999,
"observation[longitude]" => -72.5276073,
"observation[map_scale]" => 11,
"observation[location_is_exact]" => false,
"observation[positional_accuracy]" => 7798,
"observation[geoprivacy]" => "obscured"
}, headers_for_user2)

puts "created http://gorilla.inaturalist.org/observations/#{JSON.parse(results)[0]["id"]}"

Publicado por ahospers hace más de 5 años

e I’m not querying the API directly is the rate limit any higher? Here is the script I’m currently using to download 1 photo/sec for 10k/day:

import os
import pickle
import requests
import shutil
import sys
import time

def download(url, name):
print(name)
with requests.get(url, stream=True) as r:
if r.status_code != 200:
print('{} error: {}'.format(r.status_code, url), file=sys.stderr)
return
r.raw.decode_content = True
with open('./ducks/{}.jpg'.format(name), 'wb') as f:
shutil.copyfileobj(r.raw, f)

def main():
os.makedirs('ducks', exist_ok=True)
present = set([x.replace('.jpg', '') for x in os.listdir('ducks')])

with open('duck_urls', 'rb') as f:
urls = pickle.load(f)

ctr = 0
for url in urls:
if ctr > 10000:
return
name = os.path.normpath(url).split(os.path.sep)[-2]
if name in present:
continue
download(url, name)
ctr += 1
time.sleep(1)

if name == 'main':
main()

Publicado por ahospers hace casi 4 años

Agregar un comentario

Acceder o Crear una cuenta para agregar comentarios.
Vida Silvestre es una entidad asociada a la Organización Mundial de Conservación