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()
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()
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
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"]
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()
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()