def convert_pagination_params(params: RequestParams) -> RequestParams: """Allow ``count_only=True`` as a slightly more intuitive shortcut to only get a count of results""" if params.pop('count_only', False) is True: params['per_page'] = 0 if params.pop('reverse', False) is True: params['order'] = 'descending' return params
def convert_observation_fields(params: RequestParams) -> RequestParams: """Translate simplified format of observation field values into API-compatible format""" if "observation_fields" in params: params["observation_field_values_attributes"] = params.pop("observation_fields") obs_fields = params.get("observation_field_values_attributes") if isinstance(obs_fields, dict): params["observation_field_values_attributes"] = [ {"observation_field_id": k, "value": v} for k, v in obs_fields.items() ] return params
def convert_observation_field_params(params: RequestParams) -> RequestParams: """Translate simplified format of observation field values into API-compatible format""" if 'observation_fields' in params: params['observation_field_values_attributes'] = params.pop( 'observation_fields') obs_fields = params.get('observation_field_values_attributes') if isinstance(obs_fields, dict): params['observation_field_values_attributes'] = [{ 'observation_field_id': k, 'value': v } for k, v in obs_fields.items()] return params
def translate_rank_range(params: RequestParams) -> RequestParams: """If min and/or max rank is specified in params, translate into a list of ranks""" def _get_rank_index(rank: str) -> int: if rank not in RANKS: raise ValueError("Invalid rank") return RANKS.index(rank) min_rank, max_rank = params.pop("min_rank", None), params.pop("max_rank", None) if min_rank or max_rank: # Use indices in RANKS list to determine range of ranks to include min_rank_index = _get_rank_index(min_rank) if min_rank else 0 max_rank_index = _get_rank_index(max_rank) + 1 if max_rank else len(RANKS) params["rank"] = RANKS[min_rank_index:max_rank_index] return params
def _validate_multiple_choice_param( params: RequestParams, key: str, choices: Iterable) -> Tuple[RequestParams, Optional[str]]: """Verify that a multiple-choice request parameter contains valid value(s); if not, return an error message. Returns: Parameters with modifications (if any), and a validation error message (if any) """ def is_valid(value, choices): if not value: return True if not isinstance(value, list): value = [value] return all([v in choices for v in value]) def normalize(value): if not value: return value if isinstance(value, list): return [v.replace(' ', '_') for v in value] return value.replace(' ', '_') error_msg = None if key in params: params[key] = normalize(params[key]) if not is_valid(params.get(key), choices): error_msg = MULTIPLE_CHOICE_ERROR_MSG.format(key, choices, params[key]) return params, error_msg
def convert_datetime_params(params: RequestParams) -> RequestParams: """Convert any dates, datetimes, or timestamps in other formats into ISO 8601 strings. API behavior note: params that take date but not time info will accept a full timestamp and just ignore the time, so it's safe to parse both date and datetime strings into timestamps Raises: :py:exc:`dateutil.parser._parser.ParserError` if a date/datetime format is invalid """ for k, v in params.items(): if isinstance(v, (date, datetime)) or (isinstance(v, str) and k in DATETIME_PARAMS): params[k] = convert_isoformat(v) return params
def strip_empty_params(params: RequestParams) -> RequestParams: """Remove any request parameters with empty or ``None`` values.""" return {k: v for k, v in params.items() if v or v is False}
def convert_list_params(params: RequestParams) -> RequestParams: """Convert any list parameters into an API-compatible (comma-delimited) string. Will be url-encoded by requests. For example: `['k1', 'k2', 'k3'] -> k1%2Ck2%2Ck3` """ return {k: convert_list(v) for k, v in params.items()}
def convert_bool_params(params: RequestParams) -> RequestParams: """ Convert any boolean request parameters to javascript-style boolean strings """ for k, v in params.items(): if isinstance(v, bool): params[k] = str(v).lower() return params
def split_common_params( params: RequestParams) -> Tuple[RequestParams, RequestParams]: """Split out common keyword args (for pyinaturalist functions) from request params (for API)""" kwargs = {k: params.pop(k, None) for k in COMMON_PARAMS} return params, kwargs