Added finances endpoint

This commit is contained in:
nsde 2023-09-22 00:30:07 +02:00
parent 453c6e7430
commit 1384b3add8
5 changed files with 99 additions and 11 deletions

1
api/cache/crypto_prices.json vendored Normal file
View file

@ -0,0 +1 @@
{"LTC": 64.665, "_last_updated": 1695334741.4905503, "BTC": 26583.485, "MATIC": 0.52075, "XMR": 146.46058828041404, "ADA": 0.2455, "USDT": 1.000005, "ETH": 1586.115, "USD": 1.0, "EUR": 1.0662838016640013}

View file

@ -8,9 +8,12 @@ sys.path.append(project_root)
# the code above is to allow importing from the root folder
import time
import json
import hmac
import httpx
import fastapi
import functools
from dhooks import Webhook, Embed
from dotenv import load_dotenv
@ -18,7 +21,7 @@ from dotenv import load_dotenv
import checks.client
from helpers import errors
from db.users import UserManager
from db import users, finances
load_dotenv()
router = fastapi.APIRouter(tags=['core'])
@ -62,9 +65,7 @@ async def get_users(discord_id: int, incoming_request: fastapi.Request):
auth = await check_core_auth(incoming_request)
if auth: return auth
# Get user by discord ID
manager = UserManager()
user = await manager.user_by_discord_id(discord_id)
user = await users.manager.user_by_discord_id(discord_id)
if not user:
return await errors.error(404, 'Discord user not found in the API database.', 'Check the `discord_id` parameter.')
@ -88,9 +89,7 @@ async def create_user(incoming_request: fastapi.Request):
except (json.decoder.JSONDecodeError, AttributeError):
return await errors.error(400, 'Invalid or no payload received.', 'The payload must be a JSON object with a `discord_id` key.')
# Create the user
manager = UserManager()
user = await manager.create(discord_id)
user = await users.manager.create(discord_id)
await new_user_webhook(user)
user['_id'] = str(user['_id'])
@ -114,9 +113,7 @@ async def update_user(incoming_request: fastapi.Request):
'The payload must be a JSON object with a `discord_id` key and an `updates` key.'
)
# Update the user
manager = UserManager()
user = await manager.update_by_discord_id(discord_id, updates)
user = await users.manager.update_by_discord_id(discord_id, updates)
return user
@ -147,3 +144,54 @@ async def run_checks(incoming_request: fastapi.Request):
results[func.__name__] = result
return results
async def get_crypto_price(cryptocurrency: str) -> float:
"""Gets the price of a cryptocurrency using coinbase's API."""
if os.path.exists('cache/crypto_prices.json'):
with open('cache/crypto_prices.json', 'r') as f:
cache = json.load(f)
else:
cache = {}
is_old = time.time() - cache.get('_last_updated', 0) > 60 * 60
if is_old or cryptocurrency not in cache:
async with httpx.AsyncClient() as client:
response = await client.get(f'https://api.coinbase.com/v2/prices/{cryptocurrency}-USD/spot')
usd_price = float(response.json()['data']['amount'])
cache[cryptocurrency] = usd_price
cache['_last_updated'] = time.time()
with open('cache/crypto_prices.json', 'w') as f:
json.dump(cache, f)
return cache[cryptocurrency]
@router.get('/finances')
async def get_finances(incoming_request: fastapi.Request):
"""Return financial information. Requires a core API key."""
auth_error = await check_core_auth(incoming_request)
if auth_error: return auth_error
transactions = await finances.manager.get_entire_financial_history()
for table in transactions:
for transaction in transactions[table]:
currency = transaction['currency']
if '-' in currency:
currency = currency.split('-')[0]
amount = transaction['amount']
if currency == 'mBTC':
currency = 'BTC'
amount = transaction['amount'] / 1000
amount_in_usd = await get_crypto_price(currency) * amount
transactions[table][transactions[table].index(transaction)]['amount_usd'] = amount_in_usd
return transactions

39
api/db/finances.py Normal file
View file

@ -0,0 +1,39 @@
import os
import asyncio
from dotenv import load_dotenv
from motor.motor_asyncio import AsyncIOMotorClient
load_dotenv()
class FinanceManager:
def __init__(self):
self.conn = AsyncIOMotorClient(os.environ['MONGO_URI'])
async def _get_collection(self, collection_name: str):
return self.conn['finances'][collection_name]
async def get_entire_financial_history(self):
donations_db = await self._get_collection('donations')
expenses_db = await self._get_collection('expenses')
# turn both into JSON-like lists of dicts at once (make sure to fix the _id)
history = {'donations': [], 'expenses': []}
async for donation in donations_db.find():
donation['_id'] = str(donation['_id'])
history['donations'].append(donation)
async for expense in expenses_db.find():
expense['_id'] = str(expense['_id'])
history['expenses'].append(expense)
# sort all by timestamp
history['donations'] = sorted(history['donations'], key=lambda x: x['timestamp'])
return history
manager = FinanceManager()
if __name__ == '__main__':
print(asyncio.run(manager.get_entire_financial_history()))

View file

@ -19,7 +19,7 @@ from helpers import tokens, errors, network
load_dotenv()
users = UserManager()
models_list = json.load(open('models.json', encoding='utf8'))
models_list = json.load(open('cache/models.json', encoding='utf8'))
models = [model['id'] for model in models_list['data']]
with open('config/config.yml', encoding='utf8') as f: