예제 #1
0
def get_observation_fields(user_agent: str = None, **kwargs) -> ListResponse:
    """Search observation fields. Observation fields are basically typed data fields that
    users can attach to observation.

    **API reference:** https://www.inaturalist.org/pages/api+reference#get-observation_fields

    Example:

        >>> get_observation_fields(q='number of individuals')
        >>> # Show just observation field IDs and names
        >>> from pprint import pprint
        >>> pprint({r['id']: r['name'] for r in response})

        .. admonition:: Example Response
            :class: toggle

            .. literalinclude:: ../sample_data/get_observation_fields_page1.json
                :language: javascript

    Returns:
        Observation fields as a list of dicts
    """
    kwargs = check_deprecated_params(**kwargs)
    response = get(
        "{base_url}/observation_fields.json".format(base_url=INAT_BASE_URL),
        params=kwargs,
        user_agent=user_agent,
    )
    return response.json()
예제 #2
0
def create_observation(
    params: RequestParams = None, access_token: str = None, user_agent: str = None, **kwargs
) -> ListResponse:
    """Create a new observation.

    **API reference:** https://www.inaturalist.org/pages/api+reference#post-observations

    Example:
        >>> token = get_access_token('...')
        >>> create_observation(
        >>>     access_token=token,
        >>>     species_guess='Pieris rapae',
        >>>     local_photos='~/observation_photos/2020_09_01_14003156.jpg',
        >>>     observation_fields={297: 1},  # 297 is the obs. field ID for 'Number of individuals'
        >>> )

        .. admonition:: Example Response
            :class: toggle

            .. literalinclude:: ../sample_data/create_observation_result.json
                :language: javascript

        .. admonition:: Example Response (failure)
            :class: toggle

            .. literalinclude:: ../sample_data/create_observation_fail.json
                :language: javascript

    Returns:
        JSON response containing the newly created observation(s)

    Raises:
        :py:exc:`requests.HTTPError`, if the call is not successful. iNaturalist returns an
        error 422 (unprocessable entity) if it rejects the observation data (for example an
        observation date in the future or a latitude > 90. In that case the exception's
        ``response`` attribute gives more details about the errors.
    """
    # Accept either top-level params (like most other endpoints)
    # or nested {"observation": params} (like the iNat API accepts directly)
    if "observation" in kwargs:
        kwargs.update(kwargs.pop("observation"))
    kwargs = check_deprecated_params(params, **kwargs)
    kwargs = convert_observation_fields(kwargs)
    if "local_photos" in kwargs:
        kwargs["local_photos"] = ensure_file_objs(kwargs["local_photos"])

    response = post(
        url="{base_url}/observations.json".format(base_url=INAT_BASE_URL),
        json={"observation": kwargs},
        access_token=access_token,
        user_agent=user_agent,
    )
    response.raise_for_status()
    return response.json()
예제 #3
0
def get_all_observation_species_counts(user_agent: str = None,
                                       **kwargs) -> List[JsonResponse]:
    """Like :py:func:`get_observation_species_counts()`, but handles pagination and returns all
    results in one call. Explicit pagination parameters will be ignored.

    Notes:
    While the ``page`` parameter is undocumented for observations/species_counts, it appears to be supported.
    ``id_above`` and ``id_below`` are not helpful in the context.

    Example:
        >>> get_all_observation_species_counts(
        ...     user_agent=None,
        ...     quality_grade='research',
        ...     place_id=154695,
        ...     iconic_taxa='Reptilia',
        ... )

        .. admonition:: Example Response
            :class: toggle

            .. literalinclude:: ../sample_data/get_all_observation_species_counts_ex_results.json
                :language: JSON

    Returns:
        Combined list of taxon records with counts
    """
    kwargs = check_deprecated_params(**kwargs)
    results = []  # type: List[JsonResponse]
    page = 1

    pagination_params = {
        **kwargs,
        **{
            "per_page": PER_PAGE_RESULTS,
            "user_agent": user_agent,
        },
    }

    while True:
        pagination_params["page"] = page
        page_obs = get_observation_species_counts(**pagination_params)
        results = results + page_obs.get("results", [])

        if len(results) == page_obs["total_results"]:
            return results

        sleep(THROTTLING_DELAY)
        page += 1
