You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
149 lines
3.9 KiB
Python
149 lines
3.9 KiB
Python
from datetime import timedelta, date
|
|
|
|
import pygal
|
|
|
|
from .models import User
|
|
from payments.models import BACKENDS
|
|
from payments.models import Payment
|
|
|
|
|
|
PERIOD_VERBOSE_NAME = {
|
|
"y": "per month",
|
|
"m": "per day",
|
|
}
|
|
|
|
|
|
def monthdelta(date, delta):
|
|
m = (date.month + delta) % 12
|
|
y = date.year + (date.month + delta - 1) // 12
|
|
if not m:
|
|
m = 12
|
|
d = min(
|
|
date.day,
|
|
[
|
|
31,
|
|
29 if y % 4 == 0 and not y % 400 == 0 else 28,
|
|
31,
|
|
30,
|
|
31,
|
|
30,
|
|
31,
|
|
31,
|
|
30,
|
|
31,
|
|
30,
|
|
31,
|
|
][m - 1],
|
|
)
|
|
return date.replace(day=d, month=m, year=y)
|
|
|
|
|
|
def last_days(n=30):
|
|
now = date.today()
|
|
for i in range(n - 1, -1, -1):
|
|
yield now - timedelta(days=i)
|
|
|
|
|
|
def last_months(n=12):
|
|
now = date.today().replace(day=1)
|
|
for i in range(n - 1, -1, -1):
|
|
yield monthdelta(now, -i)
|
|
|
|
|
|
def time_filter_future(period, m, df):
|
|
def _filter(o):
|
|
if period == "m":
|
|
return df(o).date() <= m
|
|
if period == "y":
|
|
return df(o).date().replace(day=1) <= m
|
|
|
|
return _filter
|
|
|
|
|
|
def time_filter_between(period, m, df):
|
|
def _filter(o):
|
|
if period == "m":
|
|
return (
|
|
df(o).year == m.year and df(o).month == m.month and df(o).day == m.day
|
|
)
|
|
return df(o).date() <= m and df(o).date() > (m - timedelta(days=1))
|
|
if period == "y":
|
|
return df(o).year == m.year and df(o).month == m.month
|
|
return df(o).date().replace(day=1) <= m and df(o).date().replace(day=1) > (
|
|
m - timedelta(days=30)
|
|
)
|
|
|
|
return _filter
|
|
|
|
|
|
def users_graph(period):
|
|
chart = pygal.Line(fill=True, x_label_rotation=75, show_legend=False)
|
|
chart.title = "Users %s" % PERIOD_VERBOSE_NAME[period]
|
|
chart.x_labels = []
|
|
values = []
|
|
gen = last_days(30) if period == "m" else last_months(12)
|
|
users = User.objects.all()
|
|
|
|
for m in gen:
|
|
filter_ = time_filter_future(period, m, lambda o: o.date_joined)
|
|
users_filtered = filter(filter_, users)
|
|
values.append(len(list(users_filtered)))
|
|
chart.x_labels.append("%02d/%02d" % (m.month, m.day))
|
|
|
|
chart.add("Users", values)
|
|
return chart.render()
|
|
|
|
|
|
def payments_paid_graph(period):
|
|
chart = pygal.StackedBar(x_label_rotation=75, show_legend=True)
|
|
chart.x_labels = []
|
|
gen = list(last_days(30) if period == "m" else last_months(12))
|
|
|
|
chart.title = "Payments %s in €" % (PERIOD_VERBOSE_NAME[period])
|
|
|
|
for m in gen:
|
|
chart.x_labels.append("%02d/%02d" % (m.month, m.day))
|
|
|
|
values = dict()
|
|
for backend_id, backend in BACKENDS.items():
|
|
values = []
|
|
payments = list(
|
|
Payment.objects.filter(status="confirmed", backend_id=backend_id)
|
|
)
|
|
|
|
for m in gen:
|
|
filter_ = time_filter_between(period, m, lambda o: o.created)
|
|
filtered = filter(filter_, payments)
|
|
values.append(sum(u.paid_amount for u in filtered) / 100)
|
|
|
|
chart.add(backend_id, values)
|
|
|
|
return chart.render()
|
|
|
|
|
|
def payments_success_graph(period):
|
|
chart = pygal.StackedBar(x_label_rotation=75, show_legend=True)
|
|
chart.x_labels = []
|
|
gen = list(last_days(30) if period == "m" else last_months(12))
|
|
|
|
chart.title = "Successful payments %s" % (PERIOD_VERBOSE_NAME[period])
|
|
|
|
for m in gen:
|
|
chart.x_labels.append("%02d/%02d" % (m.month, m.day))
|
|
|
|
values = dict()
|
|
for backend_id, backend in BACKENDS.items():
|
|
values = []
|
|
payments = list(
|
|
Payment.objects.filter(status="confirmed", backend_id=backend_id)
|
|
)
|
|
|
|
for m in gen:
|
|
filter_ = time_filter_between(period, m, lambda o: o.created)
|
|
filtered = filter(filter_, payments)
|
|
values.append(sum(1 for u in filtered))
|
|
|
|
chart.add(backend_id, values)
|
|
|
|
return chart.render()
|