From c2c858f70e8aabf5c23b8922130b98d7d4e22fb7 Mon Sep 17 00:00:00 2001 From: shadrak gurupnor Date: Thu, 24 Feb 2022 09:49:43 +0530 Subject: [PATCH 1/8] fix: clean up logs job was broken --- frappe/core/doctype/activity_log/activity_log.py | 3 ++- frappe/core/doctype/log_settings/log_settings.py | 3 ++- frappe/email/queue.py | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/frappe/core/doctype/activity_log/activity_log.py b/frappe/core/doctype/activity_log/activity_log.py index 69565a2c2a..92a23dcc04 100644 --- a/frappe/core/doctype/activity_log/activity_log.py +++ b/frappe/core/doctype/activity_log/activity_log.py @@ -48,6 +48,7 @@ def clear_activity_logs(days=None): if not days: days = 90 doctype = DocType("Activity Log") + duration = (Now() - Interval(days=days)) frappe.db.delete(doctype, filters=( - doctype.creation < PseudoColumn(f"({Now() - Interval(days=days)})") + doctype.creation < duration )) \ No newline at end of file diff --git a/frappe/core/doctype/log_settings/log_settings.py b/frappe/core/doctype/log_settings/log_settings.py index 5c9bc6c265..8dcf5f4ade 100644 --- a/frappe/core/doctype/log_settings/log_settings.py +++ b/frappe/core/doctype/log_settings/log_settings.py @@ -18,8 +18,9 @@ class LogSettings(Document): def clear_error_logs(self): table = DocType("Error Log") + duration = (Now() - Interval(days=self.clear_error_log_after)) frappe.db.delete(table, filters=( - table.creation < PseudoColumn(f"({Now() - Interval(days=self.clear_error_log_after)})") + table.creation < duration )) def clear_activity_logs(self): diff --git a/frappe/email/queue.py b/frappe/email/queue.py index 16e3fecf48..79c9aa7e03 100755 --- a/frappe/email/queue.py +++ b/frappe/email/queue.py @@ -170,7 +170,7 @@ def clear_outbox(days=None): days=31 email_queues = frappe.db.sql_list("""SELECT `name` FROM `tabEmail Queue` - WHERE `priority`=0 AND `modified` < (NOW() - INTERVAL '{0}' DAY)""".format(days)) + WHERE `modified` < (NOW() - INTERVAL '{0}' DAY)""".format(days)) if email_queues: frappe.db.delete("Email Queue", {"name": ("in", email_queues)}) From 75583bf692f84c4b74ce5088c1191602c044ff0c Mon Sep 17 00:00:00 2001 From: shadrak gurupnor Date: Thu, 24 Feb 2022 18:02:15 +0530 Subject: [PATCH 2/8] fix: removed redundant pieces & rewrote the query with qb --- frappe/core/doctype/activity_log/activity_log.py | 3 +-- frappe/core/doctype/log_settings/log_settings.py | 4 +--- frappe/email/queue.py | 11 +++++++++-- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/frappe/core/doctype/activity_log/activity_log.py b/frappe/core/doctype/activity_log/activity_log.py index 92a23dcc04..0a02b45d32 100644 --- a/frappe/core/doctype/activity_log/activity_log.py +++ b/frappe/core/doctype/activity_log/activity_log.py @@ -48,7 +48,6 @@ def clear_activity_logs(days=None): if not days: days = 90 doctype = DocType("Activity Log") - duration = (Now() - Interval(days=days)) frappe.db.delete(doctype, filters=( - doctype.creation < duration + doctype.creation < (Now() - Interval(days=days)) )) \ No newline at end of file diff --git a/frappe/core/doctype/log_settings/log_settings.py b/frappe/core/doctype/log_settings/log_settings.py index 8dcf5f4ade..dec33070c0 100644 --- a/frappe/core/doctype/log_settings/log_settings.py +++ b/frappe/core/doctype/log_settings/log_settings.py @@ -7,7 +7,6 @@ from frappe import _ from frappe.model.document import Document from frappe.query_builder import DocType, Interval from frappe.query_builder.functions import Now -from pypika.terms import PseudoColumn class LogSettings(Document): @@ -18,9 +17,8 @@ class LogSettings(Document): def clear_error_logs(self): table = DocType("Error Log") - duration = (Now() - Interval(days=self.clear_error_log_after)) frappe.db.delete(table, filters=( - table.creation < duration + table.creation < (Now() - Interval(days=self.clear_error_log_after)) )) def clear_activity_logs(self): diff --git a/frappe/email/queue.py b/frappe/email/queue.py index 79c9aa7e03..629b23b601 100755 --- a/frappe/email/queue.py +++ b/frappe/email/queue.py @@ -5,6 +5,8 @@ import frappe from frappe import msgprint, _ from frappe.utils.verified_command import get_signed_params, verify_request from frappe.utils import get_url, now_datetime, cint +from frappe.query_builder import DocType, Interval +from frappe.query_builder.functions import Now def get_emails_sent_this_month(email_account=None): """Get count of emails sent from a specific email account. @@ -169,8 +171,13 @@ def clear_outbox(days=None): if not days: days=31 - email_queues = frappe.db.sql_list("""SELECT `name` FROM `tabEmail Queue` - WHERE `modified` < (NOW() - INTERVAL '{0}' DAY)""".format(days)) + email_queue = DocType("Email Queue") + queues = (frappe.qb.from_(email_queue) + .select(email_queue.name) + .where(email_queue.modified < (Now() - Interval(days=days))) + .run(as_dict=True)) + + email_queues = [queue.name for queue in queues] if email_queues: frappe.db.delete("Email Queue", {"name": ("in", email_queues)}) From 62eca433d7816e570bdfc3fe4eb6dad245f963de Mon Sep 17 00:00:00 2001 From: shadrak gurupnor Date: Sat, 26 Feb 2022 11:05:52 +0530 Subject: [PATCH 3/8] fix: added test cases for log settings --- .../doctype/log_settings/test_log_settings.py | 129 +++++++++++++++++- 1 file changed, 126 insertions(+), 3 deletions(-) diff --git a/frappe/core/doctype/log_settings/test_log_settings.py b/frappe/core/doctype/log_settings/test_log_settings.py index 40287948fd..f3021c3e1a 100644 --- a/frappe/core/doctype/log_settings/test_log_settings.py +++ b/frappe/core/doctype/log_settings/test_log_settings.py @@ -1,8 +1,131 @@ # -*- coding: utf-8 -*- -# Copyright (c) 2020, Frappe Technologies and Contributors +# Copyright (c) 2022, Frappe Technologies and Contributors # License: MIT. See LICENSE -# import frappe +import frappe import unittest +current = frappe.utils.now_datetime() +past = frappe.utils.add_to_date(current, days=-4) class TestLogSettings(unittest.TestCase): - pass + @classmethod + def setUpClass(cls): + fieldnames = ['clear_error_log_after', 'clear_activity_log_after', 'clear_email_queue_after'] + for fieldname in fieldnames: + frappe.set_value("Log Settings", None, fieldname, 1) + + @classmethod + def tearDownClass(cls): + if frappe.db.exists({"doctype": "Activity Log", "subject": "Test subject"}): + activity_logs = frappe.get_all("Activity Log", filters=dict(subject='Test subject'), pluck='name') + for log in activity_logs: + frappe.db.delete("Activity Log", log) + frappe.db.commit() + + if frappe.db.exists({"doctype": "Email Queue", "expose_recipients": "test@receiver.com"}): + email_queues = frappe.get_all("Email Queue", filters=dict(expose_recipients='test@receiver.com'), pluck='name') + for queue in email_queues: + frappe.db.delete("Email Queue", queue) + frappe.db.commit() + + if frappe.db.exists({"doctype": "Error Log", "method": "test_method"}): + error_logs = frappe.get_all("Error Log", filters=dict(method='test_method'), pluck='name') + for log in error_logs: + frappe.db.delete("Error Log", log) + frappe.db.commit() + + def test_create_activity_logs(self): + doc1 = frappe.get_doc({ + "doctype": "Activity Log", + "subject": "Test subject", + "full_name": "test user1", + }) + doc1.insert(ignore_permissions=True) + + #creation can't be set while inserting new_doc + frappe.db.set_value("Activity Log", doc1.name, "creation", past) + + doc2 = frappe.get_doc({ + "doctype": "Activity Log", + "subject": "Test subject", + "full_name": "test user2", + "creation": current + }) + doc2.insert(ignore_permissions=True) + + activity_logs = frappe.get_all("Activity Log", filters=dict(subject='Test subject'), pluck='name') + + self.assertEqual(len(activity_logs), 2) + + def test_create_error_logs(self): + traceback = """ + Traceback (most recent call last): + File "apps/frappe/frappe/email/doctype/email_account/email_account.py", line 489, in get_inbound_mails + messages = email_server.get_messages() + File "apps/frappe/frappe/email/receive.py", line 166, in get_messages + if self.has_login_limit_exceeded(e): + File "apps/frappe/frappe/email/receive.py", line 315, in has_login_limit_exceeded + return "-ERR Exceeded the login limit" in strip(cstr(e.message)) + AttributeError: 'AttributeError' object has no attribute 'message' + """ + doc1 = frappe.get_doc({ + "doctype": "Error Log", + "method": "test_method", + "error": traceback, + "creation": past + }) + doc1.insert(ignore_permissions=True) + + frappe.db.set_value("Error Log", doc1.name, "creation", past) + + doc2 = frappe.get_doc({ + "doctype": "Error Log", + "method": "test_method", + "error": traceback, + "creation": current + }) + doc2.insert(ignore_permissions=True) + + error_logs = frappe.get_all("Error Log", filters=dict(method='test_method'), pluck='name') + self.assertEqual(len(error_logs), 2) + + def test_create_email_queue(self): + doc1 = frappe.get_doc({ + "doctype": "Email Queue", + "sender": "test1@example.com", + "message": "This is a test email1", + "priority": 1, + "expose_recipients": "test@receiver.com", + }) + doc1.insert(ignore_permissions=True) + + frappe.db.set_value("Email Queue", doc1.name, "creation", past) + frappe.db.set_value("Email Queue", doc1.name, "modified", past, update_modified=False) + + doc2 = frappe.get_doc({ + "doctype": "Email Queue", + "sender": "test2@example.com", + "message": "This is a test email2", + "priority": 1, + "expose_recipients": "test@receiver.com", + "creation": current + }) + doc2.insert(ignore_permissions=True) + + email_queues = frappe.get_all("Email Queue", filters=dict(expose_recipients="test@receiver.com"), pluck='name') + + self.assertEqual(len(email_queues), 2) + + def test_delete_logs(self): + from frappe.core.doctype.log_settings.log_settings import run_log_clean_up + + run_log_clean_up() + + activity_logs = frappe.get_all("Activity Log", filters=dict(subject='Test subject'), pluck='name') + self.assertEqual(len(activity_logs), 1) + + error_logs = frappe.get_all("Error Log", filters=dict(method='test_method'), pluck='name') + self.assertEqual(len(error_logs), 1) + + email_queues = frappe.get_all("Email Queue", filters=dict(expose_recipients='test@receiver.com'), pluck='name') + self.assertEqual(len(email_queues), 1) + From 05a658c86ed243cd4c61a4b0b3fabc38c349de3d Mon Sep 17 00:00:00 2001 From: shadrak gurupnor Date: Sat, 26 Feb 2022 11:08:36 +0530 Subject: [PATCH 4/8] chore: fix linter issues --- frappe/core/doctype/log_settings/test_log_settings.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/frappe/core/doctype/log_settings/test_log_settings.py b/frappe/core/doctype/log_settings/test_log_settings.py index f3021c3e1a..60ee75e5dc 100644 --- a/frappe/core/doctype/log_settings/test_log_settings.py +++ b/frappe/core/doctype/log_settings/test_log_settings.py @@ -19,19 +19,16 @@ class TestLogSettings(unittest.TestCase): activity_logs = frappe.get_all("Activity Log", filters=dict(subject='Test subject'), pluck='name') for log in activity_logs: frappe.db.delete("Activity Log", log) - frappe.db.commit() if frappe.db.exists({"doctype": "Email Queue", "expose_recipients": "test@receiver.com"}): email_queues = frappe.get_all("Email Queue", filters=dict(expose_recipients='test@receiver.com'), pluck='name') for queue in email_queues: frappe.db.delete("Email Queue", queue) - frappe.db.commit() if frappe.db.exists({"doctype": "Error Log", "method": "test_method"}): error_logs = frappe.get_all("Error Log", filters=dict(method='test_method'), pluck='name') for log in error_logs: frappe.db.delete("Error Log", log) - frappe.db.commit() def test_create_activity_logs(self): doc1 = frappe.get_doc({ From ee355b9cd6b0efa836eba3fa6ba1db755970f319 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 23 Mar 2022 13:09:36 +0530 Subject: [PATCH 5/8] test(log_settings): test_delete_logs * Refactored TestCase to test "correctly" * Got rid of earlier flaky logic * Dropped unwanted tests --- .../doctype/log_settings/test_log_settings.py | 206 ++++++++---------- 1 file changed, 92 insertions(+), 114 deletions(-) diff --git a/frappe/core/doctype/log_settings/test_log_settings.py b/frappe/core/doctype/log_settings/test_log_settings.py index 60ee75e5dc..67574314a3 100644 --- a/frappe/core/doctype/log_settings/test_log_settings.py +++ b/frappe/core/doctype/log_settings/test_log_settings.py @@ -1,128 +1,106 @@ -# -*- coding: utf-8 -*- # Copyright (c) 2022, Frappe Technologies and Contributors # License: MIT. See LICENSE -import frappe + +from datetime import datetime import unittest -current = frappe.utils.now_datetime() -past = frappe.utils.add_to_date(current, days=-4) +import frappe +from frappe.utils import now_datetime, add_to_date +from frappe.core.doctype.log_settings.log_settings import run_log_clean_up + + class TestLogSettings(unittest.TestCase): @classmethod def setUpClass(cls): - fieldnames = ['clear_error_log_after', 'clear_activity_log_after', 'clear_email_queue_after'] - for fieldname in fieldnames: - frappe.set_value("Log Settings", None, fieldname, 1) - @classmethod - def tearDownClass(cls): - if frappe.db.exists({"doctype": "Activity Log", "subject": "Test subject"}): - activity_logs = frappe.get_all("Activity Log", filters=dict(subject='Test subject'), pluck='name') - for log in activity_logs: - frappe.db.delete("Activity Log", log) - - if frappe.db.exists({"doctype": "Email Queue", "expose_recipients": "test@receiver.com"}): - email_queues = frappe.get_all("Email Queue", filters=dict(expose_recipients='test@receiver.com'), pluck='name') - for queue in email_queues: - frappe.db.delete("Email Queue", queue) - - if frappe.db.exists({"doctype": "Error Log", "method": "test_method"}): - error_logs = frappe.get_all("Error Log", filters=dict(method='test_method'), pluck='name') - for log in error_logs: - frappe.db.delete("Error Log", log) - - def test_create_activity_logs(self): - doc1 = frappe.get_doc({ - "doctype": "Activity Log", - "subject": "Test subject", - "full_name": "test user1", - }) - doc1.insert(ignore_permissions=True) - - #creation can't be set while inserting new_doc - frappe.db.set_value("Activity Log", doc1.name, "creation", past) - - doc2 = frappe.get_doc({ - "doctype": "Activity Log", - "subject": "Test subject", - "full_name": "test user2", - "creation": current - }) - doc2.insert(ignore_permissions=True) - - activity_logs = frappe.get_all("Activity Log", filters=dict(subject='Test subject'), pluck='name') - - self.assertEqual(len(activity_logs), 2) - - def test_create_error_logs(self): - traceback = """ - Traceback (most recent call last): - File "apps/frappe/frappe/email/doctype/email_account/email_account.py", line 489, in get_inbound_mails - messages = email_server.get_messages() - File "apps/frappe/frappe/email/receive.py", line 166, in get_messages - if self.has_login_limit_exceeded(e): - File "apps/frappe/frappe/email/receive.py", line 315, in has_login_limit_exceeded - return "-ERR Exceeded the login limit" in strip(cstr(e.message)) - AttributeError: 'AttributeError' object has no attribute 'message' - """ - doc1 = frappe.get_doc({ - "doctype": "Error Log", - "method": "test_method", - "error": traceback, - "creation": past - }) - doc1.insert(ignore_permissions=True) - - frappe.db.set_value("Error Log", doc1.name, "creation", past) - - doc2 = frappe.get_doc({ - "doctype": "Error Log", - "method": "test_method", - "error": traceback, - "creation": current - }) - doc2.insert(ignore_permissions=True) - - error_logs = frappe.get_all("Error Log", filters=dict(method='test_method'), pluck='name') - self.assertEqual(len(error_logs), 2) - - def test_create_email_queue(self): - doc1 = frappe.get_doc({ - "doctype": "Email Queue", - "sender": "test1@example.com", - "message": "This is a test email1", - "priority": 1, - "expose_recipients": "test@receiver.com", - }) - doc1.insert(ignore_permissions=True) - - frappe.db.set_value("Email Queue", doc1.name, "creation", past) - frappe.db.set_value("Email Queue", doc1.name, "modified", past, update_modified=False) - - doc2 = frappe.get_doc({ - "doctype": "Email Queue", - "sender": "test2@example.com", - "message": "This is a test email2", - "priority": 1, - "expose_recipients": "test@receiver.com", - "creation": current - }) - doc2.insert(ignore_permissions=True) - - email_queues = frappe.get_all("Email Queue", filters=dict(expose_recipients="test@receiver.com"), pluck='name') - - self.assertEqual(len(email_queues), 2) + cls.savepoint = "TestLogSettings" + frappe.db.savepoint(cls.savepoint) - def test_delete_logs(self): - from frappe.core.doctype.log_settings.log_settings import run_log_clean_up + frappe.db.set_single_value( + "Log Settings", + { + "clear_error_log_after": 1, + "clear_activity_log_after": 1, + "clear_email_queue_after": 1, + }, + ) - run_log_clean_up() + @classmethod + def tearDownClass(cls): + frappe.db.rollback(save_point=cls.savepoint) - activity_logs = frappe.get_all("Activity Log", filters=dict(subject='Test subject'), pluck='name') - self.assertEqual(len(activity_logs), 1) + def setUp(self) -> None: + if self._testMethodName == "test_delete_logs": + self.datetime = frappe._dict() + self.datetime.current = now_datetime() + self.datetime.past = add_to_date(self.datetime.current, days=-4) + setup_test_logs(self.datetime.past) - error_logs = frappe.get_all("Error Log", filters=dict(method='test_method'), pluck='name') - self.assertEqual(len(error_logs), 1) + def tearDown(self) -> None: + if self._testMethodName == "test_delete_logs": + del self.datetime - email_queues = frappe.get_all("Email Queue", filters=dict(expose_recipients='test@receiver.com'), pluck='name') - self.assertEqual(len(email_queues), 1) + def test_delete_logs(self): + # make sure test data is present + activity_log_count = frappe.db.count( + "Activity Log", {"creation": ("<=", self.datetime.past)} + ) + error_log_count = frappe.db.count( + "Error Log", {"creation": ("<=", self.datetime.past)} + ) + email_queue_count = frappe.db.count( + "Email Queue", {"creation": ("<=", self.datetime.past)} + ) + + self.assertNotEqual(activity_log_count, 0) + self.assertNotEqual(error_log_count, 0) + self.assertNotEqual(email_queue_count, 0) + + # run clean up job + run_log_clean_up() + # test if logs are deleted + activity_log_count = frappe.db.count( + "Activity Log", {"creation": ("<", self.datetime.past)} + ) + error_log_count = frappe.db.count( + "Error Log", {"creation": ("<", self.datetime.past)} + ) + email_queue_count = frappe.db.count( + "Email Queue", {"creation": ("<", self.datetime.past)} + ) + + self.assertEqual(activity_log_count, 0) + self.assertEqual(error_log_count, 0) + self.assertEqual(email_queue_count, 0) + + +def setup_test_logs(past: datetime) -> None: + activity_log = frappe.get_doc( + { + "doctype": "Activity Log", + "subject": "Test subject", + "full_name": "test user2", + } + ).insert(ignore_permissions=True) + activity_log.db_set("creation", past) + + error_log = frappe.get_doc( + { + "doctype": "Error Log", + "method": "test_method", + "error": "traceback", + } + ).insert(ignore_permissions=True) + error_log.db_set("creation", past) + + doc1 = frappe.get_doc( + { + "doctype": "Email Queue", + "sender": "test1@example.com", + "message": "This is a test email1", + "priority": 1, + "expose_recipients": "test@receiver.com", + } + ).insert(ignore_permissions=True) + doc1.db_set("creation", past) From 85c7057ae4a15d1d4bd0c3a04c2d3449b5fdd17a Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 23 Mar 2022 13:11:17 +0530 Subject: [PATCH 6/8] fix(activity_log): Remove unused import from namespace --- frappe/core/doctype/activity_log/activity_log.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/frappe/core/doctype/activity_log/activity_log.py b/frappe/core/doctype/activity_log/activity_log.py index 0a02b45d32..70d4ca3ffe 100644 --- a/frappe/core/doctype/activity_log/activity_log.py +++ b/frappe/core/doctype/activity_log/activity_log.py @@ -1,15 +1,14 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2017, Frappe Technologies and contributors +# Copyright (c) 2022, Frappe Technologies and contributors # License: MIT. See LICENSE +import frappe from frappe import _ -from frappe.utils import get_fullname, now -from frappe.model.document import Document from frappe.core.utils import set_timeline_doc -import frappe +from frappe.model.document import Document from frappe.query_builder import DocType, Interval from frappe.query_builder.functions import Now -from pypika.terms import PseudoColumn +from frappe.utils import get_fullname, now + class ActivityLog(Document): def before_insert(self): @@ -50,4 +49,4 @@ def clear_activity_logs(days=None): doctype = DocType("Activity Log") frappe.db.delete(doctype, filters=( doctype.creation < (Now() - Interval(days=days)) - )) \ No newline at end of file + )) From 300227ba71d4d2b88a7fee4cfb3b7fe327227e49 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 23 Mar 2022 13:11:55 +0530 Subject: [PATCH 7/8] fix(set_single_value): Make value parameter optional Similar to set_value for accepting multiple columns ot be updated for the same Table through a Dict as the second positional arg Misc: Added type hints --- frappe/database/database.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frappe/database/database.py b/frappe/database/database.py index 1251a323d3..b20ca79298 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -10,7 +10,7 @@ import re import string from contextlib import contextmanager from time import time -from typing import Dict, List, Tuple, Union +from typing import Dict, List, Optional, Tuple, Union from pypika.terms import Criterion, NullValue, PseudoColumn @@ -556,7 +556,7 @@ class Database(object): def get_list(*args, **kwargs): return frappe.get_list(*args, **kwargs) - def set_single_value(self, doctype, fieldname, value, *args, **kwargs): + def set_single_value(self, doctype: str, fieldname: Union[str, Dict], value: Optional[Union[str, int]] = None, *args, **kwargs): """Set field value of Single DocType. :param doctype: DocType of the single object From b79d55c5d3e0fcc1cd4ede664ba3c0df58829400 Mon Sep 17 00:00:00 2001 From: Gavin D'souza Date: Wed, 23 Mar 2022 13:16:57 +0530 Subject: [PATCH 8/8] refactor(minor): clear_outbox * Use pluck API instead of building dict and then accesing keys * Styled query * Added type hints --- frappe/email/queue.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/frappe/email/queue.py b/frappe/email/queue.py index 629b23b601..f6f52e79e2 100755 --- a/frappe/email/queue.py +++ b/frappe/email/queue.py @@ -1,4 +1,4 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors # License: MIT. See LICENSE import frappe @@ -164,20 +164,16 @@ def get_queue(): by priority desc, creation asc limit 500''', { 'now': now_datetime() }, as_dict=True) -def clear_outbox(days=None): +def clear_outbox(days: int = None) -> None: """Remove low priority older than 31 days in Outbox or configured in Log Settings. Note: Used separate query to avoid deadlock """ - if not days: - days=31 - + days = days or 31 email_queue = DocType("Email Queue") - queues = (frappe.qb.from_(email_queue) - .select(email_queue.name) - .where(email_queue.modified < (Now() - Interval(days=days))) - .run(as_dict=True)) - email_queues = [queue.name for queue in queues] + email_queues = frappe.qb.from_(email_queue).select(email_queue.name).where( + email_queue.modified < (Now() - Interval(days=days)) + ).run(pluck=True) if email_queues: frappe.db.delete("Email Queue", {"name": ("in", email_queues)})