Skip to content

Commit

Permalink
docs, tests, remove search functions
Browse files Browse the repository at this point in the history
  • Loading branch information
OliverSherouse committed Jan 25, 2024
1 parent 208da1f commit 2d84fdf
Show file tree
Hide file tree
Showing 11 changed files with 538 additions and 279 deletions.
90 changes: 90 additions & 0 deletions docs/basic_functionality.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# Basic functionality

The basic functionality for `wbdata` users is provided by a set of functions in
the topic level package namespace.

## Data Retrieval

These are the functions for actually getting data values from the World Bank
API.

### Raw Data Retrieval

::: wbdata.client.Client.get_data
options:
show_root_heading: true
show_root_full_path: false
heading_level: 4


### Pandas Data Retrieval

These functions require Pandas to be installed to work.

::: wbdata.client.Client.get_series
options:
show_root_heading: true
show_root_full_path: false
heading_level: 4

::: wbdata.client.Client.get_dataframe
options:
show_root_heading: true
show_root_full_path: false
heading_level: 4

## Metadata Retrieval

These functions, for the most part, are for finding the parameters you want to
put into the data retrieval functions. These all return
[SearchResult][wbdata.client.SearchResults], which are lists that pretty-print
the table in an interactive environment, and which contain dictionary
representations of the requested resource.

### Searchable Metadata

There are enough indicators and countries that it's annoying to look through
them, so the functions for retrieving information about them can be narrowed
with additional facets and filtered with a search term or regular expression
supplied to the `query` parameter.

::: wbdata.client.Client.get_country
options:
show_root_heading: true
show_root_full_path: false
heading_level: 4


::: wbdata.client.Client.get_indicator
options:
show_root_heading: true
show_root_full_path: false
heading_level: 4

### Indicator Facets

::: wbdata.client.Client.get_source
options:
show_root_heading: true
show_root_full_path: false
heading_level: 4

::: wbdata.client.Client.get_topic
options:
show_root_heading: true
show_root_full_path: false
heading_level: 4

### Country Facets

::: wbdata.client.Client.get_incomelevel
options:
show_root_heading: true
show_root_full_path: false
heading_level: 4

::: wbdata.client.Client.get_lendingtype
options:
show_root_heading: true
show_root_full_path: false
heading_level: 4
6 changes: 0 additions & 6 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,6 @@ you can install using pip:
You can also download or get the source from
[GitHub](http://github.com/OliverSherouse/wbdata).

## Detailed Documentation

- [Wbdata library reference](wbdata_library.md)
- [api module](api_module.md)
- [fetcher module](fetcher_module.md)

## A Typical User Session

Let's say we want to find some data for the ease of doing business in some
Expand Down
2 changes: 2 additions & 0 deletions docs/reference/client.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Client Module
::: wbdata.client
3 changes: 3 additions & 0 deletions docs/reference/dates.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Dates module

::: wbdata.dates
3 changes: 3 additions & 0 deletions docs/reference/fetcher.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Fetcher Module

::: wbdata.fetcher
47 changes: 26 additions & 21 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def test_search_results_repr(data, expected):
],
)
def test_parse_value_or_iterable(arg, expected):
assert client.parse_value_or_iterable(arg) == expected
assert client._parse_value_or_iterable(arg) == expected


@pytest.mark.parametrize(
Expand All @@ -48,7 +48,7 @@ def test_parse_value_or_iterable(arg, expected):
],
)
def test_cast_float(value, expected):
assert client.cast_float(value) == expected
assert client._cast_float(value) == expected


@pytest.fixture
Expand Down Expand Up @@ -112,7 +112,9 @@ def test_get_data_args(mock_client, kwargs, expected_url, expected_args):
mock_client.fetcher.fetch = mock.Mock(return_value="Foo")
mock_client.get_data(**kwargs)
mock_client.fetcher.fetch.assert_called_once_with(
expected_url, expected_args, skip_cache=kwargs.get("skip_cache", False)
url=expected_url,
params=expected_args,
skip_cache=kwargs.get("skip_cache", False),
)


