Introduce V2 of current and historical functions
This commit is contained in:
0
wow_token/path_handler/__init__.py
Normal file
0
wow_token/path_handler/__init__.py
Normal file
50
wow_token/path_handler/math_path_handler.py
Normal file
50
wow_token/path_handler/math_path_handler.py
Normal file
@@ -0,0 +1,50 @@
|
||||
import datetime
|
||||
from typing import List, Tuple
|
||||
|
||||
from wow_token.db.cached_range import CachedRange
|
||||
from wow_token.db.compacted import Compacted
|
||||
from wow_token.db.recent import Recent
|
||||
from wow_token.db.trinity import Trinity
|
||||
from wow_token.flavor import Flavor
|
||||
from wow_token.path_handler.relative_error import InvalidRelativePathError
|
||||
from wow_token.path_handler.relative_path_handler import RelativePathHandler
|
||||
from wow_token.region import Region
|
||||
|
||||
|
||||
class MathPathHandler:
|
||||
_cdb : Compacted
|
||||
_rdb : Recent
|
||||
def __init__(self, cdb: Compacted, rdb: Recent):
|
||||
self._cdb = cdb
|
||||
self._rdb = rdb
|
||||
|
||||
def path_handler(self, uri: str) -> List[Tuple[str, int]]:
|
||||
# This URI takes the form of /v2/math/{math_function}/{flavor}/{region}/{range}
|
||||
split_uri = uri.split('/')
|
||||
math_function = split_uri[-4]
|
||||
data = RelativePathHandler(self._cdb, self._rdb).path_handler(uri)
|
||||
|
||||
match math_function:
|
||||
case 'avg':
|
||||
return self._avg(data)
|
||||
case _:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
def _avg(self, data: List[Tuple[str, int]]) -> List[Tuple[str, int]]:
|
||||
avg_buckets = []
|
||||
bucket_timestamp = None
|
||||
bucket_price = 0
|
||||
bucket_count = 0
|
||||
for timestamp, price in data:
|
||||
if bucket_timestamp is None:
|
||||
bucket_timestamp = datetime.datetime.fromisoformat(timestamp)
|
||||
elif bucket_timestamp.date() != datetime.datetime.fromisoformat(timestamp).date():
|
||||
bucket_head = datetime.datetime(year=bucket_timestamp.year, month=bucket_timestamp.month, day=bucket_timestamp.day)
|
||||
avg_buckets.append((bucket_head.isoformat(), int(bucket_price/bucket_count)))
|
||||
bucket_price = 0
|
||||
bucket_count = 0
|
||||
bucket_timestamp = datetime.datetime.fromisoformat(timestamp)
|
||||
bucket_price += price
|
||||
bucket_count += 1
|
||||
return avg_buckets
|
||||
2
wow_token/path_handler/relative_error.py
Normal file
2
wow_token/path_handler/relative_error.py
Normal file
@@ -0,0 +1,2 @@
|
||||
class InvalidRelativePathError(Exception):
|
||||
pass
|
||||
98
wow_token/path_handler/relative_path_handler.py
Normal file
98
wow_token/path_handler/relative_path_handler.py
Normal file
@@ -0,0 +1,98 @@
|
||||
import datetime
|
||||
from typing import List, Tuple
|
||||
|
||||
from wow_token.db.cached_range import CachedRange
|
||||
from wow_token.db.compacted import Compacted
|
||||
from wow_token.db.recent import Recent
|
||||
from wow_token.db.trinity import Trinity
|
||||
from wow_token.flavor import Flavor
|
||||
from wow_token.path_handler.relative_error import InvalidRelativePathError
|
||||
from wow_token.region import Region
|
||||
|
||||
|
||||
class RelativePathHandler:
|
||||
_cdb : Compacted
|
||||
_rdb : Recent
|
||||
def __init__(self, cdb: Compacted, rdb: Recent):
|
||||
self._cdb = cdb
|
||||
self._rdb = rdb
|
||||
|
||||
|
||||
def get_by_timedelta(self, flavor: Flavor, region: Region, timedelta: datetime.timedelta) -> List[Tuple[str, int]]:
|
||||
current_time = datetime.datetime.now(datetime.UTC)
|
||||
start_time = current_time - timedelta
|
||||
|
||||
if timedelta.days < 61:
|
||||
return self._rdb.get_after_unix_timestamp(flavor, region, int(start_time.timestamp()))
|
||||
elif timedelta.days <= 90:
|
||||
trinity = Trinity(region, flavor, CachedRange('90d'))
|
||||
elif timedelta.days <= 183:
|
||||
trinity = Trinity(region, flavor, CachedRange('6m'))
|
||||
elif timedelta.days <= 365:
|
||||
trinity = Trinity(region, flavor, CachedRange('1y'))
|
||||
elif timedelta.days <= 730:
|
||||
trinity = Trinity(region, flavor, CachedRange('2y'))
|
||||
else:
|
||||
trinity = Trinity(region, flavor, CachedRange('all'))
|
||||
|
||||
# If the data is exactly the size of the precomputed structure, go ahead and return it directly
|
||||
if timedelta.days == 90 or timedelta.days == 182 or timedelta.days == 365 or timedelta.days == 730:
|
||||
return self._cdb.get_precomputed_range(trinity, str)
|
||||
|
||||
final_data = []
|
||||
data = self._cdb.get_precomputed_range(trinity)
|
||||
for timestamp, price in data:
|
||||
if timestamp >= start_time:
|
||||
final_data.append((timestamp.isoformat(), price))
|
||||
return final_data
|
||||
|
||||
|
||||
def relative_time_handler(self, flavor: Flavor, region: Region, relative_range: str) -> List[Tuple[str, int]]:
|
||||
if relative_range == '30d':
|
||||
relative_range = '744h'
|
||||
|
||||
relative_unit = relative_range[-1]
|
||||
|
||||
match relative_unit:
|
||||
case 'h':
|
||||
hours = int(relative_range[:-1])
|
||||
if hours > 1488:
|
||||
raise InvalidRelativePathError
|
||||
start_time = datetime.datetime.now(datetime.UTC) - datetime.timedelta(hours=hours)
|
||||
return self._rdb.get_after_unix_timestamp(flavor, region, int(start_time.timestamp()))
|
||||
case 'd':
|
||||
days = int(relative_range[:-1])
|
||||
if days > 730:
|
||||
raise InvalidRelativePathError
|
||||
delta = datetime.timedelta(days=days)
|
||||
return self.get_by_timedelta(flavor, region, delta)
|
||||
case 'm':
|
||||
months = int(relative_range[:-1])
|
||||
if months > 48:
|
||||
raise InvalidRelativePathError
|
||||
delta = datetime.timedelta(days=int(30.437*months))
|
||||
return self.get_by_timedelta(flavor, region, delta)
|
||||
case 'y':
|
||||
years = int(relative_range[:-1])
|
||||
if years > 10:
|
||||
raise InvalidRelativePathError
|
||||
delta = datetime.timedelta(days=int(365.25*years))
|
||||
return self.get_by_timedelta(flavor, region, delta)
|
||||
case _:
|
||||
if relative_range == 'all':
|
||||
return self._cdb.get_precomputed_range(Trinity(region, flavor, CachedRange('all')), str)
|
||||
raise InvalidRelativePathError
|
||||
|
||||
|
||||
|
||||
def path_handler(self, uri) -> List[Tuple[str, int]]:
|
||||
# This URI takes the form of /v2/relative/{flavor}/{region}/{range}
|
||||
split_uri = uri.split('/')
|
||||
flavor = Flavor(split_uri[-3])
|
||||
region = Region(split_uri[-2])
|
||||
_range = split_uri[-1]
|
||||
if split_uri[-1].endswith('.json'):
|
||||
_range = split_uri[-1][:-5]
|
||||
|
||||
return self.relative_time_handler(flavor, region, _range)
|
||||
|
||||
Reference in New Issue
Block a user