+
+ {{ contact.website_email }}
+ {% if contact.pgp_finger_print %}
+ 🗝
+ {% endif %}
+
+
-
- Contact {{ contact.website_email }}
- {% if contact.pgp_finger_print %}
- 🗝
- {% endif %}
-
+
- Created {{ contact.created_at | dt }}
+ {% if contact_info.latest_email_log != None %}
+ {% set email_log = contact_info.latest_email_log %}
- {% if contact.latest_reply %}
- Last email sent {{ contact.latest_reply | dt }}
+ {% if email_log.is_reply %}
+
+ {{ email_log.created_at | dt }}
+ {% elif email_log.bounced %}
+
+
+ {{ email_log.created_at | dt }}
+
+ {% elif email_log.blocked %}
+
+ {{ email_log.created_at | dt }}
+ {% else %}
+
+ {{ email_log.created_at | dt }}
+ {% endif %}
+
+ Contact created {{ contact.created_at | dt }}
+ {% else %}
+ No Activity. Contact created {{ contact.created_at | dt }}
{% endif %}
+
+
+ {{ contact_info.nb_forward }} forwards,
+ {{ contact_info.nb_reply }} replies
+
Edit ➡
@@ -137,7 +164,7 @@
let that = $(this);
bootbox.confirm({
- message: "All activities associated with this reverse-alias will also be deleted, please confirm.",
+ message: "All activities associated with this contact will also be deleted, please confirm.",
buttons: {
confirm: {
label: 'Yes, delete it',
diff --git a/app/dashboard/templates/dashboard/index.html b/app/dashboard/templates/dashboard/index.html
index 263c206c..0fbda3b1 100644
--- a/app/dashboard/templates/dashboard/index.html
+++ b/app/dashboard/templates/dashboard/index.html
@@ -246,7 +246,7 @@
{{ email_log.created_at | dt }}
{% else %}
{{ contact.website_email }}
-
+
{{ email_log.created_at | dt }}
{% endif %}
{% else %}
diff --git a/app/dashboard/views/alias_contact_manager.py b/app/dashboard/views/alias_contact_manager.py
index cf899136..363eb975 100644
--- a/app/dashboard/views/alias_contact_manager.py
+++ b/app/dashboard/views/alias_contact_manager.py
@@ -1,8 +1,12 @@
import re
+from dataclasses import dataclass
+from operator import or_
-from flask import render_template, request, redirect, url_for, flash
+from flask import render_template, request, redirect, flash
+from flask import url_for
from flask_login import login_required, current_user
from flask_wtf import FlaskForm
+from sqlalchemy import and_, func, case
from wtforms import StringField, validators, ValidationError
from app.config import EMAIL_DOMAIN, PAGE_LIMIT
@@ -10,7 +14,7 @@ from app.dashboard.base import dashboard_bp
from app.email_utils import parseaddr_unicode
from app.extensions import db
from app.log import LOG
-from app.models import Alias, Contact
+from app.models import Alias, Contact, EmailLog
from app.utils import random_string
@@ -45,6 +49,82 @@ class NewContactForm(FlaskForm):
)
+@dataclass
+class ContactInfo(object):
+ contact: Contact
+
+ nb_forward: int
+ nb_reply: int
+
+ latest_email_log: EmailLog
+
+
+def get_contact_infos(alias: Alias, page=0, contact_id=None) -> [ContactInfo]:
+ """if contact_id is set, only return the contact info for this contact"""
+ sub = (
+ db.session.query(
+ Contact.id,
+ func.sum(case([(EmailLog.is_reply, 1)], else_=0)).label("nb_reply"),
+ func.sum(
+ case(
+ [
+ (
+ and_(
+ EmailLog.is_reply == False, EmailLog.blocked == False,
+ ),
+ 1,
+ )
+ ],
+ else_=0,
+ )
+ ).label("nb_forward"),
+ func.max(EmailLog.created_at).label("max_email_log_created_at"),
+ )
+ .join(EmailLog, EmailLog.contact_id == Contact.id, isouter=True,)
+ .filter(Contact.alias_id == alias.id)
+ .group_by(Contact.id)
+ .subquery()
+ )
+
+ q = (
+ db.session.query(Contact, EmailLog, sub.c.nb_reply, sub.c.nb_forward,)
+ .join(EmailLog, EmailLog.contact_id == Contact.id, isouter=True,)
+ .filter(Contact.alias_id == alias.id)
+ .filter(Contact.id == sub.c.id)
+ .filter(
+ or_(
+ EmailLog.created_at == sub.c.max_email_log_created_at,
+ # no email log yet for this contact
+ sub.c.max_email_log_created_at == None,
+ )
+ )
+ )
+
+ if contact_id:
+ q = q.filter(Contact.id == contact_id)
+
+ latest_activity = case(
+ [
+ (EmailLog.created_at > Contact.created_at, EmailLog.created_at),
+ (EmailLog.created_at < Contact.created_at, Contact.created_at),
+ ],
+ else_=Contact.created_at,
+ )
+ q = q.order_by(latest_activity.desc()).limit(PAGE_LIMIT).offset(page * PAGE_LIMIT)
+
+ ret = []
+ for contact, latest_email_log, nb_reply, nb_forward in q:
+ contact_info = ContactInfo(
+ contact=contact,
+ nb_forward=nb_forward,
+ nb_reply=nb_reply,
+ latest_email_log=latest_email_log,
+ )
+ ret.append(contact_info)
+
+ return ret
+
+
@dashboard_bp.route("/alias_contact_manager/
/", methods=["GET", "POST"])
@login_required
def alias_contact_manager(alias_id):
@@ -149,20 +229,20 @@ def alias_contact_manager(alias_id):
url_for("dashboard.alias_contact_manager", alias_id=alias_id)
)
+ contact_infos = get_contact_infos(alias, page)
+ last_page = len(contact_infos) < PAGE_LIMIT
+
+ # if highlighted contact isn't included, fetch it
# make sure highlighted contact is at array start
- contacts = alias.get_contacts(page)
- contact_ids = [contact.id for contact in contacts]
-
- last_page = len(contacts) < PAGE_LIMIT
-
+ contact_ids = [contact_info.contact.id for contact_info in contact_infos]
if highlight_contact_id not in contact_ids:
- contact = Contact.get(highlight_contact_id)
- if contact and contact.alias_id == alias.id:
- contacts.insert(0, contact)
+ contact_infos = (
+ get_contact_infos(alias, contact_id=highlight_contact_id) + contact_infos
+ )
return render_template(
"dashboard/alias_contact_manager.html",
- contacts=contacts,
+ contact_infos=contact_infos,
alias=alias,
new_contact_form=new_contact_form,
highlight_contact_id=highlight_contact_id,
diff --git a/app/models.py b/app/models.py
index 867f146b..48759460 100644
--- a/app/models.py
+++ b/app/models.py
@@ -9,7 +9,7 @@ import bcrypt
from arrow import Arrow
from flask import url_for
from flask_login import UserMixin
-from sqlalchemy import text, desc, CheckConstraint, and_, func
+from sqlalchemy import text, desc, CheckConstraint, and_, func, case
from sqlalchemy_utils import ArrowType
from app import s3
@@ -915,30 +915,6 @@ class Alias(db.Model, ModelMixin):
else:
return self.user.email
- def get_contacts(self, page=0):
- 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):
return f""