Expand Down Expand Up @@ -141,7 +143,7 @@ def test_parse_dates(mock_client):
],
)
def test_id_only_query(mock_client, url, id_, skip_cache, expected_url):
mock_client.fetcher.fetch = mock.Mock(return_value=[["foo"]])
mock_client.fetcher.fetch = mock.Mock(return_value=["foo"])
got = mock_client._id_only_query(url, id_, skip_cache=skip_cache)
assert list(got) == ["foo"]
mock_client.fetcher.fetch.assert_called_once_with(
Expand All @@ -155,7 +157,7 @@ def test_id_only_query(mock_client, url, id_, skip_cache, expected_url):
(function, id_, skip_cache, f"{host}{path}")
for ((function, host), (id_, path), skip_cache) in itertools.product(
(
("get_source", client.SOURCES_URL),
("get_source", client.SOURCE_URL),
("get_incomelevel", client.ILEVEL_URL),
("get_topic", client.TOPIC_URL),
("get_lendingtype", client.LTYPE_URL),
Expand All @@ -170,7 +172,7 @@ def test_id_only_query(mock_client, url, id_, skip_cache, expected_url):
],
)
def test_id_only_functions(mock_client, function, id_, skip_cache, expected_url):
mock_client.fetcher.fetch = mock.Mock(return_value=[["foo"]])
mock_client.fetcher.fetch = mock.Mock(return_value=["foo"])
got = getattr(mock_client, function)(id_, skip_cache=skip_cache)
assert list(got) == ["foo"]
mock_client.fetcher.fetch.assert_called_once_with(
Expand Down Expand Up @@ -211,16 +213,21 @@ def test_id_only_functions(mock_client, function, id_, skip_cache, expected_url)
def test_get_country(
mock_client, country_id, incomelevel, lendingtype, path, args, skip_cache
):
mock_client.fetcher.fetch = mock.Mock(return_value=[["foo"]])
got = mock_client.get_country(country_id, incomelevel, lendingtype, skip_cache)
mock_client.fetcher.fetch = mock.Mock(return_value=["foo"])
got = mock_client.get_country(
country_id=country_id,
incomelevel=incomelevel,
lendingtype=lendingtype,
skip_cache=skip_cache,
)
assert list(got) == ["foo"]
if country_id:
mock_client.fetcher.fetch.assert_called_once_with(
url=f"{client.COUNTRIES_URL}{path}", skip_cache=skip_cache
)
else:
mock_client.fetcher.fetch.assert_called_once_with(
url=f"{client.COUNTRIES_URL}{path}", args=args, skip_cache=skip_cache
url=f"{client.COUNTRIES_URL}{path}", params=args, skip_cache=skip_cache
)


Expand All @@ -241,8 +248,8 @@ def test_get_country_bad(mock_client, kwargs):
(
("foo", None, None, True, f"{client.INDICATOR_URL}/foo"),
(["foo", "bar"], None, None, False, f"{client.INDICATOR_URL}/foo;bar"),
(None, "foo", None, False, f"{client.SOURCES_URL}/foo/indicators"),
(None, ["foo", "bar"], None, True, f"{client.SOURCES_URL}/foo;bar/indicators"),
(None, "foo", None, False, f"{client.SOURCE_URL}/foo/indicators"),
(None, ["foo", "bar"], None, True, f"{client.SOURCE_URL}/foo;bar/indicators"),
(None, None, "foo", False, f"{client.TOPIC_URL}/foo/indicators"),
(None, None, ["foo", "bar"], True, f"{client.TOPIC_URL}/foo;bar/indicators"),
(None, None, None, True, client.INDICATOR_URL),
Expand Down Expand Up @@ -272,8 +279,8 @@ def test_get_indicator(mock_client, indicator, source, topic, skip_cache, expect
(None, "foo", "bar"),
),
)
def test_get_indicator_bad(mock_client, indicator, source, topic):
with pytest.raises(ValueError, match=client.INDIC_ERROR):
def test_get_indicator_indicator_and_facet(mock_client, indicator, source, topic):
with pytest.raises(ValueError, match="Cannot specify"):
mock_client.get_indicator(indicator=indicator, source=source, topic=topic)


Expand All @@ -293,10 +300,9 @@ def test_get_indicator_bad(mock_client, indicator, source, topic):
),
)
def test_search_countries(mock_client, raw, query, expected):
with mock.patch.object(mock_client, "get_country") as mock_get_countries:
mock_get_countries.return_value = raw
got = mock_client.search_countries(query)
assert list(got) == expected
mock_client.fetcher.fetch = mock.Mock(return_value=raw)
got = mock_client.get_country(query=query)
assert list(got) == expected


