Skip to content

Commit 93bc4c7

Browse files
author
Andrew Brookins
committed
Finesse the api
1 parent 777fecb commit 93bc4c7

File tree

2 files changed

+49
-19
lines changed

2 files changed

+49
-19
lines changed

app/main.py

Lines changed: 45 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import aioredis
1111
import requests
1212
from aioredis.exceptions import ResponseError
13+
from fastapi import BackgroundTasks
1314
from fastapi import Depends
1415
from fastapi import FastAPI
1516
from pydantic import BaseSettings
@@ -18,6 +19,7 @@
1819
DEFAULT_KEY_PREFIX = 'is-bitcoin-lit'
1920
SENTIMENT_API_URL = 'HTTps://api.senticrypt.com/v1/history/bitcoin-{time}.json'
2021
TIME_FORMAT_STRING = '%Y-%m-%d_%H'
22+
TWO_MINUTES = 60 * 60
2123

2224

2325
def prefixed_key(f):
@@ -65,12 +67,17 @@ class Config(BaseSettings):
6567

6668
def make_summary(data):
6769
"""Take a series of averages and summarize them as means of means."""
68-
return {
70+
summary = {
6971
'time': datetime.now().timestamp(),
7072
'mean_of_means_sentiment': sum(d['mean'] for d in data) / len(data),
7173
'mean_of_means_price': sum(float(d['btc_price']) for d in data) / len(data),
7274
}
7375

76+
summary['lit'] = '1' if float(
77+
summary['mean_of_means_sentiment'],
78+
) > 0 else '0'
79+
return summary
80+
7481

7582
async def add_many_to_timeseries(
7683
key_pairs: Iterable[Tuple[str, str]],
@@ -87,7 +94,7 @@ async def add_many_to_timeseries(
8794
for datapoint in data:
8895
for key, attr in key_pairs:
8996
partial = functools.partial(
90-
partial, key, datapoint['timestamp'], datapoint[attr],
97+
partial, key, int(datapoint['timestamp']), datapoint[attr],
9198
)
9299
return await partial()
93100

@@ -96,37 +103,57 @@ def make_keys():
96103
return Keys()
97104

98105

106+
async def persist(keys, data, summary):
107+
# TODO: Only add timeseries data that we don't already have -- how?
108+
ts_price_key = keys.timeseries_price_key()
109+
ts_sentiment_key = keys.timeseries_sentiment_key()
110+
summary_key = keys.summary_key()
111+
112+
await redis.hset(summary_key, mapping=summary)
113+
await redis.expire(summary_key, TWO_MINUTES)
114+
await add_many_to_timeseries(
115+
(
116+
(ts_price_key, 'btc_price'),
117+
(ts_sentiment_key, 'mean'),
118+
), data,
119+
)
120+
121+
99122
@app.get('/is-bitcoin-lit')
100-
async def bitcoin(keys: Keys = Depends(make_keys)):
123+
async def bitcoin(background_tasks: BackgroundTasks, keys: Keys = Depends(make_keys)):
101124
sentiment_time = datetime.now().strftime(TIME_FORMAT_STRING)
102125
summary_key = keys.summary_key()
103-
ts_price_key = keys.timeseries_price_key()
104-
ts_sentiment_key = keys.timeseries_sentiment_key()
105126
url = SENTIMENT_API_URL.format(time=sentiment_time)
106127

107128
summary = await redis.hgetall(summary_key)
108129

109-
if not summary:
110-
# TODO: Only add timeseries data that we don't already have -- how?
130+
if summary:
131+
summary['lit'] = True if summary['lit'] == '1' else False
132+
else:
111133
data = requests.get(url).json()
112134
summary = make_summary(data)
113-
await redis.hset(summary_key, mapping=summary)
114-
await redis.expire(summary_key, 60)
115-
await add_many_to_timeseries(
116-
(
117-
(ts_price_key, 'btc_price'),
118-
(ts_sentiment_key, 'mean'),
119-
), data,
120-
)
135+
background_tasks.add_task(persist, keys, data, summary)
121136

122137
return summary
123138

124139

125140
@app.on_event('startup')
126-
async def startup_event(keys: Keys = Depends(make_keys)):
141+
async def startup_event():
142+
keys = Keys()
143+
# When we create our timeseries, we'll use the "first" duplicate policy,
144+
# which ignores duplicate pairs of timestamp and values if we add them.
145+
#
146+
# Because of this, we don't worry about handling this logic ourselves --
147+
# but note that there is a performance cost to writes using this policy.
127148
try:
128-
redis.execute_command('TS.CREATE', keys.timeseries_sentiment_key())
129-
redis.execute_command('TS.CREATE', keys.timeseries_price_key())
149+
await redis.execute_command(
150+
'TS.CREATE', keys.timeseries_sentiment_key(),
151+
'DUPLICATE_POLICY', 'first',
152+
)
153+
await redis.execute_command(
154+
'TS.CREATE', keys.timeseries_price_key(),
155+
'DUPLICATE_POLICY', 'first',
156+
)
130157
except ResponseError:
131158
# Time series already exists
132159
pass

tests/test_bitcoin_api.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@
55
from app.main import Keys
66

77
URL = '/is-bitcoin-lit'
8-
EXPECTED_FIELDS = ('time', 'mean_of_means_sentiment', 'mean_of_means_price')
8+
EXPECTED_FIELDS = (
9+
'lit', 'time', 'mean_of_means_sentiment',
10+
'mean_of_means_price',
11+
)
912

1013

1114
@pytest.mark.asyncio

0 commit comments

Comments
 (0)