Ejemplo n.º 1
0
def test_get_non_existent_observation(requests_mock):
    requests_mock.get(
        urljoin(INAT_NODE_API_BASE_URL, "observations"),
        json=load_sample_data("get_nonexistent_observation.json"),
        status_code=200,
    )
    with pytest.raises(ObservationNotFound):
        get_observation(observation_id=99999999)
Ejemplo n.º 2
0
 def check_missing_obs(self, observation):
     """
     Get the observation data from iNaturalist and update the observation.
     If that yields an ObservationNotFound error, the observation was deleted at iNaturalist and should be
     deleted at vespawatch too. Otherwise, check which field (project id or taxon) changed that caused
     the observation to no longer match the filter criteria. Flag the observation with the appropriate warning.
     """
     try:
         self.w(
             f"DEBUG: will perform a get_observation() for obs #{observation.pk} (iNaturalist ID: {observation.inaturalist_id})"
         )
         inat_obs_data = get_observation(observation.inaturalist_id)
         # Let's slow down things a bit to avoid API errors
         time.sleep(1)
         self.w(
             f"\n... obs {observation.pk} still exists at iNat, it's just not part of the project anymore. Flag and update it."
         )
         observation.flag_based_on_inat_data(inat_obs_data)
         observation.update_from_inat_data(inat_obs_data)
     except TaxonMatchError:
         self.w(
             f"DEBUG: we got a TaxonMatchError on observation {observation.pk}.. Why?"
         )
     except ObservationNotFound:
         self.w(f"\n... obs {observation.pk} was not found. Deleting it.")
         observation.delete()
     except JSONDecodeError:
         self.w(
             f"DEBUG: iNaturalist API returned an error while getting obs {observation.pk}.. Why?"
         )
         # TODO: what should we do now? delete also locally? or is it a transient error?
     except ReadTimeout:
         self.w(
             f"DEBUG: got a time out from iNaturalist API while getting obs {observation.pk}.. Why?"
         )
Ejemplo n.º 3
0
def get_observation_photo_metadata(observation_id, access_token):
    """Attempt to scrape metadata from a photo info pages associated with an observation
    (first photo only)
    """
    print(f'Fetching observation {observation_id}')
    obs = get_observation(observation_id)
    photo_ids = [photo['id'] for photo in obs.get('photos', [])]
    photo_urls = [f'{PHOTO_INFO_BASE_URL}/{id}' for id in photo_ids]
    print(f'{len(photo_urls)} photo URL(s) found')
    return get_photo_metadata(photo_urls[0], access_token)
Ejemplo n.º 4
0
def test_get_observation(requests_mock):
    requests_mock.get(
        urljoin(INAT_NODE_API_BASE_URL, "observations"),
        json=load_sample_data("get_observation.json"),
        status_code=200,
    )

    obs_data = get_observation(observation_id=16227955)
    assert obs_data["quality_grade"] == "research"
    assert obs_data["id"] == 16227955
    assert obs_data["user"]["login"] == "niconoe"
    assert len(obs_data["photos"]) == 2
Ejemplo n.º 5
0
def inat_observation_comes_from_vespawatch(inat_observation_id):
    """ Takes an observation_id from iNat API and returns True if this observation was first created from the
    VespaWatch website.

    Slow, since we need an API call to retrieve the observation_field_values
    """
    obs_data = get_observation(observation_id=inat_observation_id)

    # We simply check if there's a vespawatch_id observation field on this observation
    for ofv in obs_data['ofvs']:
        if ofv['field_id'] == settings.VESPAWATCH_ID_OBS_FIELD_ID:
            return True

    return False
Ejemplo n.º 6
0
 def check_missing_obs(self, observation):
     """
     Get the observation data from iNaturalist and update the observation.
     If that yields an ObservationNotFound error, the observation was deleted at iNaturalist and should be
     deleted at vespawatch too. Otherwise, check which field (project id or taxon) changed that caused
     the observation to no longer match the filter criteria. Flag the observation with the appropriate warning.
     """
     try:
         self.w(
             f"DEBUG: will perform a get_observation() for obs #{observation.pk} (iNaturalist ID: {observation.inaturalist_id})"
         )
         inat_obs_data = get_observation(observation.inaturalist_id)
         observation.flag_based_on_inat_data(inat_obs_data)
         observation.update_from_inat_data(inat_obs_data)
     except ObservationNotFound:
         self.w(f"\n... obs {observation.pk} was not found. Deleting it.")
         observation.delete()
