This repository has been archived on 2022-08-04. You can view files and clone it, but cannot push or open issues or pull requests.
python-usda/usda/client.py

302 lines
12 KiB
Python

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from usda.enums import \
UsdaNdbListType, UsdaNdbReportType, UsdaUriActions, UsdaNdbDataSource
from usda.domain import \
ListItem, Nutrient, Food, FoodReport, FoodReportV2, NutrientReportFood
from usda.base import DataGovClientBase, DataGovApiError
from usda.pagination import \
RawPaginator, ModelPaginator, RawNutrientReportPaginator
import warnings
REPORT_TYPE_WARNING = 'Requested a {} report, but received a basic report. ' \
'Full and Statistics reports are only available on ' \
'Standard Reference food items.'
class UsdaClient(DataGovClientBase):
"""
Describes a USDA NDB API client.
"""
def __init__(self, api_gov_key):
"""
:param str api_gov_key: A Data.gov API key.
For small testing purposes, you may use ``DEMO_KEY`` as an API key;
but beware of rate limit errors.
"""
super().__init__('ndb/', api_gov_key)
def list_nutrients_raw(self, **kwargs):
r"""
Get a list of available nutrients in the database as JSON.
:param \**kwargs: GET parameters to send along the request.
:returns: Generator yielding nutrients as JSON data.
:rtype: usda.pagination.RawPaginator
"""
kwargs.setdefault('lt', UsdaNdbListType.all_nutrients.value)
return RawPaginator(self, UsdaUriActions.list, **kwargs)
def list_nutrients(self, max, offset=0, sort='n'):
"""
Get a list of available nutrients in the database.
Useful to generate Nutrient Reports.
:param int max: Maximum number of nutrients to return, up to 1500.
:param int offset: Index to start listing at.
:param str sort: The sorting method.
``'r'`` for relevance, ``'n'`` for item ID.
:returns: Generator yielding nutrients as
:class:`usda.domain.Nutrient` instances.
:rtype: usda.pagination.ModelPaginator
"""
return ModelPaginator(
Nutrient,
self.list_nutrients_raw(max=max, offset=offset, sort=sort),
)
def list_foods_raw(self, **kwargs):
r"""
Get a list of available food items in the database as JSON.
:param \**kwargs: GET parameters to send along the request.
:returns: Generator yielding food items as JSON data.
:rtype: usda.pagination.RawPaginator
"""
kwargs.setdefault('lt', UsdaNdbListType.food.value)
return RawPaginator(self, UsdaUriActions.list, **kwargs)
def list_foods(self, max, offset=0, sort='n'):
"""
Get a list of available food items in the database.
Useful to generate Food Reports.
:param int max: Maximum number of items to return, up to 1500.
:param int offset: Index to start listing at.
:param str sort: The sorting method.
``'r'`` for relevance, ``'n'`` for item ID.
:returns: Generator yielding food items as
:class:`usda.domain.Food` instances.
:rtype: usda.pagination.ModelPaginator
"""
return ModelPaginator(
Food,
self.list_foods_raw(max=max, offset=offset, sort=sort),
)
def list_food_groups_raw(self, **kwargs):
r"""
Get a list of available food groups in the database as JSON.
:param \**kwargs: GET parameters to send along the request.
:returns: Generator yielding food groups as JSON data.
:rtype: usda.pagination.RawPaginator
"""
kwargs.setdefault('lt', UsdaNdbListType.food_groups.value)
return RawPaginator(self, UsdaUriActions.list, **kwargs)
def list_food_groups(self, max, offset=0, sort='n'):
"""
Get a list of available food groups in the database.
:param int max: Maximum number of groups to return, up to 1500.
:param int offset: Index to start listing at.
:param str sort: The sorting method.
``'r'`` for relevance, ``'n'`` for group ID.
:returns: Generator yielding food groups as
:class:`usda.domain.ListItem` instances.
:rtype: usda.pagination.ModelPaginator
"""
return ModelPaginator(
ListItem,
self.list_food_groups_raw(max=max, offset=offset, sort=sort),
)
def list_derivation_codes_raw(self, **kwargs):
r"""
Get a list of available derivation codes in the database as JSON.
:param \**kwargs: GET parameters to send along the request.
:returns: Generator yielding derivation codes as JSON data.
:rtype: usda.pagination.RawPaginator
"""
kwargs.setdefault('lt', UsdaNdbListType.derivation_codes.value)
return RawPaginator(self, UsdaUriActions.list, **kwargs)
def list_derivation_codes(self, max, offset=0, sort='n'):
"""
Get a list of available derivation codes in the database.
:param int max: Maximum number of codes to return, up to 1500.
:param int offset: Index to start listing at.
:param str sort: The sorting method.
``'r'`` for relevance, ``'n'`` for code ID.
:returns: Generator yielding derivation codes as
:class:`usda.domain.ListItem` instances.
:rtype: usda.pagination.ModelPaginator
"""
return ModelPaginator(
ListItem,
self.list_derivation_codes_raw(max=max, offset=offset, sort=sort),
)
def search_foods_raw(self, **kwargs):
r"""
Get a list of food items matching a specified query, as JSON.
:param \**kwargs: GET parameters to send along the request.
:returns: Generator yielding food items as JSON data.
:rtype: usda.pagination.RawPaginator
"""
return RawPaginator(self, UsdaUriActions.search, **kwargs)
def search_foods(self, query, max, offset=0, sort='r', ds=None):
"""
Get a list of food items matching a specified query.
:param str query: A search query.
:param int max: Maximum number of items to return, up to 1500.
:param int offset: Index to start listing at.
:param str sort: The sorting method.
``'r'`` for relevance, ``'n'`` for item ID.
:param ds: Optionally filter by data source: Standard Reference or
Branded Food Products.
:type ds: usda.enums.UsdaNdbDataSource
:returns: Generator yielding food items as
:class:`usda.domain.Food` instances.
:rtype: usda.pagination.ModelPaginator
"""
params = {'q': query, 'max': max, 'offset': offset, 'sort': sort}
if ds:
if isinstance(ds, UsdaNdbDataSource):
params['ds'] = ds.value
else:
params['ds'] = ds
return ModelPaginator(Food, self.search_foods_raw(**params))
def get_food_report_raw(self, **kwargs):
r"""
Get a Food Report for a given food item ID as JSON.
:param \**kwargs: GET parameters to send along the request.
:returns: A food report as JSON data.
:rtype: dict
:raises requests.exceptions.HTTPError: If a response has a HTTP 4xx or
5xx status code.
:raises DataGovApiError: If a Data.gov API returns an error.
"""
return self.run_request(UsdaUriActions.report, **kwargs)
def get_food_report(self, ndb_food_id,
report_type=UsdaNdbReportType.basic):
"""
Get a Food Report for a given food item ID.
:param int ndb_food_id: ID of a food item to get a food report for.
:param report_type: Food Report type to get.
:type report_type: usda.enums.UsdaNdbReportType
:returns: A food report for the given food item ID.
:rtype: usda.domain.FoodReport
:raises requests.exceptions.HTTPError: If a response has a HTTP 4xx or
5xx status code.
:raises DataGovApiError: If a Data.gov API returns an error.
"""
report = FoodReport.from_response_data(
self.get_food_report_raw(
type=report_type.value,
ndbno=str(ndb_food_id).zfill(5),
),
)
if report.report_type == UsdaNdbReportType.basic \
and report.report_type != report_type:
warnings.warn(
REPORT_TYPE_WARNING.format(report_type.name),
UserWarning,
stacklevel=2,
)
return report
def get_food_report_v2_raw(self, **kwargs):
r"""
Get a Food Report version 2 for one or more food item IDs as JSON.
:param \**kwargs: GET parameters to send along the request.
:returns: A list of food reports version 2 as JSON data.
:rtype: dict
:raises requests.exceptions.HTTPError: If a response has a HTTP 4xx or
5xx status code.
:raises DataGovApiError: If a Data.gov API returns an error.
"""
return self.run_request(UsdaUriActions.v2report, **kwargs)
def get_food_report_v2(self, *ids, report_type=UsdaNdbReportType.basic):
r"""
Get a list of Food Reports version 2 for one or more food item IDs.
:param int \*ids: Up to 25 food item IDs.
:param report_type: Food Report type to get.
:type report_type: usda.enums.UsdaNdbReportType
:returns: A list of food reports versions 2.
:rtype: list(usda.domain.FoodReportV2)
:raises requests.exceptions.HTTPError: If a response has a HTTP 4xx or
5xx status code.
:raises DataGovApiError: If a Data.gov API returns an error.
"""
def _get_report(food):
if 'error' in food:
raise DataGovApiError(food['error'])
report = FoodReportV2.from_response_data(food)
if report.report_type == UsdaNdbReportType.basic \
and report.report_type != report_type:
warnings.warn(
REPORT_TYPE_WARNING.format(report_type.name),
UserWarning,
stacklevel=2,
)
return report
return list(map(
_get_report,
self.get_food_report_v2_raw(
type=report_type.value,
ndbno=[
str(food_id).zfill(5) for food_id in ids
],
)['foods']
))
def get_nutrient_report_raw(self, **kwargs):
r"""
Get a Nutrient Report for each of the given nutrient IDs as JSON.
:param \**kwargs: GET parameters to send along the request.
:returns: A generator yielding food items with the specified nutrients
as JSON data.
:rtype: usda.pagination.RawNutrientReportPaginator
"""
return RawNutrientReportPaginator(
self, UsdaUriActions.nutrients, **kwargs)
def get_nutrient_report(self, *nutrients):
r"""
Get a Nutrient Report for each of the given nutrient IDs.
:param int \*nutrients: Up to 20 nutrient IDs.
:returns: A generator yielding food items with the specified nutrients
as :class:`usda.domain.NutrientReportFood` instances.
:rtype: usda.pagination.ModelPaginator
:raises ValueError: If there are more than 20 nutrient IDs.
:raises requests.exceptions.HTTPError: If a response has a HTTP 4xx or
5xx status code.
:raises DataGovApiError: If a Data.gov API returns an error.
"""
if len(nutrients) > 20:
raise ValueError("A nutrient report request cannot contain "
"more than 20 nutrients")
return ModelPaginator(
NutrientReportFood,
self.get_nutrient_report_raw(nutrients=nutrients),
)