Skip to content

Commit

Permalink
Merge pull request #490 from pyinat/v2
Browse files Browse the repository at this point in the history
Add basic support for GET /observations v2
  • Loading branch information
JWCook authored May 29, 2023
2 parents c5c14ec + 2372ba3 commit e0b98c6
Show file tree
Hide file tree
Showing 17 changed files with 1,361 additions and 16 deletions.
4 changes: 3 additions & 1 deletion CONTRIBUTORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ Thanks to the following individuals for providing pull requests, feedback, bug r
questions, and other contributions that have helped to improve pyinaturalist:

* [AE Hamrick](https://github.com/AEHamrick)
* [abubelinha](https://github.com/abubelinha)
* [Ben Armstrong](https://github.com/synrg)
* [Dr. Tom August](https://github.com/AugustT)
* [Eduardo Ramírez](https://github.com/eduramirezh)
* [Felipe S Barros](https://github.com/FelipeSBarros)
* [Ken-ichi Ueda](https://github.com/kueda/)
* [Ken-ichi Ueda](https://github.com/kueda)
* [Matt Muir](https://forum.inaturalist.org/u/muir)
* [Nicolas Noé](https://github.com/niconoe)
* [Patrick Leary](https://github.com/pleary)
* [Peter Desmet](https://github.com/peterdesmet)
* [pisum](https://forum.inaturalist.org/u/pisum)
Expand Down
2 changes: 1 addition & 1 deletion HISTORY.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* Added new **User** endpoint: `get_current_user()`

### Modified Endpoints
Add support for searching observations by observation fields, using a new `fields` param for the following functions:
Add support for searching observations by observation fields, using a new `observation_fields` param for the following functions:
* `get_observations()`
* `get_observation_histogram()`
* `get_observation_identifiers()`
Expand Down
2 changes: 1 addition & 1 deletion pyinaturalist/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@
from pyinaturalist.session import ClientSession, clear_cache
from pyinaturalist.v0 import *
from pyinaturalist.v1 import *
from pyinaturalist.v2 import *

# For disambiguation
from pyinaturalist.v0 import create_observation as create_observation_v0
from pyinaturalist.v0 import get_observations as get_observations_v0
from pyinaturalist.v0 import update_observation as update_observation_v0
from pyinaturalist.v0 import delete_observation as delete_observation_v0
from pyinaturalist.v2 import get_observations as get_observations_v2
9 changes: 9 additions & 0 deletions pyinaturalist/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,15 @@
# Endpoint-specific options for multiple choice parameters
V0_OBS_ORDER_BY_PROPERTIES = ['date_added', 'observed_on']
V1_OBS_ORDER_BY_PROPERTIES = ['created_at', 'id', 'observed_on', 'species_guess', 'votes']
V2_OBS_ORDER_BY_PROPERTIES = [
'created_at',
'id',
'observed_on',
'species_guess',
'updated_at',
'votes',
'random',
]
PROJECT_ORDER_BY_PROPERTIES = ['created', 'distance', 'featured', 'recent_posts', 'updated']

# Multiple-choice request parameters, with keys mapped to their possible choices (non-endpoint-specific)
Expand Down
6 changes: 3 additions & 3 deletions pyinaturalist/docs/templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ def _observation_v1(
verifiable: Optional[bool] = None,
not_id: Optional[MultiInt] = None,
sound_license: Optional[MultiStr] = None,
fields: Optional[Union[List, Dict]] = None,
observation_fields: Optional[Union[List, Dict]] = None,
ofv_datatype: Optional[MultiStr] = None,
place_id: Optional[MultiInt] = None,
project_id: Optional[MultiInt] = None,
Expand Down Expand Up @@ -260,7 +260,7 @@ def _observation_v1(
project_id: Must be added to the project this ID or slug
rank: Taxon must have this rank
site_id: Must be affiliated with the iNaturalist network website with this ID
fields: Must have these observation fields (optionally with values)
observation_fields: Must have these observation fields (optionally with values)
ofv_datatype: Must have an observation field value with this datatype
sound_license: Must have at least one sound with this license
without_taxon_id: Exclude observations of these taxa and their descendants
Expand Down Expand Up @@ -333,7 +333,7 @@ def _observation_v0(
on: Filter by date string
extra: Retrieve additional information.
**'projects'** returns info about the projects the observations have been added to,
**'fields'** returns observation field values,
**'observation_fields'** returns observation field values,
**'observation_photos'** returns information about the photos' relationship with the
observation, like their order.
response_format: A supported response format to return
Expand Down
12 changes: 11 additions & 1 deletion pyinaturalist/models/observation.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,20 @@ class Observation(BaseModel):
:v1:`GET /observations <Observations/get_observations>`
"""

application: Dict = field(factory=dict, doc='Application that created the observation')
created_at: datetime = datetime_now_field(doc='Date and time the observation was created')
captive: bool = field(
default=None, doc='Indicates if the organism is non-wild (captive or cultivated)'
)
community_taxon_id: int = field(default=None, doc='The current community identification taxon')
context_geoprivacy: str = field(default=None)
context_taxon_geoprivacy: str = field(default=None)
context_user_geoprivacy: str = field(default=None)
description: str = field(default=None, doc='Observation description')
faves: List[Dict] = field(
factory=list, doc='Details on users who have favorited the observation'
)
flags: List[Dict] = field(factory=list)
geoprivacy: str = field(default=None, options=GEOPRIVACY_LEVELS, doc='Location privacy level')
identifications_count: int = field(default=0, doc='Total number of identifications')
identifications_most_agree: bool = field(
Expand Down Expand Up @@ -131,11 +136,15 @@ class Observation(BaseModel):
default=None, doc="Taxon name from observer's initial identification"
)
tags: List[str] = field(factory=list, doc='Arbitrary user tags added to the observation')
taxon_geoprivacy: str = field(default=None)
updated_at: DateTime = datetime_field(doc='Date and time the observation was last updated')
uri: str = field(default=None, doc='Link to observation details page')
uuid: str = field(
default=None, doc='Universally unique ID; generally preferred over ``id`` where possible'
)
viewer_trusted_by_observer: bool = field(
default=None, doc='Observer trusts the authenticated user with access to hidden coordinates'
)
votes: List[Dict] = field(factory=list, doc='Votes on data quality assessment metrics')

# Lazy-loaded model objects
Expand Down Expand Up @@ -173,15 +182,16 @@ class Observation(BaseModel):
# created_at_details: Dict = field(factory=dict)
# created_time_zone: str = field(default=None)
# faves_count: int = field(default=None)
# flags: List = field(factory=list)
# geojson: Dict = field(factory=dict)
# id_please: bool = field(default=None)
# map_scale: int = field(default=None)
# non_owner_ids: List = field(factory=list)
# non_traditional_projects: property = LazyProperty(Project.from_json_list, type=List[Project])
# observed_on_details: Dict = field(factory=dict)
# observed_on_string: str = field(default=None)
# observation_photos: List[Photo] = field(converter=Photo.from_dict_list, factory=list)
# observed_time_zone: str = field(default=None)
# private_geojson: Dict = field(factory=dict)
# spam: bool = field(default=None)
# time_observed_at: DateTime = datetime_attr
# time_zone_offset: str = field(default=None)
Expand Down
3 changes: 2 additions & 1 deletion pyinaturalist/request_params.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,8 @@ def convert_observation_field_params(params: RequestParams) -> RequestParams:


def convert_observation_field_filters(params: RequestParams) -> RequestParams:
fields = params.pop('fields', None)
"""Convert observation field filters from simplified format to full request params"""
fields = params.pop('observation_fields', None)
if not fields:
return params
# List of fields to filter on (any value)
Expand Down
4 changes: 2 additions & 2 deletions pyinaturalist/v1/observations.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,11 +67,11 @@ def get_observations(**params) -> JsonResponse:
Search for observations with a given observation field:
>>> response = get_observations(fields=['Species count'])
>>> response = get_observations(observation_fields=['Species count'])
Or observation field value:
>>> response = get_observations(fields={'Species count': 2})
>>> response = get_observations(observation_fields={'Species count': 2})
.. admonition:: Example Response
Expand Down
7 changes: 6 additions & 1 deletion pyinaturalist/v2/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
"""Coming soon!"""
"""
Functions to access the iNaturalist API v2
See: http://api.inaturalist.org/v2/docs/
"""
# flake8: noqa: F401, F403
from pyinaturalist.v2.observations import get_observations
Loading

0 comments on commit e0b98c6

Please sign in to comment.