@pytest.mark.parametrize(
Expand All @@ -315,10 +321,9 @@ def test_search_countries(mock_client, raw, query, expected):
),
)
def test_search_indicators(mock_client, raw, query, expected):
with mock.patch.object(mock_client, "get_indicator") as mock_get_indicators:
mock_get_indicators.return_value = raw
got = mock_client.search_indicators(query)
assert list(got) == expected
mock_client.fetcher.fetch = mock.Mock(return_value=raw)
got = mock_client.get_indicator(query=query)
assert list(got) == expected


def test_get_series_passthrough(mock_client):
Expand Down
6 changes: 1 addition & 5 deletions wbdata/__init__.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
"""
wbdata: A wrapper for the World Bank API
"""
__version__ = "0.3.0.post"

import functools
__version__ = "1.0.0.dev"

from .client import Client


@functools.lru_cache
def get_default_client() -> Client:
"""
Get the default client
Expand All @@ -25,5 +23,3 @@ def get_default_client() -> Client:
get_series = get_default_client().get_series
get_source = get_default_client().get_source
get_topic = get_default_client().get_topic
search_countries = get_default_client().search_countries
search_indicators = get_default_client().search_indicators
34 changes: 29 additions & 5 deletions wbdata/cache.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
"""
Caching functionality
"""
import datetime as dt
import logging
import os
Expand All @@ -12,6 +16,13 @@

log = logging.getLogger(__name__)

CACHE_PATH = os.getenv(
"WBDATA_CACHE_PATH",
os.path.join(
appdirs.user_cache_dir(appname="wbdata", version=__version__), "cache"
),
)

try:
TTL_DAYS = int(os.getenv("WBDATA_CACHE_TTL_DAYS", "7"))
except ValueError:
Expand All @@ -22,7 +33,7 @@
MAX_SIZE = int(os.getenv("WBDATA_CACHE_MAX_SIZE", "100"))
except ValueError:
logging.warning("Couldn't parse WBDATA_CACHE_MAX_SIZE value, defaulting to 100")
MAX_SIZE = 7
MAX_SIZE = 100


def get_cache(
Expand All @@ -31,11 +42,24 @@ def get_cache(
max_size: Union[int, None] = None,
) -> cachetools.Cache:
"""
Get the global cache
Create a persistent cache.
Default caching functionality can be controlled with environment variables:
`WBDATA_CACHE_PATH`: path for the cache (default: system default
application cache)
`WBDATA_CACHE_TTL_DAYS`: number of days to cache results (default: 7)
`WBDATA_CACHE_MAX_SIZE`: maximum number of items to cache (default: 100)
Parameters:
path: path to the cache. If `None`, value of `WBDATA_CACHE_PATH`
ttl_days: number of days to cache results. If `None`, value of
`WBDATA_CACHE_TTL_DAYS`
max_size: maximum number of items to cache. If `None`, value of
`WBDATA_CACHE_MAX_SIZE`.
"""
path = path or Path(
appdirs.user_cache_dir(appname="wbdata", version=__version__)
).joinpath("cache")
path = path or CACHE_PATH
Path(path).parent.mkdir(parents=True, exist_ok=True)
ttl_days = ttl_days or TTL_DAYS
max_size = max_size or MAX_SIZE
Expand Down
Loading

0 comments on commit 2d84fdf

Please sign in to comment.