diff --git a/app/models.py b/app/models.py index ea78d970..2fd19ebc 100644 --- a/app/models.py +++ b/app/models.py @@ -2132,6 +2132,41 @@ class Metric(db.Model, ModelMixin): NB_APP = "nb_app" +class Metric2(db.Model, ModelMixin): + """ + For storing different metrics like number of users, etc + Store each metric as a column as opposed to having different rows as in Metric + """ + + date = db.Column(ArrowType, default=arrow.utcnow, nullable=False) + name = db.Column(db.String(256), nullable=False) + + nb_user = db.Column(db.Float, nullable=True) + nb_activated_user = db.Column(db.Float, nullable=True) + + nb_premium = db.Column(db.Float, nullable=True) + nb_apple_premium = db.Column(db.Float, nullable=True) + nb_cancelled_premium = db.Column(db.Float, nullable=True) + nb_manual_premium = db.Column(db.Float, nullable=True) + nb_coinbase_premium = db.Column(db.Float, nullable=True) + + # nb users who have been referred + nb_referred_user = db.Column(db.Float, nullable=True) + nb_referred_user_paid = db.Column(db.Float, nullable=True) + + nb_alias = db.Column(db.Float, nullable=True) + + nb_forward = db.Column(db.Float, nullable=True) + nb_block = db.Column(db.Float, nullable=True) + nb_reply = db.Column(db.Float, nullable=True) + nb_bounced = db.Column(db.Float, nullable=True) + nb_spam = db.Column(db.Float, nullable=True) + + nb_verified_custom_domain = db.Column(db.Float, nullable=True) + + nb_app = db.Column(db.Float, nullable=True) + + class Bounce(db.Model, ModelMixin): """Record all bounces. Deleted after 7 days""" diff --git a/cron.py b/cron.py index 9d34b317..c2b93b20 100644 --- a/cron.py +++ b/cron.py @@ -49,6 +49,7 @@ from app.models import ( Metric, TransactionalEmail, Bounce, + Metric2, ) from server import create_app @@ -198,134 +199,6 @@ def poll_apple_subscription(): LOG.d("Finish poll_apple_subscription") -@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 - nb_apple_premium: int - nb_cancelled_premium: int - nb_manual_premium: int - nb_coinbase_premium: int - - # nb users who have been referred - nb_referred_user: int - nb_referred_user_upgrade: 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), User.created_at < moment) - - nb_user = q.count() - LOG.d("total number user %s", nb_user) - - nb_referred_user = q.filter(User.referral_id.isnot(None)).count() - nb_referred_user_upgrade = 0 - for user in q.filter(User.referral_id.isnot(None)): - if user.is_paid(): - nb_referred_user_upgrade += 1 - - LOG.d( - "%s nb_referred_user:%s nb_referred_user_upgrade:%s", - moment, - nb_referred_user, - nb_referred_user_upgrade, - ) - - # 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) - - # email log stats - q = ( - db.session.query(EmailLog) - .join(User, EmailLog.user_id == User.id) - .filter( - EmailLog.created_at < moment, - ) - ) - for ie in IGNORED_EMAILS: - q = q.filter(~User.email.contains(ie)) - - nb_spam = nb_bounced = nb_forward = nb_block = nb_reply = 0 - for email_log in q.yield_per(500): - 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_bounced %s, nb_spam %s", - nb_forward, - nb_block, - nb_reply, - nb_bounced, - nb_spam, - ) - - nb_premium = Subscription.query.filter( - Subscription.created_at < moment, Subscription.cancelled.is_(False) - ).count() - nb_apple_premium = AppleSubscription.query.filter( - AppleSubscription.created_at < moment - ).count() - nb_cancelled_premium = Subscription.query.filter( - Subscription.created_at < moment, Subscription.cancelled.is_(True) - ).count() - - now = arrow.now() - nb_manual_premium = ManualSubscription.query.filter( - ManualSubscription.created_at < moment, - ManualSubscription.end_at > now, - ManualSubscription.is_giveaway.is_(False), - ).count() - - nb_coinbase_premium = CoinbaseSubscription.query.filter( - CoinbaseSubscription.created_at < moment, CoinbaseSubscription.end_at > now - ).count() - - nb_custom_domain = CustomDomain.query.filter( - CustomDomain.created_at < moment - ).count() - - nb_app = Client.query.filter(Client.created_at < moment).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 compute_metrics(): now = arrow.now() diff --git a/migrations/versions/2021_032310_9d6adad83936_.py b/migrations/versions/2021_032310_9d6adad83936_.py new file mode 100644 index 00000000..75ea34c5 --- /dev/null +++ b/migrations/versions/2021_032310_9d6adad83936_.py @@ -0,0 +1,53 @@ +"""empty message + +Revision ID: 9d6adad83936 +Revises: 94f14eb0fe5b +Create Date: 2021-03-23 10:23:17.879887 + +""" +import sqlalchemy_utils +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '9d6adad83936' +down_revision = '94f14eb0fe5b' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('metric2', + 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', sqlalchemy_utils.types.arrow.ArrowType(), nullable=False), + sa.Column('name', sa.String(length=256), nullable=False), + sa.Column('nb_user', sa.Float(), nullable=True), + sa.Column('nb_activated_user', sa.Float(), nullable=True), + sa.Column('nb_premium', sa.Float(), nullable=True), + sa.Column('nb_apple_premium', sa.Float(), nullable=True), + sa.Column('nb_cancelled_premium', sa.Float(), nullable=True), + sa.Column('nb_manual_premium', sa.Float(), nullable=True), + sa.Column('nb_coinbase_premium', sa.Float(), nullable=True), + sa.Column('nb_referred_user', sa.Float(), nullable=True), + sa.Column('nb_referred_user_paid', sa.Float(), nullable=True), + sa.Column('nb_alias', sa.Float(), nullable=True), + sa.Column('nb_forward', sa.Float(), nullable=True), + sa.Column('nb_block', sa.Float(), nullable=True), + sa.Column('nb_reply', sa.Float(), nullable=True), + sa.Column('nb_bounced', sa.Float(), nullable=True), + sa.Column('nb_spam', sa.Float(), nullable=True), + sa.Column('nb_verified_custom_domain', sa.Float(), nullable=True), + sa.Column('nb_app', sa.Float(), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('metric2') + # ### end Alembic commands ###