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.
114 lines
3.7 KiB
Python
114 lines
3.7 KiB
Python
from datetime import timedelta
|
|
from io import StringIO
|
|
|
|
from django.conf import settings
|
|
from django.contrib.auth.models import User
|
|
from django.core.management.base import BaseCommand
|
|
from django.core.mail import EmailMessage
|
|
from django.db.models import Count
|
|
from django.utils import timezone
|
|
|
|
|
|
def get_prev_month(d):
|
|
if d.month == 1:
|
|
year = d.year - 1
|
|
month = 12
|
|
else:
|
|
year = d.year
|
|
month = d.month - 1
|
|
return d.replace(month=month, year=year)
|
|
|
|
|
|
def should_bill(report, user, time_limit):
|
|
""" Determines if one user has actually paid for the current month """
|
|
|
|
# Here for consistency, should be filtered in the query
|
|
if not user.vpnuser.expiration or user.vpnuser.expiration < time_limit:
|
|
return False
|
|
|
|
# Replay payments
|
|
payments = list(user.payment_set.order_by('id').filter(status='confirmed'))
|
|
paid_expiration = None
|
|
for p in payments:
|
|
d = p.confirmed_on or p.created
|
|
paid_expiration = max(paid_expiration or d, d) + p.time
|
|
|
|
# Numbre of days paid after the start of the month
|
|
# If negative and not filtered with vpnuser.expiration, user was given time.
|
|
# If positive, user has paid for this time.
|
|
delta = paid_expiration - time_limit
|
|
|
|
if delta < timedelta():
|
|
report.write("- %s (#%d): %s\n" % (user.username, user.id, -delta))
|
|
|
|
return delta > timedelta()
|
|
|
|
|
|
class Command(BaseCommand):
|
|
help = "Generate and send a monthly usage report to ADMINS"
|
|
|
|
def handle(self, *args, **options):
|
|
addresses = settings.USAGE_REPORT_DESTINATION
|
|
|
|
def format_e(n):
|
|
return '%.2f%s' % (n / 100, settings.PAYMENTS_CURRENCY[1])
|
|
|
|
# Dates
|
|
end = timezone.now().replace(microsecond=0, second=0, minute=0, hour=0, day=5)
|
|
start = get_prev_month(end)
|
|
|
|
# Filter users
|
|
filtering_report = StringIO()
|
|
all_users = User.objects.order_by('id')
|
|
active_users = all_users.filter(vpnuser__expiration__gt=start)
|
|
paying_users = active_users.filter(payment__status='confirmed').annotate(Count('payment')).filter(payment__count__gt=0)
|
|
users = [u for u in paying_users if should_bill(filtering_report, u, start)]
|
|
|
|
# Generate report
|
|
report = "CCVPN Usage Report\n"
|
|
report += "==================\n\n"
|
|
|
|
report += "From: %s\nTo : %s\n\n" % (start, end)
|
|
|
|
keys = ('Users', 'Active', 'W/Payment', 'Selected')
|
|
values = (all_users.count(), active_users.count(), paying_users.count(), len(users))
|
|
report += " | ".join("%-10s" % s for s in keys) + "\n"
|
|
report += " | ".join("%-10s" % s for s in values) + "\n"
|
|
report += "\n"
|
|
|
|
user_cost = settings.VPN_USER_COST
|
|
total_cost = settings.VPN_USER_COST * len(users)
|
|
|
|
report += "Billed: %d * %s = %s\n" % (len(users), format_e(user_cost), format_e(total_cost))
|
|
report += "\n"
|
|
|
|
if filtering_report.getvalue():
|
|
report += "Ignored users:\n"
|
|
report += filtering_report.getvalue()
|
|
report += "\n"
|
|
|
|
users_text = "\n".join("%s (#%d)" % (u.username, u.id) for u in users)
|
|
|
|
subject = "[CCVPN] Usage Report: %s to %s" % (
|
|
start.strftime('%m/%Y'), end.strftime('%m/%Y'))
|
|
|
|
# Send
|
|
print(report)
|
|
print("-------")
|
|
print("Send to: " + ", ".join(a for a in addresses))
|
|
|
|
print("Confirm? [y/n] ", end='')
|
|
i = input()
|
|
if i.lower().strip() != 'y':
|
|
return
|
|
|
|
for dest in addresses:
|
|
mail = EmailMessage(subject=subject, body=report,
|
|
from_email=settings.DEFAULT_FROM_EMAIL, to=[dest])
|
|
mail.attach('users.txt', users_text, 'text/plain')
|
|
mail.send()
|
|
|
|
print("Sent.")
|
|
|
|
|