From c71824c68e9cd6451628d88eaf4cde63b4bddec8 Mon Sep 17 00:00:00 2001 From: Son Nguyen Kim Date: Fri, 14 Oct 2022 17:35:34 +0200 Subject: [PATCH] Init daily metric (#1351) * Add DailyMetric model * increment nb_new_web_non_proton_user * fix test * fix test --- app/auth/views/register.py | 6 ++- app/models.py | 29 ++++++++++++++ .../versions/2022_101416_2c2093c82bc0_.py | 38 +++++++++++++++++++ tests/auth/test_register.py | 21 +++++++++- tests/dashboard/test_custom_alias.py | 32 ++++++++++++++++ 5 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 migrations/versions/2022_101416_2c2093c82bc0_.py diff --git a/app/auth/views/register.py b/app/auth/views/register.py index 379e1576..561eb452 100644 --- a/app/auth/views/register.py +++ b/app/auth/views/register.py @@ -16,7 +16,7 @@ from app.email_utils import ( ) from app.events.auth_event import RegisterEvent from app.log import LOG -from app.models import User, ActivationCode +from app.models import User, ActivationCode, DailyMetric from app.utils import random_string, encode_url, sanitize_email @@ -91,6 +91,10 @@ def register(): try: send_activation_email(user, next_url) RegisterEvent(RegisterEvent.ActionType.success).send() + DailyMetric.get_or_create_today_metric().nb_new_web_non_proton_user += ( + 1 + ) + Session.commit() except Exception: flash("Invalid email, are you sure the email is correct?", "error") RegisterEvent(RegisterEvent.ActionType.invalid_email).send() diff --git a/app/models.py b/app/models.py index 07e6ec3c..3edc8ab5 100644 --- a/app/models.py +++ b/app/models.py @@ -1457,6 +1457,7 @@ class Alias(Base, ModelMixin): new_alias.custom_domain_id = custom_domain.id Session.add(new_alias) + DailyMetric.get_or_create_today_metric().nb_alias += 1 if commit: Session.commit() @@ -2892,6 +2893,34 @@ class Metric2(Base, ModelMixin): nb_app = sa.Column(sa.Float, nullable=True) +class DailyMetric(Base, ModelMixin): + """ + For storing daily event-based metrics. + The difference between DailyEventMetric and Metric2 is Metric2 stores the total + whereas DailyEventMetric is reset for a new day + """ + + __tablename__ = "daily_metric" + date = sa.Column(sa.Date, nullable=False, unique=True) + + # users who sign up via web without using "Login with Proton" + nb_new_web_non_proton_user = sa.Column( + sa.Integer, nullable=False, server_default="0", default=0 + ) + + nb_alias = sa.Column(sa.Integer, nullable=False, server_default="0", default=0) + + @staticmethod + def get_or_create_today_metric() -> DailyMetric: + today = arrow.utcnow().date() + daily_metric = DailyMetric.get_by(date=today) + if not daily_metric: + daily_metric = DailyMetric.create( + date=today, nb_new_web_non_proton_user=0, nb_alias=0 + ) + return daily_metric + + class Bounce(Base, ModelMixin): """Record all bounces. Deleted after 7 days""" diff --git a/migrations/versions/2022_101416_2c2093c82bc0_.py b/migrations/versions/2022_101416_2c2093c82bc0_.py new file mode 100644 index 00000000..c3a5e6d1 --- /dev/null +++ b/migrations/versions/2022_101416_2c2093c82bc0_.py @@ -0,0 +1,38 @@ +"""empty message + +Revision ID: 2c2093c82bc0 +Revises: bd95b2b4217f +Create Date: 2022-10-14 16:27:49.839887 + +""" +import sqlalchemy_utils +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '2c2093c82bc0' +down_revision = 'bd95b2b4217f' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('daily_metric', + sa.Column('id', sa.Integer(), autoincrement=True, nullable=False), + sa.Column('created_at', sqlalchemy_utils.types.arrow.ArrowType(), nullable=False), + sa.Column('updated_at', sqlalchemy_utils.types.arrow.ArrowType(), nullable=True), + sa.Column('date', sa.Date(), nullable=False), + sa.Column('nb_new_web_non_proton_user', sa.Integer(), server_default='0', nullable=False), + sa.Column('nb_alias', sa.Integer(), server_default='0', nullable=False), + sa.PrimaryKeyConstraint('id'), + sa.UniqueConstraint('date') + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('daily_metric') + # ### end Alembic commands ### diff --git a/tests/auth/test_register.py b/tests/auth/test_register.py index bf9a3eba..fafa4679 100644 --- a/tests/auth/test_register.py +++ b/tests/auth/test_register.py @@ -1,8 +1,10 @@ from flask import url_for +from app.db import Session +from app.models import DailyMetric + def test_register_success(flask_client): - """User arrives at the waiting activation page.""" r = flask_client.post( url_for("auth.register"), data={"email": "abcd@gmail.com", "password": "password"}, @@ -10,9 +12,26 @@ def test_register_success(flask_client): ) assert r.status_code == 200 + # User arrives at the waiting activation page. assert b"An email to validate your email is on its way" in r.data +def test_register_increment_nb_new_web_non_proton_user(flask_client): + daily_metric = DailyMetric.get_or_create_today_metric() + Session.commit() + nb_new_web_non_proton_user = daily_metric.nb_new_web_non_proton_user + + r = flask_client.post( + url_for("auth.register"), + data={"email": "abcd@gmail.com", "password": "password"}, + follow_redirects=True, + ) + + assert r.status_code == 200 + new_daily_metric = DailyMetric.get_or_create_today_metric() + assert new_daily_metric.nb_new_web_non_proton_user == nb_new_web_non_proton_user + 1 + + def test_register_disabled(flask_client): """User cannot create new account when DISABLE_REGISTRATION.""" from app import config diff --git a/tests/dashboard/test_custom_alias.py b/tests/dashboard/test_custom_alias.py index 31a4d690..306175c2 100644 --- a/tests/dashboard/test_custom_alias.py +++ b/tests/dashboard/test_custom_alias.py @@ -19,6 +19,7 @@ from app.models import ( DomainDeletedAlias, DeletedAlias, SLDomain, + DailyMetric, ) from app.utils import random_word from tests.utils import login, random_domain, create_new_user @@ -53,6 +54,37 @@ def test_add_alias_success(flask_client): assert not alias._mailboxes +def test_add_alias_increment_nb_daily_metric_alias(flask_client): + user = login(flask_client) + + daily_metric = DailyMetric.get_or_create_today_metric() + Session.commit() + nb_alias = daily_metric.nb_alias + + suffix = f".{int(random() * 100000)}@{EMAIL_DOMAIN}" + alias_suffix = AliasSuffix( + is_custom=False, + suffix=suffix, + signed_suffix=signer.sign(suffix).decode(), + is_premium=False, + domain=EMAIL_DOMAIN, + ) + + # create with a single mailbox + r = flask_client.post( + url_for("dashboard.custom_alias"), + data={ + "prefix": "prefix", + "signed-alias-suffix": alias_suffix.signed_suffix, + "mailboxes": [user.default_mailbox_id], + }, + follow_redirects=True, + ) + assert r.status_code == 200 + new_daily_metric = DailyMetric.get_or_create_today_metric() + assert new_daily_metric.nb_alias == nb_alias + 1 + + def test_add_alias_multiple_mailboxes(flask_client): user = login(flask_client) Session.commit()