Skip to content

Commit 09f6d7b

Browse files
committed
refac
1 parent 6746959 commit 09f6d7b

3 files changed

Lines changed: 53 additions & 6 deletions

File tree

backend/open_webui/models/automations.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,10 @@ class AutomationRun(Base):
4545
error = Column(Text, nullable=True)
4646
created_at = Column(BigInteger, nullable=False)
4747

48-
__table_args__ = (Index('ix_automation_run_automation_id', 'automation_id'),)
48+
__table_args__ = (
49+
Index('ix_automation_run_automation_id', 'automation_id'),
50+
Index('ix_automation_run_aid_created', 'automation_id', 'created_at'),
51+
)
4952

5053

5154
####################
@@ -308,6 +311,37 @@ def get_latest(self, automation_id: str, db: Optional[Session] = None) -> Option
308311
)
309312
return AutomationRunModel.model_validate(row) if row else None
310313

314+
def get_latest_batch(
315+
self, automation_ids: list[str], db: Optional[Session] = None
316+
) -> dict[str, AutomationRunModel]:
317+
"""Fetch the latest run for each automation in a single query."""
318+
if not automation_ids:
319+
return {}
320+
with get_db_context(db) as db:
321+
# Subquery: max created_at per automation_id
322+
subq = (
323+
db.query(
324+
AutomationRun.automation_id,
325+
func.max(AutomationRun.created_at).label('max_created'),
326+
)
327+
.filter(AutomationRun.automation_id.in_(automation_ids))
328+
.group_by(AutomationRun.automation_id)
329+
.subquery()
330+
)
331+
rows = (
332+
db.query(AutomationRun)
333+
.join(
334+
subq,
335+
(AutomationRun.automation_id == subq.c.automation_id)
336+
& (AutomationRun.created_at == subq.c.max_created),
337+
)
338+
.all()
339+
)
340+
return {
341+
row.automation_id: AutomationRunModel.model_validate(row)
342+
for row in rows
343+
}
344+
311345
def get_by_automation(
312346
self,
313347
automation_id: str,

backend/open_webui/routers/automations.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ def check_automation_access(automation, user):
6161

6262

6363
def enrich_automation(automation: AutomationModel, db: Session, tz: str = None) -> AutomationResponse:
64+
"""Full enrichment for single-item views (includes next_runs computation)."""
6465
last_run = AutomationRuns.get_latest(automation.id, db=db)
6566
return AutomationResponse(
6667
**automation.model_dump(),
@@ -97,8 +98,18 @@ async def get_automation_items(
9798
db=db,
9899
)
99100

101+
# Batch-fetch latest runs in a single query instead of N+1
102+
ids = [item.id for item in result.items]
103+
latest_runs = AutomationRuns.get_latest_batch(ids, db=db) if ids else {}
104+
100105
return {
101-
'items': [enrich_automation(item, db, tz=user.timezone) for item in result.items],
106+
'items': [
107+
AutomationResponse(
108+
**item.model_dump(),
109+
last_run=latest_runs.get(item.id),
110+
)
111+
for item in result.items
112+
],
102113
'total': result.total,
103114
}
104115

src/routes/(app)/automations/+page.svelte

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@
4747
4848
let page = 1;
4949
50-
// Debounce only query changes
51-
$: if (query !== undefined) {
50+
// Debounce only query changes (gate behind loaded to prevent double-fetch on mount)
51+
$: if (loaded && query !== undefined) {
5252
loading = true;
5353
clearTimeout(searchDebounceTimer);
5454
searchDebounceTimer = setTimeout(() => {
@@ -57,8 +57,8 @@
5757
}, 300);
5858
}
5959
60-
// Immediate response to page/filter changes
61-
$: if (page && statusFilter !== undefined) {
60+
// Immediate response to page/filter changes (gate behind loaded)
61+
$: if (loaded && page && statusFilter !== undefined) {
6262
getAutomationList();
6363
}
6464
@@ -171,6 +171,8 @@
171171
}
172172
173173
loaded = true;
174+
// Explicit initial fetch — reactive blocks will handle subsequent changes
175+
await getAutomationList();
174176
175177
return () => {
176178
clearTimeout(searchDebounceTimer);

0 commit comments

Comments
 (0)