From f2fb59966436ea689c836df2823cebd7aa499a1a Mon Sep 17 00:00:00 2001
From: Son NK <>
Date: Tue, 4 Aug 2020 11:36:53 +0200
Subject: [PATCH] Optimize Alias.get_contacts() to retrieve the latest reply
for each contact in a single query
---
.../dashboard/alias_contact_manager.html | 5 ++--
app/models.py | 28 +++++++++++++++----
shell.py | 3 ++
3 files changed, 27 insertions(+), 9 deletions(-)
diff --git a/app/dashboard/templates/dashboard/alias_contact_manager.html b/app/dashboard/templates/dashboard/alias_contact_manager.html
index d3c3e5bc..e9c1bd21 100644
--- a/app/dashboard/templates/dashboard/alias_contact_manager.html
+++ b/app/dashboard/templates/dashboard/alias_contact_manager.html
@@ -89,9 +89,8 @@
Created {{ contact.created_at | dt }}
- {% if contact.last_reply() %}
- {% set email_log = contact.last_reply() %}
- Last email sent {{ email_log.created_at | dt }}
+ {% if contact.latest_reply %}
+ Last email sent {{ contact.latest_reply | dt }}
{% endif %}
diff --git a/app/models.py b/app/models.py
index 86d3143d..a4dc3f59 100644
--- a/app/models.py
+++ b/app/models.py
@@ -2,20 +2,19 @@ import enum
import random
import uuid
from email.utils import formataddr
-from typing import List, Tuple
+from typing import List, Tuple, Optional
import arrow
import bcrypt
+from arrow import Arrow
from flask import url_for
from flask_login import UserMixin
-from sqlalchemy import text, desc, CheckConstraint
+from sqlalchemy import text, desc, CheckConstraint, and_, func
from sqlalchemy.exc import IntegrityError
-from sqlalchemy.orm import joinedload
from sqlalchemy_utils import ArrowType
from app import s3
from app.config import (
- EMAIL_DOMAIN,
MAX_NB_EMAIL_FREE_PLAN,
URL,
AVATAR_URL_EXPIRATION,
@@ -914,13 +913,27 @@ class Alias(db.Model, ModelMixin):
return self.user.email
def get_contacts(self, page=0):
- contacts = (
- Contact.filter_by(alias_id=self.id)
+ latest_reply = func.max(EmailLog.created_at)
+ q = (
+ db.session.query(Contact, latest_reply)
+ .join(
+ EmailLog,
+ and_(EmailLog.contact_id == Contact.id, EmailLog.is_reply),
+ isouter=True,
+ )
+ .filter(Contact.alias_id == self.id)
+ .group_by(Contact.id)
.order_by(Contact.created_at.desc())
.limit(PAGE_LIMIT)
.offset(page * PAGE_LIMIT)
.all()
)
+
+ contacts = []
+ for contact, l in q:
+ contact.latest_reply = l
+ contacts.append(contact)
+
return contacts
def __repr__(self):
@@ -1050,6 +1063,9 @@ class Contact(db.Model, ModelMixin):
alias = db.relationship(Alias)
user = db.relationship(User)
+ # the latest reply sent to this contact
+ latest_reply: Optional[Arrow] = None
+
@property
def email(self):
return self.website_email
diff --git a/shell.py b/shell.py
index bbed14b7..5072fbb5 100644
--- a/shell.py
+++ b/shell.py
@@ -8,6 +8,9 @@ from app.models import *
from server import create_app
from time import sleep
+from sqlalchemy import *
+from sqlalchemy.orm import *
+
def create_db():
if not database_exists(DB_URI):