diff --git a/cron.py b/cron.py
index e1715d07..98c2a0e2 100644
--- a/cron.py
+++ b/cron.py
@@ -1,6 +1,8 @@
import argparse
+from dataclasses import dataclass
import arrow
+from arrow import Arrow
from app import s3
from app.api.views.apple import verify_receipt
@@ -117,89 +119,140 @@ def poll_apple_subscription():
LOG.d("Finish poll_apple_subscription")
-def stats():
- """send admin stats everyday"""
- if not ADMIN_EMAIL:
- # nothing to do
- return
+@dataclass
+class Stats:
+ nb_user: int
+ nb_alias: int
+ nb_forward: int
+ nb_block: int
+ nb_reply: int
+ nb_bounced: int
+ nb_spam: int
+
+ nb_custom_domain: int
+ nb_app: int
+ nb_premium: int
+
+
+def stats_before(moment: Arrow) -> Stats:
+ """return the stats before a specific moment, ignoring all stats come from users in IGNORED_EMAILS
+ """
# nb user
q = User.query
for ie in IGNORED_EMAILS:
- q = q.filter(~User.email.contains(ie))
+ q = q.filter(~User.email.contains(ie), User.created_at < moment)
nb_user = q.count()
-
LOG.d("total number user %s", nb_user)
- # nb gen emails
- q = db.session.query(Alias, User).filter(Alias.user_id == User.id)
+ # nb alias
+ q = db.session.query(Alias, User).filter(
+ Alias.user_id == User.id, Alias.created_at < moment
+ )
for ie in IGNORED_EMAILS:
q = q.filter(~User.email.contains(ie))
nb_alias = q.count()
LOG.d("total number alias %s", nb_alias)
- # nb mails forwarded
+ # email log stats
q = db.session.query(EmailLog, Contact, Alias, User).filter(
EmailLog.contact_id == Contact.id,
Contact.alias_id == Alias.id,
Alias.user_id == User.id,
+ EmailLog.created_at < moment,
)
for ie in IGNORED_EMAILS:
q = q.filter(~User.email.contains(ie))
- nb_forward = nb_block = nb_reply = 0
+ nb_spam = nb_bounced = nb_forward = nb_block = nb_reply = 0
for email_log, _, _, _ in q:
- if email_log.is_reply:
+ if email_log.bounced:
+ nb_bounced += 1
+ elif email_log.is_spam:
+ nb_spam += 1
+ elif email_log.is_reply:
nb_reply += 1
elif email_log.blocked:
nb_block += 1
else:
nb_forward += 1
- LOG.d("nb forward %s, nb block %s, nb reply %s", nb_forward, nb_block, nb_reply)
+ LOG.d(
+ "nb_forward %s, nb_block %s, nb_reply %s, nb_bounced %s, nb_spam %s",
+ nb_forward,
+ nb_block,
+ nb_reply,
+ nb_bounced,
+ nb_spam,
+ )
- nb_premium = Subscription.query.count()
- nb_custom_domain = CustomDomain.query.count()
-
- nb_custom_domain_alias = Alias.query.filter(
- Alias.custom_domain_id.isnot(None)
+ nb_premium = Subscription.query.filter(Subscription.created_at < moment).count()
+ nb_premium += AppleSubscription.query.filter(
+ AppleSubscription.created_at < moment
).count()
- nb_disabled_alias = Alias.query.filter(Alias.enabled == False).count()
+ nb_custom_domain = CustomDomain.query.filter(
+ CustomDomain.created_at < moment
+ ).count()
- nb_app = Client.query.count()
+ nb_app = Client.query.filter(Client.created_at < moment).count()
- nb_bounced_email = EmailLog.query.filter(EmailLog.bounced).count()
- nb_spam = EmailLog.query.filter(EmailLog.is_spam).count()
+ data = locals()
+ # to keep only Stats field
+ data = {
+ k: v
+ for (k, v) in data.items()
+ if k in vars(Stats)["__dataclass_fields__"].keys()
+ }
+ return Stats(**data)
+
+
+def increase_percent(old, new) -> str:
+ if old == 0:
+ return "N/A"
+
+ increase = (new - old) / old * 100
+ return f"{increase:.1f}%"
+
+
+def stats():
+ """send admin stats everyday"""
+ if not ADMIN_EMAIL:
+ # nothing to do
+ return
+
+ stats_today = stats_before(arrow.now())
+ stats_yesterday = stats_before(arrow.now().shift(days=-1))
+
+ nb_user_increase = increase_percent(stats_yesterday.nb_user, stats_today.nb_user)
+ nb_alias_increase = increase_percent(stats_yesterday.nb_alias, stats_today.nb_alias)
+ nb_forward_increase = increase_percent(
+ stats_yesterday.nb_forward, stats_today.nb_forward
+ )
today = arrow.now().format()
send_email(
ADMIN_EMAIL,
- subject=f"SimpleLogin Stats for {today}, {nb_user} users, {nb_alias} aliases, {nb_forward} forwards",
+ subject=f"SimpleLogin Stats for {today}, {nb_user_increase} users, {nb_alias_increase} aliases, {nb_forward_increase} forwards",
plaintext="",
html=f"""
Stats for {today}
-nb_user: {nb_user}
-nb_premium: {nb_premium}
+nb_user: {stats_today.nb_user} - {increase_percent(stats_yesterday.nb_user, stats_today.nb_user)}
+nb_premium: {stats_today.nb_premium} - {increase_percent(stats_yesterday.nb_premium, stats_today.nb_premium)}
+nb_alias: {stats_today.nb_alias} - {increase_percent(stats_yesterday.nb_alias, stats_today.nb_alias)}
-nb_alias: {nb_alias}
-nb_disabled_alias: {nb_disabled_alias}
+nb_forward: {stats_today.nb_forward} - {increase_percent(stats_yesterday.nb_forward, stats_today.nb_forward)}
+nb_reply: {stats_today.nb_reply} - {increase_percent(stats_yesterday.nb_reply, stats_today.nb_reply)}
+nb_block: {stats_today.nb_block} - {increase_percent(stats_yesterday.nb_block, stats_today.nb_block)}
+nb_bounced: {stats_today.nb_bounced} - {increase_percent(stats_yesterday.nb_bounced, stats_today.nb_bounced)}
+nb_spam: {stats_today.nb_spam} - {increase_percent(stats_yesterday.nb_spam, stats_today.nb_spam)}
-nb_custom_domain: {nb_custom_domain}
-nb_custom_domain_alias: {nb_custom_domain_alias}
-
-nb_forward: {nb_forward}
-nb_reply: {nb_reply}
-nb_block: {nb_block}
-
-nb_app: {nb_app}
-
-nb_bounced_email: {nb_bounced_email}
-nb_spam: {nb_spam}
+nb_custom_domain: {stats_today.nb_custom_domain} - {increase_percent(stats_yesterday.nb_custom_domain, stats_today.nb_custom_domain)}
+nb_app: {stats_today.nb_app} - {increase_percent(stats_yesterday.nb_app, stats_today.nb_app)}
""",
)