From 320382ec199c0f6a4581e29539799125c3d49336 Mon Sep 17 00:00:00 2001 From: Jordan Cook Date: Sun, 4 Jul 2021 13:03:45 -0500 Subject: [PATCH] Sort non-property attrs by name; remove some completed TODOs --- pyinaturalist/api_docs/model_docs.py | 3 -- pyinaturalist/constants.py | 2 +- pyinaturalist/formatters.py | 1 - pyinaturalist/models/observation.py | 43 ++++++++++++++-------------- pyinaturalist/models/project.py | 30 ++++++++++++------- pyinaturalist/node_api.py | 1 - pyinaturalist/request_params.py | 1 - pyinaturalist/v0/observations.py | 1 - test/test_models.py | 1 - 9 files changed, 41 insertions(+), 42 deletions(-) diff --git a/pyinaturalist/api_docs/model_docs.py b/pyinaturalist/api_docs/model_docs.py index e10ba5ea..8470e531 100644 --- a/pyinaturalist/api_docs/model_docs.py +++ b/pyinaturalist/api_docs/model_docs.py @@ -39,9 +39,6 @@ def get_model_classes() -> List[Type]: return model_classes -# TODO: Also include regular @properties? -# TODO: CSS to style LazyProperties with a different background color? -# TODO: Remove autodoc member docs for LazyProperties def get_model_doc(cls: Type) -> List[Tuple[str, str, str]]: """Get the name, type and description for all model attributes, properties, and LazyProperties. If an attribute has metadata for options (possible values for the attribute), include those diff --git a/pyinaturalist/constants.py b/pyinaturalist/constants.py index 3a262b76..04528cb9 100644 --- a/pyinaturalist/constants.py +++ b/pyinaturalist/constants.py @@ -130,7 +130,7 @@ ID_CATEGORIES = ['improving', 'supporting', 'leading', 'maverick'] ORDER_DIRECTIONS = ['asc', 'desc'] PLACE_CATEGORIES = ['standard', 'community'] -PROJECT_TYPES = ['collection', 'umbrella'] +PROJECT_TYPES = ['assessment', 'bioblitz', 'collection', 'umbrella'] QUALITY_GRADES = ['casual', 'needs_id', 'research'] SEARCH_PROPERTIES = ['names', 'tags', 'description', 'place'] SOURCES = ['places', 'projects', 'taxa', 'users'] diff --git a/pyinaturalist/formatters.py b/pyinaturalist/formatters.py index ae40616e..7b8695ef 100644 --- a/pyinaturalist/formatters.py +++ b/pyinaturalist/formatters.py @@ -199,7 +199,6 @@ def _format_model_objects(obj: ResponseOrResults, cls: Type[BaseModel], **kwargs return '\n'.join([str(obj) for obj in objects]) -# TODO: Figure out type annotations for these. Or just replace with pprint()? format_controlled_terms = partial(_format_model_objects, cls=ControlledTerm) format_identifications = partial(_format_model_objects, cls=Identification) format_observations = partial(_format_model_objects, cls=Observation) diff --git a/pyinaturalist/models/observation.py b/pyinaturalist/models/observation.py index 9eeb0353..b97c7dcd 100644 --- a/pyinaturalist/models/observation.py +++ b/pyinaturalist/models/observation.py @@ -1,3 +1,4 @@ +# TODO: Possible models for faves, sounds, and votes? from datetime import datetime from typing import Any, Dict, List, Optional @@ -47,6 +48,7 @@ class Observation(BaseModel): ) community_taxon_id: int = field(default=None, doc='The current community identification taxon') description: str = field(default=None, doc='Observation description') + faves: List[Dict] = field(factory=list, doc='Details on users who have favorited the observation') geoprivacy: str = field(default=None, options=GEOPRIVACY_LEVELS, doc='Location privacy level') identifications_count: int = field(default=None, doc='Total number of identifications') identifications_most_agree: bool = field(default=None, doc='Indicates if most identifications agree') @@ -68,38 +70,22 @@ class Observation(BaseModel): default=None, doc='Indicates if coordinates are obscured (showing a broad approximate location on the map)', ) + observed_on: DateTime = datetime_field(doc='Date and time the organism was observed') + outlinks: List[Dict] = field( + factory=list, doc='Linked observation pages on other sites (e.g., GBIF)' + ) out_of_range: bool = field( default=None, doc='Indicates if the taxon is observed outside of its known range' ) owners_identification_from_vision: bool = field( default=None, doc="Indicates if the owner's ID was selected from computer vision results" ) + place_guess: str = field(default=None, doc='Place name determined from observation coordinates') + place_ids: List[int] = field(factory=list) positional_accuracy: int = field( default=None, doc='GPS accuracy in meters (real accuracy, if obscured)' ) - public_positional_accuracy: int = field( - default=None, doc='GPS accuracy in meters (not accurate if obscured)' - ) - quality_grade: str = field(default=None, options=QUALITY_GRADES, doc='Quality grade') - site_id: int = field( - default=None, doc='Site ID for iNaturalist network members, or ``1`` for inaturalist.org' - ) - species_guess: str = field(default=None, doc="Taxon name from observer's initial identification") - observed_on: DateTime = datetime_field(doc='Date and time the organism was observed') - 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' - ) - - # Nested collections - # TODO: Possible models for faves, sounds, and votes? - faves: List[Dict] = field(factory=list, doc='Details on users who have favorited the observation') - outlinks: List[Dict] = field( - factory=list, doc='Linked observation pages on other sites (e.g., GBIF)' - ) - place_ids: List[int] = field(factory=list) preferences: Dict[str, Any] = field( factory=dict, doc='Any user observation preferences (related to community IDs, coordinate access, etc.)', @@ -111,10 +97,23 @@ class Observation(BaseModel): project_ids_without_curator_id: List[int] = field( factory=list, doc='Project IDs without curator identification for this observation' ) + public_positional_accuracy: int = field( + default=None, doc='GPS accuracy in meters (not accurate if obscured)' + ) + quality_grade: str = field(default=None, options=QUALITY_GRADES, doc='Quality grade') quality_metrics: List[Dict] = field(factory=list, doc='Data quality assessment metrics') reviewed_by: List[int] = field(factory=list, doc='IDs of users who have reviewed the observation') + site_id: int = field( + default=None, doc='Site ID for iNaturalist network members, or ``1`` for inaturalist.org' + ) sounds: List[Dict] = field(factory=list, doc='Observation sound files') + species_guess: str = field(default=None, doc="Taxon name from observer's initial identification") tags: List[str] = field(factory=list, doc='Arbitrary user tags added to the observation') + 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' + ) votes: List[Dict] = field(factory=list, doc='Votes on data quality assessment metrics') # Lazy-loaded model objects diff --git a/pyinaturalist/models/project.py b/pyinaturalist/models/project.py index 7da8d9b8..5aa77d66 100644 --- a/pyinaturalist/models/project.py +++ b/pyinaturalist/models/project.py @@ -1,7 +1,14 @@ from datetime import datetime from typing import Dict, List -from pyinaturalist.constants import INAT_BASE_URL, Coordinates, DateTime, JsonResponse, TableRow +from pyinaturalist.constants import ( + INAT_BASE_URL, + PROJECT_TYPES, + Coordinates, + DateTime, + JsonResponse, + TableRow, +) from pyinaturalist.models import ( BaseModel, LazyProperty, @@ -81,21 +88,22 @@ class Project(BaseModel): doc='Indicates if this is an umbrella project (containing observations from other projects)', ) location: Coordinates = coordinate_pair() - place_id: int = field(default=None) - prefers_user_trust: bool = field(default=None) - project_type: str = field(default=None) # Enum - slug: str = field(default=None, doc='URL slug') - terms: str = field(default=None, doc='Project terms') - title: str = field(default=None, doc='Project title') - updated_at: DateTime = datetime_field(doc='Date and time the project was last updated') - - # Nested collections - project_observation_rules: List[Dict] = field(factory=list) + place_id: int = field(default=None, doc='Project place ID') + prefers_user_trust: bool = field( + default=None, + doc='Indicates if the project wants users to share hidden coordinates with the project admins', + ) + project_observation_rules: List[Dict] = field(factory=list, doc='Project observation rules') + project_type: str = field(default=None, options=PROJECT_TYPES, doc='Project type') # Enum rule_preferences: List[Dict] = field(factory=list) search_parameters: List[Dict] = field(factory=list, doc='Filters for observations to include') site_features: List[Dict] = field( factory=list, doc='Details about if/when the project was featured on inaturalist.org' ) + slug: str = field(default=None, doc='URL slug') + terms: str = field(default=None, doc='Project terms') + title: str = field(default=None, doc='Project title') + updated_at: DateTime = datetime_field(doc='Date and time the project was last updated') user_ids: List[int] = field(factory=list) # Lazy-loaded model objects diff --git a/pyinaturalist/node_api.py b/pyinaturalist/node_api.py index 6966b948..9ee7100d 100644 --- a/pyinaturalist/node_api.py +++ b/pyinaturalist/node_api.py @@ -19,7 +19,6 @@ def get_all_observations(**params) -> List[JsonResponse]: return paginate_all(get_observations, method='id', **params)['results'] -# TODO: Deprecate get_geojson_observations and move to pyinaturalist-convert def get_geojson_observations(properties: List[str] = None, **params) -> JsonResponse: """Get all observation results combined into a GeoJSON ``FeatureCollection``. By default this includes some basic observation properties as GeoJSON ``Feature`` properties. diff --git a/pyinaturalist/request_params.py b/pyinaturalist/request_params.py index 34f5117d..3d8b5dfe 100644 --- a/pyinaturalist/request_params.py +++ b/pyinaturalist/request_params.py @@ -2,7 +2,6 @@ The main purpose of these functions is to support some python-specific conveniences and translate them into standard request parameters, along with request validation that makes debugging easier. """ -# TODO: It would be nice to put all the multiple-choice options on the models and use attrs validators from datetime import date, datetime from dateutil.relativedelta import relativedelta from logging import getLogger diff --git a/pyinaturalist/v0/observations.py b/pyinaturalist/v0/observations.py index 49601d4c..7873d88c 100644 --- a/pyinaturalist/v0/observations.py +++ b/pyinaturalist/v0/observations.py @@ -1,4 +1,3 @@ -# TODO: Decide on consistent terminology for POST/PUT endpoints, rename, and add DeprecationWarnings to previous names from logging import getLogger from typing import List, Union diff --git a/test/test_models.py b/test/test_models.py index 59744290..5cd5093f 100644 --- a/test/test_models.py +++ b/test/test_models.py @@ -454,7 +454,6 @@ def test_taxon__conservation_status(): assert cs.status_name == 'imperiled' -# TODO: No sample data for this yet. Only on get_taxa_by_id response for particular taxa. def test_taxon__conservation_statuses(): css = Taxon.from_json(j_taxon_6_cs_statuses).conservation_statuses[0] assert isinstance(css, ConservationStatus)