From 5503e188173807bbd42abeec273afa0f120c0848 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Thu, 27 Jan 2022 18:18:43 +0530 Subject: [PATCH] chore(typing): Add typing, style fixes This was going to be a fix at first. Until I realised it couldn't...so I removed the changes and kept the things back that helped me debug the issue --- frappe/core/doctype/communication/email.py | 32 ++++++++++--------- frappe/core/doctype/communication/mixins.py | 15 ++++++--- .../doctype/email_account/email_account.py | 10 +++--- .../email/doctype/email_queue/email_queue.py | 31 +++++++++++------- frappe/website/utils.py | 3 +- 5 files changed, 52 insertions(+), 39 deletions(-) diff --git a/frappe/core/doctype/communication/email.py b/frappe/core/doctype/communication/email.py index 54ddbce2c4..1346054aaf 100755 --- a/frappe/core/doctype/communication/email.py +++ b/frappe/core/doctype/communication/email.py @@ -1,29 +1,32 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors # License: MIT. See LICENSE -import frappe import json -from email.utils import formataddr -from frappe.core.utils import get_parent_doc -from frappe.utils import (get_url, get_formatted_email, cint, list_to_str, - validate_email_address, split_emails, parse_addr, get_datetime) -from frappe.email.email_body import get_message_id +from typing import TYPE_CHECKING, Dict + +import frappe import frappe.email.smtp -import time from frappe import _ -from frappe.utils.background_jobs import enqueue +from frappe.email.email_body import get_message_id +from frappe.utils import (cint, get_datetime, get_formatted_email, + list_to_str, split_emails, validate_email_address) + +if TYPE_CHECKING: + from frappe.core.doctype.communication.communication import Communication + OUTGOING_EMAIL_ACCOUNT_MISSING = _(""" Unable to send mail because of a missing email account. Please setup default Email Account from Setup > Email > Email Account """) + @frappe.whitelist() def make(doctype=None, name=None, content=None, subject=None, sent_or_received = "Sent", sender=None, sender_full_name=None, recipients=None, communication_medium="Email", send_email=False, print_html=None, print_format=None, attachments='[]', send_me_a_copy=False, cc=None, bcc=None, flags=None, read_receipt=None, print_letterhead=True, email_template=None, communication_type=None, - ignore_permissions=False): + ignore_permissions=False) -> Dict[str, str]: """Make a new communication. :param doctype: Reference DocType. @@ -56,7 +59,7 @@ def make(doctype=None, name=None, content=None, subject=None, sent_or_received = cc = list_to_str(cc) if isinstance(cc, list) else cc bcc = list_to_str(bcc) if isinstance(bcc, list) else bcc - comm = frappe.get_doc({ + comm: "Communication" = frappe.get_doc({ "doctype":"Communication", "subject": subject, "content": content, @@ -93,12 +96,13 @@ def make(doctype=None, name=None, content=None, subject=None, sent_or_received = send_me_a_copy=send_me_a_copy, print_letterhead=print_letterhead) emails_not_sent_to = comm.exclude_emails_list(include_sender=send_me_a_copy) + return { "name": comm.name, - "emails_not_sent_to": ", ".join(emails_not_sent_to or []) + "emails_not_sent_to": ", ".join(emails_not_sent_to) } -def validate_email(doc): +def validate_email(doc: "Communication") -> None: """Validate Email Addresses of Recipients and CC""" if not (doc.communication_type=="Communication" and doc.communication_medium == "Email") or doc.flags.in_receive: return @@ -114,8 +118,6 @@ def validate_email(doc): for email in split_emails(doc.bcc): validate_email_address(email, throw=True) - # validate sender - def set_incoming_outgoing_accounts(doc): from frappe.email.doctype.email_account.email_account import EmailAccount incoming_email_account = EmailAccount.find_incoming( diff --git a/frappe/core/doctype/communication/mixins.py b/frappe/core/doctype/communication/mixins.py index b6d8070d00..dd9f58342e 100644 --- a/frappe/core/doctype/communication/mixins.py +++ b/frappe/core/doctype/communication/mixins.py @@ -1,3 +1,4 @@ +from typing import List import frappe from frappe import _ from frappe.core.utils import get_parent_doc @@ -194,14 +195,18 @@ class CommunicationEmailMixin: return _("Leave this conversation") return '' - def exclude_emails_list(self, is_inbound_mail_communcation=False, include_sender=False): + def exclude_emails_list(self, is_inbound_mail_communcation=False, include_sender=False) -> List: """List of mail id's excluded while sending mail. """ all_ids = self.get_all_email_addresses(exclude_displayname=True) - final_ids = self.mail_recipients(is_inbound_mail_communcation = is_inbound_mail_communcation) + \ - self.mail_bcc(is_inbound_mail_communcation = is_inbound_mail_communcation) + \ - self.mail_cc(is_inbound_mail_communcation = is_inbound_mail_communcation, include_sender=include_sender) - return set(all_ids) - set(final_ids) + + final_ids = ( + self.mail_recipients(is_inbound_mail_communcation=is_inbound_mail_communcation) + + self.mail_bcc(is_inbound_mail_communcation=is_inbound_mail_communcation) + + self.mail_cc(is_inbound_mail_communcation=is_inbound_mail_communcation, include_sender=include_sender) + ) + + return list(set(all_ids) - set(final_ids)) def get_assignees(self): """Get owners of the reference document. diff --git a/frappe/email/doctype/email_account/email_account.py b/frappe/email/doctype/email_account/email_account.py index 3f8d399c52..c78f003b65 100755 --- a/frappe/email/doctype/email_account/email_account.py +++ b/frappe/email/doctype/email_account/email_account.py @@ -1,5 +1,6 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors +# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors # License: MIT. See LICENSE + import email.utils import functools import imaplib @@ -7,6 +8,7 @@ import socket import time from datetime import datetime, timedelta from poplib import error_proto +from typing import List import frappe from frappe import _, are_emails_muted, safe_encode @@ -82,9 +84,6 @@ class EmailAccount(Document): if frappe.local.flags.in_patch or frappe.local.flags.in_test: return - #if self.enable_incoming and not self.append_to: - # frappe.throw(_("Append To is mandatory for incoming mails")) - if (not self.awaiting_password and not frappe.local.flags.in_install and not frappe.local.flags.in_patch): if self.password or self.smtp_server in ('127.0.0.1', 'localhost'): @@ -458,7 +457,7 @@ class EmailAccount(Document): if exceptions: raise Exception(frappe.as_json(exceptions)) - def get_inbound_mails(self, test_mails=None): + def get_inbound_mails(self, test_mails=None) -> List[InboundMail]: """retrive and return inbound mails. """ @@ -625,7 +624,6 @@ class EmailAccount(Document): if frappe.db.exists("Email Account", {"enable_automatic_linking": 1, "name": ('!=', self.name)}): frappe.throw(_("Automatic Linking can be activated only for one Email Account.")) - def append_email_to_sent_folder(self, message): email_server = None try: diff --git a/frappe/email/doctype/email_queue/email_queue.py b/frappe/email/doctype/email_queue/email_queue.py index 9730004065..a7a593806f 100644 --- a/frappe/email/doctype/email_queue/email_queue.py +++ b/frappe/email/doctype/email_queue/email_queue.py @@ -20,6 +20,7 @@ from frappe.email.queue import get_unsubcribed_url, get_unsubscribe_message from frappe.email.email_body import add_attachment, get_formatted_html, get_email from frappe.utils import cint, split_emails, add_days, nowdate, cstr, get_hook_method from frappe.email.doctype.email_account.email_account import EmailAccount +from frappe.query_builder.utils import DocType MAX_RETRY_COUNT = 3 @@ -477,18 +478,24 @@ class QueueBuilder: all_ids = list(set(self.recipients + self.cc)) - EmailUnsubscribe = frappe.qb.DocType("Email Unsubscribe") - - unsubscribed = (frappe.qb.from_(EmailUnsubscribe) - .select(EmailUnsubscribe.email) - .where(EmailUnsubscribe.email.isin(all_ids) & - ( - ( - (EmailUnsubscribe.reference_doctype == self.reference_doctype) & (EmailUnsubscribe.reference_name == self.reference_name) - ) | EmailUnsubscribe.global_unsubscribe == 1 - ) - ).distinct() - ).run(pluck=True) + EmailUnsubscribe = DocType("Email Unsubscribe") + + unsubscribed = ( + frappe.qb.from_(EmailUnsubscribe).select( + EmailUnsubscribe.email + ).where( + EmailUnsubscribe.email.isin(all_ids) + & ( + ( + (EmailUnsubscribe.reference_doctype == self.reference_doctype) + & (EmailUnsubscribe.reference_name == self.reference_name) + ) | ( + EmailUnsubscribe.global_unsubscribe == 1 + ) + ) + ).distinct() + ).run(pluck=True) + self._unsubscribed_user_emails = unsubscribed or [] return self._unsubscribed_user_emails diff --git a/frappe/website/utils.py b/frappe/website/utils.py index d4d0cead63..909d9678bb 100644 --- a/frappe/website/utils.py +++ b/frappe/website/utils.py @@ -5,6 +5,7 @@ import mimetypes import os import re from functools import wraps +from typing import Dict, Optional import yaml from six import iteritems @@ -453,7 +454,7 @@ def cache_html(func): return cache_html_decorator -def build_response(path, data, http_status_code, headers=None): +def build_response(path, data, http_status_code, headers: Optional[Dict] = None): # build response response = Response() response.data = set_content_type(response, data, path)