From 48838eb176702f1d3316e1b6cb79f7b9c517c9fc Mon Sep 17 00:00:00 2001 From: Son Nguyen Kim Date: Tue, 7 Sep 2021 15:22:50 +0200 Subject: [PATCH] add highlighted alias in case it's not included in the result --- app/api/serializer.py | 194 ++++++++++++++++++++--------------- app/dashboard/views/index.py | 12 ++- 2 files changed, 123 insertions(+), 83 deletions(-) diff --git a/app/api/serializer.py b/app/api/serializer.py index 47fad9b2..f31dfee4 100644 --- a/app/api/serializer.py +++ b/app/api/serializer.py @@ -7,7 +7,15 @@ from sqlalchemy.orm import joinedload from app.config import PAGE_LIMIT from app.extensions import db -from app.models import Alias, Contact, EmailLog, Mailbox, AliasMailbox, CustomDomain +from app.models import ( + Alias, + Contact, + EmailLog, + Mailbox, + AliasMailbox, + CustomDomain, + User, +) @dataclass @@ -137,87 +145,7 @@ def get_alias_infos_with_pagination_v3( mailbox_id=None, directory_id=None, ) -> [AliasInfo]: - # subquery on alias annotated with nb_reply, nb_blocked, nb_forward, max_created_at, latest_email_log_created_at - alias_activity_subquery = ( - db.session.query( - Alias.id, - func.sum(case([(EmailLog.is_reply, 1)], else_=0)).label("nb_reply"), - func.sum( - case( - [(and_(EmailLog.is_reply.is_(False), EmailLog.blocked), 1)], - else_=0, - ) - ).label("nb_blocked"), - func.sum( - case( - [ - ( - and_( - EmailLog.is_reply.is_(False), - EmailLog.blocked.is_(False), - ), - 1, - ) - ], - else_=0, - ) - ).label("nb_forward"), - func.max(EmailLog.created_at).label("latest_email_log_created_at"), - ) - .join(EmailLog, Alias.id == EmailLog.alias_id, isouter=True) - .filter(Alias.user_id == user.id) - .group_by(Alias.id) - .subquery() - ) - - alias_contact_subquery = ( - db.session.query(Alias.id, func.max(Contact.id).label("max_contact_id")) - .join(Contact, Alias.id == Contact.alias_id, isouter=True) - .filter(Alias.user_id == user.id) - .group_by(Alias.id) - .subquery() - ) - - latest_activity = case( - [ - (Alias.created_at > EmailLog.created_at, Alias.created_at), - (Alias.created_at < EmailLog.created_at, EmailLog.created_at), - ], - else_=Alias.created_at, - ) - - q = ( - db.session.query( - Alias, - Contact, - EmailLog, - CustomDomain, - alias_activity_subquery.c.nb_reply, - alias_activity_subquery.c.nb_blocked, - alias_activity_subquery.c.nb_forward, - ) - .options(joinedload(Alias.hibp_breaches)) - .join(Contact, Alias.id == Contact.alias_id, isouter=True) - .join(CustomDomain, Alias.custom_domain_id == CustomDomain.id, isouter=True) - .join(EmailLog, Contact.id == EmailLog.contact_id, isouter=True) - .filter(Alias.id == alias_activity_subquery.c.id) - .filter(Alias.id == alias_contact_subquery.c.id) - .filter( - or_( - EmailLog.created_at - == alias_activity_subquery.c.latest_email_log_created_at, - and_( - # no email log yet for this alias - alias_activity_subquery.c.latest_email_log_created_at.is_(None), - # to make sure only 1 contact is returned in this case - or_( - Contact.id == alias_contact_subquery.c.max_contact_id, - alias_contact_subquery.c.max_contact_id.is_(None), - ), - ), - ) - ) - ) + q = construct_alias_query(user) if query: q = q.filter( @@ -262,6 +190,13 @@ def get_alias_infos_with_pagination_v3( q = q.order_by(Alias.email.desc()) else: # default sorting + latest_activity = case( + [ + (Alias.created_at > EmailLog.created_at, Alias.created_at), + (Alias.created_at < EmailLog.created_at, EmailLog.created_at), + ], + else_=Alias.created_at, + ) q = q.order_by(latest_activity.desc()) q = list(q.limit(PAGE_LIMIT).offset(page_id * PAGE_LIMIT)) @@ -374,3 +309,98 @@ def get_alias_contacts(alias, page_id: int) -> [dict]: res.append(serialize_contact(fe)) return res + + +def get_alias_info_v3(user: User, alias_id: int) -> AliasInfo: + # use the same query construction in get_alias_infos_with_pagination_v3 + q = construct_alias_query(user) + q = q.filter(Alias.id == alias_id) + + for alias, contact, email_log, custom_domain, nb_reply, nb_blocked, nb_forward in q: + return AliasInfo( + alias=alias, + mailbox=alias.mailbox, + mailboxes=alias.mailboxes, + nb_forward=nb_forward, + nb_blocked=nb_blocked, + nb_reply=nb_reply, + latest_email_log=email_log, + latest_contact=contact, + custom_domain=custom_domain, + ) + + +def construct_alias_query(user: User): + # subquery on alias annotated with nb_reply, nb_blocked, nb_forward, max_created_at, latest_email_log_created_at + alias_activity_subquery = ( + db.session.query( + Alias.id, + func.sum(case([(EmailLog.is_reply, 1)], else_=0)).label("nb_reply"), + func.sum( + case( + [(and_(EmailLog.is_reply.is_(False), EmailLog.blocked), 1)], + else_=0, + ) + ).label("nb_blocked"), + func.sum( + case( + [ + ( + and_( + EmailLog.is_reply.is_(False), + EmailLog.blocked.is_(False), + ), + 1, + ) + ], + else_=0, + ) + ).label("nb_forward"), + func.max(EmailLog.created_at).label("latest_email_log_created_at"), + ) + .join(EmailLog, Alias.id == EmailLog.alias_id, isouter=True) + .filter(Alias.user_id == user.id) + .group_by(Alias.id) + .subquery() + ) + + alias_contact_subquery = ( + db.session.query(Alias.id, func.max(Contact.id).label("max_contact_id")) + .join(Contact, Alias.id == Contact.alias_id, isouter=True) + .filter(Alias.user_id == user.id) + .group_by(Alias.id) + .subquery() + ) + + return ( + db.session.query( + Alias, + Contact, + EmailLog, + CustomDomain, + alias_activity_subquery.c.nb_reply, + alias_activity_subquery.c.nb_blocked, + alias_activity_subquery.c.nb_forward, + ) + .options(joinedload(Alias.hibp_breaches)) + .join(Contact, Alias.id == Contact.alias_id, isouter=True) + .join(CustomDomain, Alias.custom_domain_id == CustomDomain.id, isouter=True) + .join(EmailLog, Contact.id == EmailLog.contact_id, isouter=True) + .filter(Alias.id == alias_activity_subquery.c.id) + .filter(Alias.id == alias_contact_subquery.c.id) + .filter( + or_( + EmailLog.created_at + == alias_activity_subquery.c.latest_email_log_created_at, + and_( + # no email log yet for this alias + alias_activity_subquery.c.latest_email_log_created_at.is_(None), + # to make sure only 1 contact is returned in this case + or_( + Contact.id == alias_contact_subquery.c.max_contact_id, + alias_contact_subquery.c.max_contact_id.is_(None), + ), + ), + ) + ) + ) diff --git a/app/dashboard/views/index.py b/app/dashboard/views/index.py index 51690075..5cc61c27 100644 --- a/app/dashboard/views/index.py +++ b/app/dashboard/views/index.py @@ -4,7 +4,7 @@ from flask import render_template, request, redirect, url_for, flash from flask_login import login_required, current_user from app import alias_utils -from app.api.serializer import get_alias_infos_with_pagination_v3 +from app.api.serializer import get_alias_infos_with_pagination_v3, get_alias_info_v3 from app.config import PAGE_LIMIT, ALIAS_LIMIT from app.dashboard.base import dashboard_bp from app.extensions import db, limiter @@ -163,6 +163,16 @@ def index(): ) last_page = len(alias_infos) < PAGE_LIMIT + # add highlighted alias in case it's not included + if highlight_alias_id and highlight_alias_id not in [ + alias_info.alias.id for alias_info in alias_infos + ]: + highlight_alias_info = get_alias_info_v3( + current_user, alias_id=highlight_alias_id + ) + if highlight_alias_info: + alias_infos.insert(0, highlight_alias_info) + return render_template( "dashboard/index.html", alias_infos=alias_infos,