예제 #4
0
def get_all_observations(params: RequestParams = None,
                         user_agent: str = None,
                         **kwargs) -> List[JsonResponse]:
    """Like :py:func:`get_observations()`, but handles pagination and returns all results in one
    call. Explicit pagination parameters will be ignored.

    Notes on pagination from the iNaturalist documentation:
    "The large size of the observations index prevents us from supporting the page parameter when
    retrieving records from large result sets. If you need to retrieve large numbers of records,
    use the ``per_page`` and ``id_above`` or ``id_below`` parameters instead."

    Example:

        >>> observations = get_all_observations(
        >>>     taxon_name='Danaus plexippus',
        >>>     created_on='2020-08-27',
        >>> )

    Returns:
        Combined list of observation records. Response format is the same as the inner "results"
        object returned by :py:func:`.get_observations()`.
    """
    kwargs = check_deprecated_params(params, **kwargs)
    results = []  # type: List[JsonResponse]
    id_above = 0
    pagination_params = {
        **kwargs,
        **{
            "order_by": "id",
            "order": "asc",
            "per_page": PER_PAGE_RESULTS,
            "user_agent": user_agent,
        },
    }

    while True:
        pagination_params["id_above"] = id_above
        page_obs = get_observations(**pagination_params)
        results = results + page_obs.get("results", [])

        if page_obs["total_results"] <= PER_PAGE_RESULTS:
            return results

        sleep(THROTTLING_DELAY)
        id_above = results[-1]["id"]
예제 #5
0
def get_observations(params: RequestParams = None,
                     user_agent: str = None,
                     **kwargs) -> JsonResponse:
    """Search observations.

    **API reference:** http://api.inaturalist.org/v1/docs/#!/Observations/get_observations

    Example:

        >>> # Get observations of Monarch butterflies with photos + public location info,
        >>> # on a specific date in the provice of Saskatchewan, CA
        >>> observations = get_observations(
        >>>     taxon_name='Danaus plexippus',
        >>>     created_on='2020-08-27',
        >>>     photos=True,
        >>>     geo=True,
        >>>     geoprivacy='open',
        >>>     place_id=7953,
        >>> )

        .. admonition:: Example Response
            :class: toggle

            .. literalinclude:: ../sample_data/get_observations_node_page1.json
                :language: JSON

    Returns:
        JSON response containing observation records
    """
    kwargs = check_deprecated_params(params, **kwargs)
    validate_multiple_choice_param(kwargs, "order_by",
                                   NODE_OBS_ORDER_BY_PROPERTIES)
    r = make_inaturalist_api_get_call("observations",
                                      params=kwargs,
                                      user_agent=user_agent)
    return r.json()
예제 #6
0
def update_observation(
    observation_id: int,
    params: RequestParams = None,
    access_token: str = None,
    user_agent: str = None,
    **kwargs
) -> ListResponse:
    """
    Update a single observation.

    **API reference:** https://www.inaturalist.org/pages/api+reference#put-observations-id

    .. note::

        Unlike the underlying REST API endpoint, this function will **not** delete any existing
        photos from your observation if not specified in ``local_photos``. If you want this to
        behave the same as the REST API and you do want to delete photos, call with
        ``ignore_photos=False``.

    Example:

        >>> token = get_access_token('...')
        >>> update_observation(
        >>>     17932425,
        >>>     access_token=token,
        >>>     description="updated description!",
        >>> )

        .. admonition:: Example Response
            :class: toggle

            .. literalinclude:: ../sample_data/update_observation_result.json
                :language: javascript

    Returns:
        JSON response containing the newly updated observation(s)

    Raises:
        :py:exc:`requests.HTTPError`, if the call is not successful. iNaturalist returns an
            error 410 if the observation doesn't exists or belongs to another user.
    """
    # Accept either top-level params (like most other endpoints)
    # or nested params (like the iNat API actually accepts)
    if "observation" in kwargs:
        kwargs.update(kwargs.pop("observation"))
    kwargs = check_deprecated_params(params, **kwargs)
    kwargs = convert_observation_fields(kwargs)
    if "local_photos" in kwargs:
        kwargs["local_photos"] = ensure_file_objs(kwargs["local_photos"])

    # This is the one Boolean parameter that's specified as an int, for some reason.
    # Also, set it to True if not specified, which seems like much saner default behavior.
    if "ignore_photos" in kwargs:
        kwargs["ignore_photos"] = int(kwargs["ignore_photos"])
    else:
        kwargs["ignore_photos"] = 1

    response = put(
        url="{base_url}/observations/{id}.json".format(base_url=INAT_BASE_URL, id=observation_id),
        json={"observation": kwargs},
        access_token=access_token,
        user_agent=user_agent,
    )
    response.raise_for_status()
    return response.json()