mirror of
https://github.com/NovaOSS/nova-web.git
synced 2024-11-25 16:13:58 +01:00
/finances improvements
This commit is contained in:
parent
1c48174d47
commit
9ebba26b0b
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
|
@ -9,6 +9,7 @@
|
||||||
"**/__pycache__": true,
|
"**/__pycache__": true,
|
||||||
"**/.vscode": true,
|
"**/.vscode": true,
|
||||||
"**/*.map": true,
|
"**/*.map": true,
|
||||||
|
"**/*.css.map": true,
|
||||||
"static/css/base.css": true,
|
"static/css/base.css": true,
|
||||||
"static/css/home.css": true,
|
"static/css/home.css": true,
|
||||||
"static/css/navbar.css": true,
|
"static/css/navbar.css": true,
|
||||||
|
|
|
@ -17,7 +17,6 @@ REDIRECT_URI = 'https://nova-oss.com/callback/discord'
|
||||||
# REDIRECT_URI = 'http://localhost:2211/callback/discord'
|
# REDIRECT_URI = 'http://localhost:2211/callback/discord'
|
||||||
SCOPES = ['identify']
|
SCOPES = ['identify']
|
||||||
|
|
||||||
@functools.lru_cache(maxsize=128)
|
|
||||||
def get_user(discord_id: int) -> dict:
|
def get_user(discord_id: int) -> dict:
|
||||||
res = requests.get(
|
res = requests.get(
|
||||||
url=f'http://localhost:2333/users?discord_id={discord_id}',
|
url=f'http://localhost:2333/users?discord_id={discord_id}',
|
||||||
|
|
|
@ -4,6 +4,7 @@ import time
|
||||||
import json
|
import json
|
||||||
import flask
|
import flask
|
||||||
import requests
|
import requests
|
||||||
|
import pandas as pd
|
||||||
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
|
|
||||||
|
@ -26,29 +27,70 @@ def register(app):
|
||||||
try:
|
try:
|
||||||
data = get_finances()
|
data = get_finances()
|
||||||
except Exception:
|
except Exception:
|
||||||
return flask.Response('Error fetching finances from database. This might be cause the data is still being processed. Sorry.', status=500)
|
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']
|
||||||
|
|
||||||
finances = {
|
finances = {
|
||||||
'last_update': data['timestamp'],
|
'last_update': data['timestamp'],
|
||||||
'donations_total': round(sum([i['amount_usd'] for i in data['donations']])),
|
'donations_total': round(sum([i['amount_usd'] for i in donations])),
|
||||||
'donations_num': len(data['donations']),
|
'donations_num': len(donations),
|
||||||
'donations_avg': round(sum([i['amount_usd'] for i in data['donations']]) / len(data['donations']), 2),
|
'donations_avg': round(sum([i['amount_usd'] for i in donations]) / len(donations), 2),
|
||||||
'donations_max': round(max([i['amount_usd'] for i in data['donations']])),
|
'donations_max': round(max([i['amount_usd'] for i in donations])),
|
||||||
'most_used_donation_currency': max(data['donations'], key=lambda x: x['amount_usd'])['currency'],
|
'most_used_donation_currency': max(donations, key=lambda x: x['amount_usd'])['currency'],
|
||||||
|
|
||||||
'expenses_total': round(sum([i['amount_usd'] for i in data['expenses']])),
|
'expenses_total': round(sum([i['amount_usd'] for i in expenses])),
|
||||||
'expenses_wages': round(sum([i['amount_usd'] for i in data['expenses'] if i['type'] == 'wage'])),
|
'expenses_wages': round(sum([i['amount_usd'] for i in expenses if i['type'] == 'wage'])),
|
||||||
'most_used_expense_currency': max(data['expenses'], key=lambda x: x['amount_usd'])['currency'],
|
'most_used_expense_currency': max(expenses, key=lambda x: x['amount_usd'])['currency'],
|
||||||
}
|
}
|
||||||
|
|
||||||
return flask.render_template('finances.html', finances=finances)
|
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')
|
@app.route('/finances/json')
|
||||||
def finances_download():
|
def finances_download():
|
||||||
try:
|
try:
|
||||||
data = get_finances()
|
data = get_finances()
|
||||||
except Exception:
|
except Exception:
|
||||||
return flask.Response('Error fetching finances from database. This might be cause the data is still being processed. Sorry.', status=500)
|
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()
|
virtual_file = io.StringIO()
|
||||||
json.dump(data, virtual_file, indent=4)
|
json.dump(data, virtual_file, indent=4)
|
||||||
|
|
|
@ -24,23 +24,47 @@
|
||||||
|
|
||||||
<div class="featured__facts box"><p>
|
<div class="featured__facts box"><p>
|
||||||
We have received <b>{{ finances.donations_num }} donations</b> so far,
|
We have received <b>{{ finances.donations_num }} donations</b> so far,
|
||||||
totaling <b>{{ finances.donations_total }} USD</b>.
|
totaling <b>${{ finances.donations_total }}</b>. The largest donation is <b>${{ finances.donations_max }}</b>.
|
||||||
</p></div>
|
</p></div>
|
||||||
<div class="featured__facts box"><p>
|
<div class="featured__facts box"><p>
|
||||||
The average donation has an amount of <b>{{ finances.donations_avg }} USD</b>.
|
The average donation has an amount of <b>${{ finances.donations_avg }}</b>.
|
||||||
Most people used <b>{{ finances.most_used_donation_currency }}</b> to donate.
|
Most people used <b>{{ finances.most_used_donation_currency }}</b> to donate.
|
||||||
</p></div>
|
</p></div>
|
||||||
<div class="featured__facts box"><p>
|
|
||||||
The largest donation is <b>{{ finances.donations_max }} USD</b>.
|
|
||||||
</p></div>
|
|
||||||
|
|
||||||
<!-- <img src="{{ finances.donations_chart_url }} alt="Donations chart"> -->
|
|
||||||
|
|
||||||
<h2>Expenses</h2>
|
<h2>Expenses</h2>
|
||||||
<div class="featured__facts box"><p>We have spent <b>{{ finances.expenses_total }} USD</b>.</p></div>
|
<div class="featured__facts box"><p>We have spent <b>${{ finances.expenses_total }}</b> so far, <b>${{ finances.expenses_wages }}</b> of which are wages to our employees.</p></div>
|
||||||
<div class="featured__facts box"><p>We paid our developers <b>{{ finances.expenses_wages }} USD</b> so far.</p></div>
|
|
||||||
<div class="featured__facts box"><p>We usually pay in <b>{{ finances.most_used_expense_currency }}</b>.</p></div>
|
|
||||||
|
|
||||||
|
<h2>Balance</h2>
|
||||||
|
<div class="featured__facts box"><p>Our current balance is ~<b>${{ finances.donations_total - finances.expenses_total }}</b>.
|
||||||
|
Please note that this is just a rough estimate and not an exact value. Due to transaction fees and price fluctuations,
|
||||||
|
the actual balance is actually lower.
|
||||||
|
</p></div>
|
||||||
|
|
||||||
|
<h2>Transactions</h2>
|
||||||
|
{{ transaction_table | safe }}
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||||
|
<link rel="stylesheet" href="//cdn.datatables.net/1.13.6/css/jquery.dataTables.min.css">
|
||||||
|
<script src="//cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>
|
||||||
|
<script>
|
||||||
|
$(document).ready( function () {
|
||||||
|
$('#transactionTable').DataTable({
|
||||||
|
'order': [[ 4, 'desc' ]]
|
||||||
|
});
|
||||||
|
} );
|
||||||
|
|
||||||
|
// add a column that links to https://3xpl.com/search?q= and then the "Proof" column value of each row
|
||||||
|
const table = document.getElementById('transactionTable');
|
||||||
|
for (let i = 1; i < table.rows.length; i++) {
|
||||||
|
const row = table.rows[i];
|
||||||
|
const proof = row.cells[5].innerText;
|
||||||
|
|
||||||
|
if (proof == '') continue;
|
||||||
|
|
||||||
|
row.cells[5].innerHTML = `<a href="https://3xpl.com/search?q=${proof}" target="_blank"><i class="bi bi-box-arrow-up-right"></i></a>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
{% include 'common/end.html' %}
|
{% include 'common/end.html' %}
|
|
@ -7,12 +7,12 @@
|
||||||
<a href="https://discord.nova-oss.com" target="_blank">
|
<a href="https://discord.nova-oss.com" target="_blank">
|
||||||
<button class="special">
|
<button class="special">
|
||||||
<i class="bi bi-discord"></i>
|
<i class="bi bi-discord"></i>
|
||||||
Join 3,800+ members
|
Join 5,000+ members
|
||||||
</button>
|
</button>
|
||||||
</a>
|
</a>
|
||||||
<!-- <a href="/login">
|
<!-- <a href="https://chat.nova-oss.com" target="_blank">
|
||||||
<button class="secondary">
|
<button class="secondary">
|
||||||
Log in <mark>beta</mark>
|
Donate
|
||||||
</button>
|
</button>
|
||||||
</a> -->
|
</a> -->
|
||||||
<br>
|
<br>
|
||||||
|
|
Loading…
Reference in a new issue