Ejemplo n.º 7
0
def create_test_obs(token):
    response = create_observation(
        taxon_id=54327,
        observed_on_string=datetime.now().isoformat(),
        description=
        "This is a test observation used by pyinaturalist, and will be deleted shortly.",
        tag_list="wasp, Belgium",
        latitude=50.647143,
        longitude=4.360216,
        positional_accuracy=50,
        geoprivacy="open",
        access_token=token,
        observation_fields={297: 1},
    )
    test_obs_id = response[0]["id"]
    print("Created new observation: {}".format(test_obs_id))

    obs = get_observation(test_obs_id)
    print("Fetched new observation:")
    pprint(obs, indent=2)
    return test_obs_id
Ejemplo n.º 8
0
def test_user_agent(requests_mock):
    # TODO: test for all functions that access the inaturalist API?
    requests_mock.get(
        urljoin(INAT_NODE_API_BASE_URL, "observations"),
        json=load_sample_data("get_observation.json"),
        status_code=200,
    )
    accepted_json = {
        "access_token": "604e5df329b98eecd22bb0a84f88b68a075a023ac437f2317b02f3a9ba414a08",
        "token_type": "Bearer",
        "scope": "write",
        "created_at": 1539352135,
    }
    requests_mock.post(
        "https://www.inaturalist.org/oauth/token", json=accepted_json, status_code=200,
    )

    default_ua = "Pyinaturalist/{v}".format(v=pyinaturalist.__version__)

    # By default, we have a 'Pyinaturalist' user agent:
    get_observation(observation_id=16227955)
    assert requests_mock._adapter.last_request._request.headers["User-Agent"] == default_ua
    get_access_token("valid_username", "valid_password", "valid_app_id", "valid_app_secret")
    assert requests_mock._adapter.last_request._request.headers["User-Agent"] == default_ua

    # But if the user sets a custom one, it is indeed used:
    get_observation(observation_id=16227955, user_agent="CustomUA")
    assert requests_mock._adapter.last_request._request.headers["User-Agent"] == "CustomUA"
    get_access_token(
        "valid_username",
        "valid_password",
        "valid_app_id",
        "valid_app_secret",
        user_agent="CustomUA",
    )
    assert requests_mock._adapter.last_request._request.headers["User-Agent"] == "CustomUA"

    # We can also set it globally:
    pyinaturalist.user_agent = "GlobalUA"
    get_observation(observation_id=16227955)
    assert requests_mock._adapter.last_request._request.headers["User-Agent"] == "GlobalUA"
    get_access_token("valid_username", "valid_password", "valid_app_id", "valid_app_secret")
    assert requests_mock._adapter.last_request._request.headers["User-Agent"] == "GlobalUA"

    # And it persists across requests:
    get_observation(observation_id=16227955)
    assert requests_mock._adapter.last_request._request.headers["User-Agent"] == "GlobalUA"
    get_access_token("valid_username", "valid_password", "valid_app_id", "valid_app_secret")
    assert requests_mock._adapter.last_request._request.headers["User-Agent"] == "GlobalUA"

    # But if we have a global and local one, the local has priority
    get_observation(observation_id=16227955, user_agent="CustomUA 2")
    assert requests_mock._adapter.last_request._request.headers["User-Agent"] == "CustomUA 2"
    get_access_token(
        "valid_username",
        "valid_password",
        "valid_app_id",
        "valid_app_secret",
        user_agent="CustomUA 2",
    )
    assert requests_mock._adapter.last_request._request.headers["User-Agent"] == "CustomUA 2"

    # We can reset the global settings to the default:
    pyinaturalist.user_agent = pyinaturalist.DEFAULT_USER_AGENT
    get_observation(observation_id=16227955)
    assert requests_mock._adapter.last_request._request.headers["User-Agent"] == default_ua
    get_access_token("valid_username", "valid_password", "valid_app_id", "valid_app_secret")
    assert requests_mock._adapter.last_request._request.headers["User-Agent"] == default_ua