From 738b39e5e79b0b58b5bd6e56a3bccc0052c192ba Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 12 Jan 2026 15:35:00 +0000 Subject: [PATCH 1/3] Initial plan From f38c721180b2a467e5329ec6048960e984ae9ca5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 13 Jan 2026 10:40:00 +0000 Subject: [PATCH 2/3] Fix N+1 query issues in get_linked_todos and get_linked_events Co-authored-by: mrrobot47 <25586785+mrrobot47@users.noreply.github.com> --- next_crm/api/activities.py | 100 +++++++++++++++++++++++++------------ 1 file changed, 69 insertions(+), 31 deletions(-) diff --git a/next_crm/api/activities.py b/next_crm/api/activities.py index 4d6c2e37c1..f1373ae458 100644 --- a/next_crm/api/activities.py +++ b/next_crm/api/activities.py @@ -560,38 +560,58 @@ def get_linked_todos(name): fields=fields, ) + if not todos: + return [] + + # Collect all linked event names + event_names = [ + todo["custom_linked_event"] + for todo in todos + if todo.get("custom_linked_event") + ] + + # Batch fetch events + events_map = {} + if event_names: + events = frappe.db.get_all( + "Event", + filters={"name": ["in", event_names]}, + fields=["name", "sync_with_google_calendar", "google_calendar"], + ) + events_map = {e["name"]: e for e in events} + + # Batch fetch all event participants + participants_map = {} + if event_names: + all_participants = frappe.db.get_all( + "Event Participants", + filters={"parent": ["in", event_names]}, + fields=["parent", "reference_doctype", "reference_docname", "email"], + ) + for p in all_participants: + if p["parent"] not in participants_map: + participants_map[p["parent"]] = [] + participants_map[p["parent"]].append({ + "reference_doctype": p["reference_doctype"], + "reference_docname": p["reference_docname"], + "email": p["email"], + }) + + # Apply to todos for todo in todos: - if todo.get("custom_linked_event", None): - event = frappe.db.get_value( - "Event", - todo["custom_linked_event"], - ["name", "sync_with_google_calendar", "google_calendar"], - ) - if not event: - continue + linked_event = todo.get("custom_linked_event") + if linked_event and linked_event in events_map: + event = events_map[linked_event] todo["_event"] = { - "name": event[0], - "sync_with_google_calendar": event[1], - "google_calendar": event[2], + "name": event["name"], + "sync_with_google_calendar": event["sync_with_google_calendar"], + "google_calendar": event["google_calendar"], + "event_participants": participants_map.get(event["name"], []), } - event_participants = frappe.db.get_all( - "Event Participants", - filters={"parent": todo["_event"]["name"]}, - fields=["reference_doctype", "reference_docname", "email"], - ) - event_participants = [ - { - "reference_doctype": participant["reference_doctype"], - "reference_docname": participant["reference_docname"], - "email": participant["email"], - } - for participant in event_participants - ] - todo["_event"]["event_participants"] = event_participants else: todo["_event"] = None - return todos or [] + return todos def get_linked_events(name): @@ -614,14 +634,32 @@ def get_linked_events(name): ], ) - for event in events: - event["event_participants"] = frappe.db.get_all( + if not events: + return [] + + # Batch fetch all event participants + event_names = [event.name for event in events] + participants_map = {} + if event_names: + all_participants = frappe.db.get_all( "Event Participants", - filters={"parent": event.name}, - fields=["reference_doctype", "reference_docname", "email"], + filters={"parent": ["in", event_names]}, + fields=["parent", "reference_doctype", "reference_docname", "email"], ) + for p in all_participants: + if p["parent"] not in participants_map: + participants_map[p["parent"]] = [] + participants_map[p["parent"]].append({ + "reference_doctype": p["reference_doctype"], + "reference_docname": p["reference_docname"], + "email": p["email"], + }) + + # Apply to events + for event in events: + event["event_participants"] = participants_map.get(event.name, []) - return events or [] + return events def get_linked_opportunities(doctype, name): From 4355547fe5af1b8ece53ea01b2d1955acad214d0 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 13 Jan 2026 10:40:46 +0000 Subject: [PATCH 3/3] Use sets to avoid duplicate event names in queries Co-authored-by: mrrobot47 <25586785+mrrobot47@users.noreply.github.com> --- next_crm/api/activities.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/next_crm/api/activities.py b/next_crm/api/activities.py index f1373ae458..fc7db63597 100644 --- a/next_crm/api/activities.py +++ b/next_crm/api/activities.py @@ -563,12 +563,12 @@ def get_linked_todos(name): if not todos: return [] - # Collect all linked event names - event_names = [ + # Collect all unique linked event names + event_names = list({ todo["custom_linked_event"] for todo in todos if todo.get("custom_linked_event") - ] + }) # Batch fetch events events_map = {} @@ -638,7 +638,7 @@ def get_linked_events(name): return [] # Batch fetch all event participants - event_names = [event.name for event in events] + event_names = list({event.name for event in events}) participants_map = {} if event_names: all_participants = frappe.db.get_all(