Merge pull request #24862 from ankush/workflow_action_faster

perf: faster workflow action processing
This commit is contained in:
Ankush Menat 2024-02-12 12:51:47 +05:30 committed by GitHub
commit 7b197f1a2b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 24 additions and 51 deletions

View file

@ -21,35 +21,9 @@ class TestWorkflow(FrappeTestCase):
def setUp(self):
self.workflow = create_todo_workflow()
frappe.set_user("Administrator")
if self._testMethodName == "test_if_workflow_actions_were_processed_using_user":
if not frappe.db.has_column("Workflow Action", "user"):
# mariadb would raise this statement would create an implicit commit
# if we do not commit before alter statement
# nosemgrep
frappe.db.commit()
frappe.db.multisql(
{
"mariadb": "ALTER TABLE `tabWorkflow Action` ADD COLUMN user varchar(140)",
"postgres": 'ALTER TABLE "tabWorkflow Action" ADD COLUMN "user" varchar(140)',
}
)
frappe.cache.delete_value("table_columns")
def tearDown(self):
frappe.delete_doc("Workflow", "Test ToDo")
if self._testMethodName == "test_if_workflow_actions_were_processed_using_user":
if frappe.db.has_column("Workflow Action", "user"):
# mariadb would raise this statement would create an implicit commit
# if we do not commit before alter statement
# nosemgrep
frappe.db.commit()
frappe.db.multisql(
{
"mariadb": "ALTER TABLE `tabWorkflow Action` DROP COLUMN user",
"postgres": 'ALTER TABLE "tabWorkflow Action" DROP COLUMN "user"',
}
)
frappe.cache.delete_value("table_columns")
def test_default_condition(self):
"""test default condition is set"""

View file

@ -108,15 +108,17 @@ def process_workflow_actions(doc, state):
if not next_possible_transitions:
return
user_data_map, roles = get_users_next_action_data(next_possible_transitions, doc)
if not user_data_map:
return
roles = {t.allowed for t in next_possible_transitions}
create_workflow_actions_for_roles(roles, doc)
if send_email_alert(workflow):
enqueue(send_workflow_action_email, queue="short", users_data=list(user_data_map.values()), doc=doc)
enqueue(
send_workflow_action_email,
queue="short",
doc=doc,
transitions=next_possible_transitions,
enqueue_after_commit=True,
)
@frappe.whitelist(allow_guest=True)
@ -321,12 +323,20 @@ def get_next_possible_transitions(workflow_name, state, doc=None):
def get_users_next_action_data(transitions, doc):
roles = set()
user_data_map = {}
@frappe.request_cache
def user_has_permission(user: str) -> bool:
from frappe.permissions import has_permission
return has_permission(doctype=doc, user=user, print_logs=False)
for transition in transitions:
roles.add(transition.allowed)
users = get_users_with_role(transition.allowed)
filtered_users = filter_allowed_users(users, doc, transition)
filtered_users = [
user for user in users if has_approval_access(user, doc, transition) and user_has_permission(user)
]
for user in filtered_users:
if not user_data_map.get(user):
user_data_map[user] = frappe._dict(
@ -344,10 +354,12 @@ def get_users_next_action_data(transitions, doc):
}
)
)
return user_data_map, roles
return user_data_map
def create_workflow_actions_for_roles(roles, doc):
if not roles:
return
workflow_action = frappe.get_doc(
{
"doctype": "Workflow Action",
@ -364,7 +376,8 @@ def create_workflow_actions_for_roles(roles, doc):
workflow_action.insert(ignore_permissions=True)
def send_workflow_action_email(users_data, doc):
def send_workflow_action_email(doc, transitions):
users_data = get_users_next_action_data(transitions, doc)
common_args = get_common_email_args(doc)
message = common_args.pop("message", None)
for d in users_data:
@ -451,20 +464,6 @@ def get_doc_workflow_state(doc):
return doc.get(workflow_state_field)
def filter_allowed_users(users, doc, transition):
"""Filters list of users by checking if user has access to doc and
if the user satisfies 'workflow transision self approval' condition
"""
from frappe.permissions import has_permission
return [
user
for user in users
if has_approval_access(user, doc, transition)
and has_permission(doctype=doc, user=user, print_logs=False)
]
def get_common_email_args(doc):
doctype = doc.get("doctype")
docname = doc.get("name")