nova-web/web/finances.py
NovaOSS Admins a1543f9a4f Fixes
2023-10-15 22:51:43 +00:00

130 lines
4.3 KiB
Python
Executable file

import os
import io
import time
import json
import flask
import requests
import pandas as pd
from dotenv import load_dotenv
load_dotenv()
# cache get_finances() for 5 minutes
def get_finances():
res = requests.get(
url=f'{os.environ["CORE_API_URL"]}/finances',
headers={
'Authorization': f'{os.environ["CORE_API_KEY"]}'
},
timeout=5
)
res.raise_for_status()
return res.json()
def register(app):
@app.route('/finances')
def finances_view():
try:
data = get_finances()
except Exception as exc:
print(exc)
return flask.Response('Error fetching finances from database. This might be cause the data is still being processed. Please allow up to a few minutes.', status=500)
donations = data['donations']
expenses = data['expenses']
try:
avg = round(sum([i['amount_usd'] for i in donations]) / len(donations), 2)
except ZeroDivisionError:
avg = 0
try:
most_donation = max(donations, key=lambda x: x['amount_usd'])['currency']
except ValueError:
most_donation = '?'
try:
most_expense = max(expenses, key=lambda x: x['amount_usd'])['currency']
except ValueError:
most_expense = '?'
try:
donations_max = round(max([i['amount_usd'] for i in donations]))
except ValueError:
donations_max = 0
finances = {
'last_update': data['timestamp'],
'donations_total': round(sum([i['amount_usd'] for i in donations])),
'donations_num': len(donations),
'donations_avg': avg,
'donations_max': donations_max,
'most_used_donation_currency': most_donation,
'expenses_total': round(sum([i['amount_usd'] for i in expenses])),
'expenses_wages': round(sum([i['amount_usd'] for i in expenses if i['type'] == 'wage'])),
'most_used_expense_currency': most_expense,
}
transaction_table = []
transactions = [{'change': '+', **donation} for donation in donations] + [{'change': '-', **expense} for expense in expenses]
transactions = sorted(transactions, key=lambda x: x['timestamp'], reverse=True)
for transaction in transactions:
description = ''
if transaction['change'] == '+':
description = f'Discord/{transaction["user"]}'
if transaction['change'] == '-':
description = f'{transaction["reason"]} -> {transaction["to"]}'
if transaction['currency'] in ['USD', 'EUR', 'GBP', 'CAD', 'AUD', 'NZD', 'CHF', 'JPY', 'CNY', 'HKD', 'SGD']:
transaction['proof'] = ''
if transaction['change'] == '-':
transaction['amount_usd'] = -transaction['amount_usd']
amount = f'{round(transaction["amount_usd"])}'
transaction_table.append({
'Amount (USD)': amount,
'Currency': transaction['currency'],
'Type': transaction.get('type', 'donation'),
'Description': description,
'Timestamp': time.strftime('%Y-%m-%d %H:%M', time.localtime(transaction['timestamp'])),
'Proof': transaction['proof'],
})
# convert transaction_table to html
transaction_table = pd.DataFrame(transaction_table).to_html(
index=False,
classes=['table', 'table-striped', 'table-hover'],
table_id='transactionTable'
)
return flask.render_template('finances.html', finances=finances, transaction_table=transaction_table)
@app.route('/finances/json')
def finances_download():
try:
data = get_finances()
except Exception:
return flask.Response('Error fetching finances from database. This might be cause the data is still being processed. Please allow up to a few minutes.', status=500)
virtual_file = io.StringIO()
json.dump(data, virtual_file, indent=4)
name = f'finances-{time.strftime("%Y-%m-%d")}.json'
virtual_file.seek(0)
return flask.send_file(
io.BytesIO(virtual_file.read().encode('utf-8')),
mimetype='application/json',
as_attachment=True,
download_name=name
)