fix: exclude ephemeral tasks from stats page queries (#656)
* fix: exclude ephemeral tasks from stats page queries * test: add ephemeral exclusion tests for all stats query functions --------- Co-authored-by: Kandev Agent <agent@kandev.dev>
This commit is contained in:
@@ -53,9 +53,11 @@ func createTestDB(t *testing.T) *sqlx.DB {
|
||||
id TEXT PRIMARY KEY,
|
||||
workspace_id TEXT NOT NULL DEFAULT '',
|
||||
board_id TEXT NOT NULL,
|
||||
workflow_id TEXT NOT NULL DEFAULT '',
|
||||
workflow_step_id TEXT NOT NULL DEFAULT '',
|
||||
title TEXT NOT NULL,
|
||||
state TEXT DEFAULT 'TODO',
|
||||
is_ephemeral INTEGER NOT NULL DEFAULT 0,
|
||||
archived_at TIMESTAMP,
|
||||
created_at TIMESTAMP NOT NULL,
|
||||
updated_at TIMESTAMP NOT NULL
|
||||
@@ -293,3 +295,261 @@ func TestGetGitStats_EmptyWorkspace(t *testing.T) {
|
||||
t.Errorf("expected 0 commits, got %d", stats.TotalCommits)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetGlobalStats_ExcludesEphemeralTasks(t *testing.T) {
|
||||
dbConn := createTestDB(t)
|
||||
repo, err := NewWithDB(dbConn, dbConn)
|
||||
if err != nil {
|
||||
t.Fatalf("NewWithDB failed: %v", err)
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
now := time.Now().UTC()
|
||||
nowStr := now.Format(time.RFC3339)
|
||||
|
||||
// Insert workspace + board + workflow step
|
||||
execOrFatal(t, dbConn, `INSERT INTO workspaces (id, name, created_at, updated_at) VALUES ('ws-1', 'Test', ?, ?)`, nowStr, nowStr)
|
||||
execOrFatal(t, dbConn, `INSERT INTO boards (id, workspace_id, name, created_at, updated_at) VALUES ('board-1', 'ws-1', 'Board', ?, ?)`, nowStr, nowStr)
|
||||
execOrFatal(t, dbConn, `INSERT INTO workflow_steps (id, workflow_id, name, position, created_at, updated_at) VALUES ('step-todo', 'board-1', 'To Do', 0, ?, ?)`, nowStr, nowStr)
|
||||
|
||||
// Insert a regular task
|
||||
execOrFatal(t, dbConn, `INSERT INTO tasks (id, workspace_id, board_id, workflow_step_id, title, is_ephemeral, created_at, updated_at) VALUES ('task-regular', 'ws-1', 'board-1', 'step-todo', 'Regular', 0, ?, ?)`, nowStr, nowStr)
|
||||
|
||||
// Insert an ephemeral (quick chat) task
|
||||
execOrFatal(t, dbConn, `INSERT INTO tasks (id, workspace_id, board_id, workflow_step_id, title, is_ephemeral, created_at, updated_at) VALUES ('task-ephemeral', 'ws-1', 'board-1', '', 'Quick Chat', 1, ?, ?)`, nowStr, nowStr)
|
||||
|
||||
// Add sessions for both tasks
|
||||
execOrFatal(t, dbConn, `INSERT INTO task_sessions (id, task_id, agent_profile_id, state, started_at, updated_at) VALUES ('sess-regular', 'task-regular', 'agent-1', 'COMPLETED', ?, ?)`, nowStr, nowStr)
|
||||
execOrFatal(t, dbConn, `INSERT INTO task_sessions (id, task_id, agent_profile_id, state, started_at, updated_at) VALUES ('sess-ephemeral', 'task-ephemeral', 'agent-1', 'COMPLETED', ?, ?)`, nowStr, nowStr)
|
||||
|
||||
// Add turns for both sessions
|
||||
execOrFatal(t, dbConn, `INSERT INTO task_session_turns (id, task_session_id, task_id, started_at, completed_at, created_at, updated_at) VALUES ('turn-regular', 'sess-regular', 'task-regular', ?, ?, ?, ?)`, nowStr, nowStr, nowStr, nowStr)
|
||||
execOrFatal(t, dbConn, `INSERT INTO task_session_turns (id, task_session_id, task_id, started_at, completed_at, created_at, updated_at) VALUES ('turn-ephemeral', 'sess-ephemeral', 'task-ephemeral', ?, ?, ?, ?)`, nowStr, nowStr, nowStr, nowStr)
|
||||
|
||||
// Global stats should only count the regular task
|
||||
stats, err := repo.GetGlobalStats(ctx, "ws-1", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("GetGlobalStats failed: %v", err)
|
||||
}
|
||||
if stats.TotalTasks != 1 {
|
||||
t.Errorf("expected 1 total task (ephemeral excluded), got %d", stats.TotalTasks)
|
||||
}
|
||||
if stats.TotalSessions != 1 {
|
||||
t.Errorf("expected 1 total session (ephemeral excluded), got %d", stats.TotalSessions)
|
||||
}
|
||||
if stats.TotalTurns != 1 {
|
||||
t.Errorf("expected 1 total turn (ephemeral excluded), got %d", stats.TotalTurns)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetTaskStats_ExcludesEphemeralTasks(t *testing.T) {
|
||||
dbConn := createTestDB(t)
|
||||
repo, err := NewWithDB(dbConn, dbConn)
|
||||
if err != nil {
|
||||
t.Fatalf("NewWithDB failed: %v", err)
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
now := time.Now().UTC()
|
||||
nowStr := now.Format(time.RFC3339)
|
||||
|
||||
execOrFatal(t, dbConn, `INSERT INTO workspaces (id, name, created_at, updated_at) VALUES ('ws-1', 'Test', ?, ?)`, nowStr, nowStr)
|
||||
execOrFatal(t, dbConn, `INSERT INTO boards (id, workspace_id, name, created_at, updated_at) VALUES ('board-1', 'ws-1', 'Board', ?, ?)`, nowStr, nowStr)
|
||||
|
||||
// Insert regular and ephemeral tasks
|
||||
execOrFatal(t, dbConn, `INSERT INTO tasks (id, workspace_id, board_id, workflow_step_id, title, is_ephemeral, created_at, updated_at) VALUES ('task-regular', 'ws-1', 'board-1', '', 'Regular', 0, ?, ?)`, nowStr, nowStr)
|
||||
execOrFatal(t, dbConn, `INSERT INTO tasks (id, workspace_id, board_id, workflow_step_id, title, is_ephemeral, created_at, updated_at) VALUES ('task-ephemeral', 'ws-1', 'board-1', '', 'Quick Chat', 1, ?, ?)`, nowStr, nowStr)
|
||||
|
||||
results, err := repo.GetTaskStats(ctx, "ws-1", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("GetTaskStats failed: %v", err)
|
||||
}
|
||||
if len(results) != 1 {
|
||||
t.Fatalf("expected 1 task stat (ephemeral excluded), got %d", len(results))
|
||||
}
|
||||
if results[0].TaskID != "task-regular" {
|
||||
t.Errorf("expected task-regular, got %s", results[0].TaskID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetDailyActivity_ExcludesEphemeralTasks(t *testing.T) {
|
||||
dbConn := createTestDB(t)
|
||||
repo, err := NewWithDB(dbConn, dbConn)
|
||||
if err != nil {
|
||||
t.Fatalf("NewWithDB failed: %v", err)
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
now := time.Now().UTC()
|
||||
nowStr := now.Format(time.RFC3339)
|
||||
|
||||
execOrFatal(t, dbConn, `INSERT INTO workspaces (id, name, created_at, updated_at) VALUES ('ws-1', 'Test', ?, ?)`, nowStr, nowStr)
|
||||
execOrFatal(t, dbConn, `INSERT INTO boards (id, workspace_id, name, created_at, updated_at) VALUES ('board-1', 'ws-1', 'Board', ?, ?)`, nowStr, nowStr)
|
||||
|
||||
execOrFatal(t, dbConn, `INSERT INTO tasks (id, workspace_id, board_id, workflow_step_id, title, is_ephemeral, created_at, updated_at) VALUES ('task-regular', 'ws-1', 'board-1', '', 'Regular', 0, ?, ?)`, nowStr, nowStr)
|
||||
execOrFatal(t, dbConn, `INSERT INTO tasks (id, workspace_id, board_id, workflow_step_id, title, is_ephemeral, created_at, updated_at) VALUES ('task-ephemeral', 'ws-1', 'board-1', '', 'Quick Chat', 1, ?, ?)`, nowStr, nowStr)
|
||||
|
||||
execOrFatal(t, dbConn, `INSERT INTO task_sessions (id, task_id, agent_profile_id, state, started_at, updated_at) VALUES ('sess-regular', 'task-regular', 'agent-1', 'COMPLETED', ?, ?)`, nowStr, nowStr)
|
||||
execOrFatal(t, dbConn, `INSERT INTO task_sessions (id, task_id, agent_profile_id, state, started_at, updated_at) VALUES ('sess-ephemeral', 'task-ephemeral', 'agent-1', 'COMPLETED', ?, ?)`, nowStr, nowStr)
|
||||
|
||||
execOrFatal(t, dbConn, `INSERT INTO task_session_turns (id, task_session_id, task_id, started_at, completed_at, created_at, updated_at) VALUES ('turn-regular', 'sess-regular', 'task-regular', ?, ?, ?, ?)`, nowStr, nowStr, nowStr, nowStr)
|
||||
execOrFatal(t, dbConn, `INSERT INTO task_session_turns (id, task_session_id, task_id, started_at, completed_at, created_at, updated_at) VALUES ('turn-ephemeral', 'sess-ephemeral', 'task-ephemeral', ?, ?, ?, ?)`, nowStr, nowStr, nowStr, nowStr)
|
||||
|
||||
results, err := repo.GetDailyActivity(ctx, "ws-1", 7)
|
||||
if err != nil {
|
||||
t.Fatalf("GetDailyActivity failed: %v", err)
|
||||
}
|
||||
|
||||
totalTurns := 0
|
||||
for _, r := range results {
|
||||
totalTurns += r.TurnCount
|
||||
}
|
||||
if totalTurns != 1 {
|
||||
t.Errorf("expected 1 total turn (ephemeral excluded), got %d", totalTurns)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCompletedTaskActivity_ExcludesEphemeralTasks(t *testing.T) {
|
||||
dbConn := createTestDB(t)
|
||||
repo, err := NewWithDB(dbConn, dbConn)
|
||||
if err != nil {
|
||||
t.Fatalf("NewWithDB failed: %v", err)
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
now := time.Now().UTC()
|
||||
nowStr := now.Format(time.RFC3339)
|
||||
|
||||
execOrFatal(t, dbConn, `INSERT INTO workspaces (id, name, created_at, updated_at) VALUES ('ws-1', 'Test', ?, ?)`, nowStr, nowStr)
|
||||
execOrFatal(t, dbConn, `INSERT INTO boards (id, workspace_id, name, created_at, updated_at) VALUES ('board-1', 'ws-1', 'Board', ?, ?)`, nowStr, nowStr)
|
||||
execOrFatal(t, dbConn, `INSERT INTO workflow_steps (id, workflow_id, name, position, created_at, updated_at) VALUES ('step-done', 'board-1', 'Done', 1, ?, ?)`, nowStr, nowStr)
|
||||
|
||||
// Regular completed task
|
||||
execOrFatal(t, dbConn, `INSERT INTO tasks (id, workspace_id, board_id, workflow_step_id, title, is_ephemeral, archived_at, created_at, updated_at) VALUES ('task-regular', 'ws-1', 'board-1', 'step-done', 'Regular', 0, ?, ?, ?)`, nowStr, nowStr, nowStr)
|
||||
// Ephemeral completed task
|
||||
execOrFatal(t, dbConn, `INSERT INTO tasks (id, workspace_id, board_id, workflow_step_id, title, is_ephemeral, archived_at, created_at, updated_at) VALUES ('task-ephemeral', 'ws-1', 'board-1', '', 'Quick Chat', 1, ?, ?, ?)`, nowStr, nowStr, nowStr)
|
||||
|
||||
execOrFatal(t, dbConn, `INSERT INTO task_sessions (id, task_id, agent_profile_id, state, started_at, completed_at, updated_at) VALUES ('sess-regular', 'task-regular', 'agent-1', 'COMPLETED', ?, ?, ?)`, nowStr, nowStr, nowStr)
|
||||
execOrFatal(t, dbConn, `INSERT INTO task_sessions (id, task_id, agent_profile_id, state, started_at, completed_at, updated_at) VALUES ('sess-ephemeral', 'task-ephemeral', 'agent-1', 'COMPLETED', ?, ?, ?)`, nowStr, nowStr, nowStr)
|
||||
|
||||
results, err := repo.GetCompletedTaskActivity(ctx, "ws-1", 7)
|
||||
if err != nil {
|
||||
t.Fatalf("GetCompletedTaskActivity failed: %v", err)
|
||||
}
|
||||
|
||||
totalCompleted := 0
|
||||
for _, r := range results {
|
||||
totalCompleted += r.CompletedTasks
|
||||
}
|
||||
if totalCompleted != 1 {
|
||||
t.Errorf("expected 1 completed task (ephemeral excluded), got %d", totalCompleted)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAgentUsage_ExcludesEphemeralTasks(t *testing.T) {
|
||||
dbConn := createTestDB(t)
|
||||
repo, err := NewWithDB(dbConn, dbConn)
|
||||
if err != nil {
|
||||
t.Fatalf("NewWithDB failed: %v", err)
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
now := time.Now().UTC()
|
||||
nowStr := now.Format(time.RFC3339)
|
||||
|
||||
execOrFatal(t, dbConn, `INSERT INTO workspaces (id, name, created_at, updated_at) VALUES ('ws-1', 'Test', ?, ?)`, nowStr, nowStr)
|
||||
execOrFatal(t, dbConn, `INSERT INTO boards (id, workspace_id, name, created_at, updated_at) VALUES ('board-1', 'ws-1', 'Board', ?, ?)`, nowStr, nowStr)
|
||||
|
||||
execOrFatal(t, dbConn, `INSERT INTO tasks (id, workspace_id, board_id, workflow_step_id, title, is_ephemeral, created_at, updated_at) VALUES ('task-regular', 'ws-1', 'board-1', '', 'Regular', 0, ?, ?)`, nowStr, nowStr)
|
||||
execOrFatal(t, dbConn, `INSERT INTO tasks (id, workspace_id, board_id, workflow_step_id, title, is_ephemeral, created_at, updated_at) VALUES ('task-ephemeral', 'ws-1', 'board-1', '', 'Quick Chat', 1, ?, ?)`, nowStr, nowStr)
|
||||
|
||||
execOrFatal(t, dbConn, `INSERT INTO task_sessions (id, task_id, agent_profile_id, agent_profile_snapshot, state, started_at, updated_at) VALUES ('sess-regular', 'task-regular', 'agent-1', '{"name":"Agent"}', 'COMPLETED', ?, ?)`, nowStr, nowStr)
|
||||
execOrFatal(t, dbConn, `INSERT INTO task_sessions (id, task_id, agent_profile_id, agent_profile_snapshot, state, started_at, updated_at) VALUES ('sess-ephemeral', 'task-ephemeral', 'agent-1', '{"name":"Agent"}', 'COMPLETED', ?, ?)`, nowStr, nowStr)
|
||||
|
||||
results, err := repo.GetAgentUsage(ctx, "ws-1", 5, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("GetAgentUsage failed: %v", err)
|
||||
}
|
||||
|
||||
totalSessions := 0
|
||||
for _, r := range results {
|
||||
totalSessions += r.SessionCount
|
||||
}
|
||||
if totalSessions != 1 {
|
||||
t.Errorf("expected 1 session (ephemeral excluded), got %d", totalSessions)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetGitStats_ExcludesEphemeralTasks(t *testing.T) {
|
||||
dbConn := createTestDB(t)
|
||||
repo, err := NewWithDB(dbConn, dbConn)
|
||||
if err != nil {
|
||||
t.Fatalf("NewWithDB failed: %v", err)
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
now := time.Now().UTC()
|
||||
nowStr := now.Format(time.RFC3339)
|
||||
|
||||
execOrFatal(t, dbConn, `INSERT INTO workspaces (id, name, created_at, updated_at) VALUES ('ws-1', 'Test', ?, ?)`, nowStr, nowStr)
|
||||
execOrFatal(t, dbConn, `INSERT INTO boards (id, workspace_id, name, created_at, updated_at) VALUES ('board-1', 'ws-1', 'Board', ?, ?)`, nowStr, nowStr)
|
||||
|
||||
execOrFatal(t, dbConn, `INSERT INTO tasks (id, workspace_id, board_id, workflow_step_id, title, is_ephemeral, created_at, updated_at) VALUES ('task-regular', 'ws-1', 'board-1', '', 'Regular', 0, ?, ?)`, nowStr, nowStr)
|
||||
execOrFatal(t, dbConn, `INSERT INTO tasks (id, workspace_id, board_id, workflow_step_id, title, is_ephemeral, created_at, updated_at) VALUES ('task-ephemeral', 'ws-1', 'board-1', '', 'Quick Chat', 1, ?, ?)`, nowStr, nowStr)
|
||||
|
||||
execOrFatal(t, dbConn, `INSERT INTO task_sessions (id, task_id, agent_profile_id, state, started_at, updated_at) VALUES ('sess-regular', 'task-regular', 'agent-1', 'COMPLETED', ?, ?)`, nowStr, nowStr)
|
||||
execOrFatal(t, dbConn, `INSERT INTO task_sessions (id, task_id, agent_profile_id, state, started_at, updated_at) VALUES ('sess-ephemeral', 'task-ephemeral', 'agent-1', 'COMPLETED', ?, ?)`, nowStr, nowStr)
|
||||
|
||||
execOrFatal(t, dbConn, `INSERT INTO task_session_commits (id, session_id, commit_sha, committed_at, files_changed, insertions, deletions, created_at) VALUES ('c-regular', 'sess-regular', 'abc123', ?, 3, 10, 2, ?)`, nowStr, nowStr)
|
||||
execOrFatal(t, dbConn, `INSERT INTO task_session_commits (id, session_id, commit_sha, committed_at, files_changed, insertions, deletions, created_at) VALUES ('c-ephemeral', 'sess-ephemeral', 'def456', ?, 5, 20, 8, ?)`, nowStr, nowStr)
|
||||
|
||||
stats, err := repo.GetGitStats(ctx, "ws-1", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("GetGitStats failed: %v", err)
|
||||
}
|
||||
if stats.TotalCommits != 1 {
|
||||
t.Errorf("expected 1 commit (ephemeral excluded), got %d", stats.TotalCommits)
|
||||
}
|
||||
if stats.TotalFilesChanged != 3 {
|
||||
t.Errorf("expected 3 files changed, got %d", stats.TotalFilesChanged)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetRepositoryStats_ExcludesEphemeralTasks(t *testing.T) {
|
||||
dbConn := createTestDB(t)
|
||||
repo, err := NewWithDB(dbConn, dbConn)
|
||||
if err != nil {
|
||||
t.Fatalf("NewWithDB failed: %v", err)
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
now := time.Now().UTC()
|
||||
nowStr := now.Format(time.RFC3339)
|
||||
|
||||
execOrFatal(t, dbConn, `INSERT INTO workspaces (id, name, created_at, updated_at) VALUES ('ws-1', 'Test', ?, ?)`, nowStr, nowStr)
|
||||
execOrFatal(t, dbConn, `INSERT INTO boards (id, workspace_id, name, created_at, updated_at) VALUES ('board-1', 'ws-1', 'Board', ?, ?)`, nowStr, nowStr)
|
||||
execOrFatal(t, dbConn, `INSERT INTO repositories (id, workspace_id, name, created_at, updated_at) VALUES ('repo-1', 'ws-1', 'my-repo', ?, ?)`, nowStr, nowStr)
|
||||
|
||||
execOrFatal(t, dbConn, `INSERT INTO tasks (id, workspace_id, board_id, workflow_step_id, title, is_ephemeral, created_at, updated_at) VALUES ('task-regular', 'ws-1', 'board-1', '', 'Regular', 0, ?, ?)`, nowStr, nowStr)
|
||||
execOrFatal(t, dbConn, `INSERT INTO tasks (id, workspace_id, board_id, workflow_step_id, title, is_ephemeral, created_at, updated_at) VALUES ('task-ephemeral', 'ws-1', 'board-1', '', 'Quick Chat', 1, ?, ?)`, nowStr, nowStr)
|
||||
|
||||
execOrFatal(t, dbConn, `INSERT INTO task_repositories (id, task_id, repository_id, created_at, updated_at) VALUES ('tr-1', 'task-regular', 'repo-1', ?, ?)`, nowStr, nowStr)
|
||||
execOrFatal(t, dbConn, `INSERT INTO task_repositories (id, task_id, repository_id, created_at, updated_at) VALUES ('tr-2', 'task-ephemeral', 'repo-1', ?, ?)`, nowStr, nowStr)
|
||||
|
||||
execOrFatal(t, dbConn, `INSERT INTO task_sessions (id, task_id, agent_profile_id, repository_id, state, started_at, updated_at) VALUES ('sess-regular', 'task-regular', 'agent-1', 'repo-1', 'COMPLETED', ?, ?)`, nowStr, nowStr)
|
||||
execOrFatal(t, dbConn, `INSERT INTO task_sessions (id, task_id, agent_profile_id, repository_id, state, started_at, updated_at) VALUES ('sess-ephemeral', 'task-ephemeral', 'agent-1', 'repo-1', 'COMPLETED', ?, ?)`, nowStr, nowStr)
|
||||
|
||||
results, err := repo.GetRepositoryStats(ctx, "ws-1", nil)
|
||||
if err != nil {
|
||||
t.Fatalf("GetRepositoryStats failed: %v", err)
|
||||
}
|
||||
if len(results) != 1 {
|
||||
t.Fatalf("expected 1 repo, got %d", len(results))
|
||||
}
|
||||
if results[0].TotalTasks != 1 {
|
||||
t.Errorf("expected 1 task (ephemeral excluded), got %d", results[0].TotalTasks)
|
||||
}
|
||||
if results[0].SessionCount != 1 {
|
||||
t.Errorf("expected 1 session (ephemeral excluded), got %d", results[0].SessionCount)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ func (r *Repository) GetTaskStats(ctx context.Context, workspaceID string, start
|
||||
WHERE (? IS NULL OR s.started_at >= ?)
|
||||
GROUP BY s.task_id
|
||||
) turn_stats ON turn_stats.task_id = t.id
|
||||
WHERE t.workspace_id = ? AND (? IS NULL OR t.created_at >= ?)
|
||||
WHERE t.workspace_id = ? AND t.is_ephemeral = 0 AND (? IS NULL OR t.created_at >= ?)
|
||||
ORDER BY t.updated_at DESC
|
||||
`, dur)
|
||||
|
||||
@@ -133,37 +133,38 @@ func (r *Repository) GetGlobalStats(ctx context.Context, workspaceID string, sta
|
||||
|
||||
query := fmt.Sprintf(`
|
||||
SELECT
|
||||
(SELECT COUNT(*) FROM tasks WHERE workspace_id = ? AND (? IS NULL OR created_at >= ?)) as total_tasks,
|
||||
(SELECT COUNT(*) FROM tasks WHERE workspace_id = ? AND is_ephemeral = 0 AND (? IS NULL OR created_at >= ?)) as total_tasks,
|
||||
(SELECT COUNT(*) FROM tasks t
|
||||
LEFT JOIN workflow_steps ws ON ws.id = t.workflow_step_id
|
||||
WHERE t.workspace_id = ?
|
||||
AND t.is_ephemeral = 0
|
||||
AND (t.archived_at IS NOT NULL
|
||||
OR ws.position = (SELECT MAX(ws2.position) FROM workflow_steps ws2 WHERE ws2.workflow_id = ws.workflow_id))
|
||||
AND (? IS NULL OR t.created_at >= ?)) as completed_tasks,
|
||||
(SELECT COUNT(*) FROM tasks WHERE workspace_id = ? AND state = 'IN_PROGRESS' AND archived_at IS NULL AND (? IS NULL OR created_at >= ?)) as in_progress_tasks,
|
||||
(SELECT COUNT(*) FROM task_sessions s JOIN tasks t ON t.id = s.task_id WHERE t.workspace_id = ? AND (? IS NULL OR s.started_at >= ?)) as total_sessions,
|
||||
(SELECT COUNT(*) FROM tasks WHERE workspace_id = ? AND is_ephemeral = 0 AND state = 'IN_PROGRESS' AND archived_at IS NULL AND (? IS NULL OR created_at >= ?)) as in_progress_tasks,
|
||||
(SELECT COUNT(*) FROM task_sessions s JOIN tasks t ON t.id = s.task_id WHERE t.workspace_id = ? AND t.is_ephemeral = 0 AND (? IS NULL OR s.started_at >= ?)) as total_sessions,
|
||||
(SELECT COUNT(*) FROM task_session_turns turn
|
||||
JOIN task_sessions s ON s.id = turn.task_session_id
|
||||
JOIN tasks t ON t.id = s.task_id
|
||||
WHERE t.workspace_id = ? AND (? IS NULL OR s.started_at >= ?)) as total_turns,
|
||||
WHERE t.workspace_id = ? AND t.is_ephemeral = 0 AND (? IS NULL OR s.started_at >= ?)) as total_turns,
|
||||
(SELECT COUNT(*) FROM task_session_messages msg
|
||||
JOIN task_sessions s ON s.id = msg.task_session_id
|
||||
JOIN tasks t ON t.id = s.task_id
|
||||
WHERE t.workspace_id = ? AND (? IS NULL OR s.started_at >= ?)) as total_messages,
|
||||
WHERE t.workspace_id = ? AND t.is_ephemeral = 0 AND (? IS NULL OR s.started_at >= ?)) as total_messages,
|
||||
(SELECT COUNT(*) FROM task_session_messages msg
|
||||
JOIN task_sessions s ON s.id = msg.task_session_id
|
||||
JOIN tasks t ON t.id = s.task_id
|
||||
WHERE t.workspace_id = ? AND msg.author_type = 'user' AND (? IS NULL OR s.started_at >= ?)) as total_user_messages,
|
||||
WHERE t.workspace_id = ? AND t.is_ephemeral = 0 AND msg.author_type = 'user' AND (? IS NULL OR s.started_at >= ?)) as total_user_messages,
|
||||
(SELECT COUNT(*) FROM task_session_messages msg
|
||||
JOIN task_sessions s ON s.id = msg.task_session_id
|
||||
JOIN tasks t ON t.id = s.task_id
|
||||
WHERE t.workspace_id = ? AND msg.type LIKE 'tool_%%' AND (? IS NULL OR s.started_at >= ?)) as total_tool_calls,
|
||||
WHERE t.workspace_id = ? AND t.is_ephemeral = 0 AND msg.type LIKE 'tool_%%' AND (? IS NULL OR s.started_at >= ?)) as total_tool_calls,
|
||||
(SELECT COALESCE(SUM(
|
||||
CASE WHEN turn.completed_at IS NOT NULL THEN %s ELSE 0 END
|
||||
), 0) FROM task_session_turns turn
|
||||
JOIN task_sessions s ON s.id = turn.task_session_id
|
||||
JOIN tasks t ON t.id = s.task_id
|
||||
WHERE t.workspace_id = ? AND (? IS NULL OR s.started_at >= ?)) as total_duration_ms
|
||||
WHERE t.workspace_id = ? AND t.is_ephemeral = 0 AND (? IS NULL OR s.started_at >= ?)) as total_duration_ms
|
||||
`, dur)
|
||||
|
||||
var stats models.GlobalStats
|
||||
@@ -229,7 +230,7 @@ func (r *Repository) GetDailyActivity(ctx context.Context, workspaceID string, d
|
||||
JOIN tasks t ON t.id = s.task_id
|
||||
LEFT JOIN task_session_messages msg ON msg.task_session_id = s.id
|
||||
AND %s = %s
|
||||
WHERE t.workspace_id = ?
|
||||
WHERE t.workspace_id = ? AND t.is_ephemeral = 0
|
||||
GROUP BY %s
|
||||
) activity ON activity.activity_date = d.date
|
||||
ORDER BY d.date ASC
|
||||
@@ -277,7 +278,7 @@ func (r *Repository) GetCompletedTaskActivity(ctx context.Context, workspaceID s
|
||||
SELECT task_id, MAX(completed_at) as completed_at
|
||||
FROM task_sessions WHERE completed_at IS NOT NULL GROUP BY task_id
|
||||
) ts ON ts.task_id = t.id
|
||||
WHERE t.workspace_id = ?
|
||||
WHERE t.workspace_id = ? AND t.is_ephemeral = 0
|
||||
AND (t.archived_at IS NOT NULL
|
||||
OR ws.position = (SELECT MAX(ws2.position) FROM workflow_steps ws2 WHERE ws2.workflow_id = ws.workflow_id))
|
||||
GROUP BY %s
|
||||
@@ -370,7 +371,7 @@ func buildRepositoryStatsQuery(drv string) string {
|
||||
FROM task_repositories tr
|
||||
JOIN tasks t ON t.id = tr.task_id
|
||||
LEFT JOIN workflow_steps ws ON ws.id = t.workflow_step_id
|
||||
WHERE (? IS NULL OR t.created_at >= ?)
|
||||
WHERE t.is_ephemeral = 0 AND (? IS NULL OR t.created_at >= ?)
|
||||
GROUP BY tr.repository_id
|
||||
) task_stats ON task_stats.repository_id = r.id
|
||||
LEFT JOIN (
|
||||
@@ -381,19 +382,21 @@ func buildRepositoryStatsQuery(drv string) string {
|
||||
COUNT(DISTINCT CASE WHEN msg.author_type = 'user' THEN msg.id END) as user_message_count,
|
||||
COUNT(DISTINCT CASE WHEN msg.type LIKE 'tool_%%' THEN msg.id END) as tool_call_count
|
||||
FROM task_repositories tr
|
||||
JOIN tasks t ON t.id = tr.task_id
|
||||
JOIN task_sessions s ON s.task_id = tr.task_id
|
||||
LEFT JOIN task_session_turns turn ON turn.task_session_id = s.id
|
||||
LEFT JOIN task_session_messages msg ON msg.task_session_id = s.id
|
||||
WHERE (? IS NULL OR s.started_at >= ?)
|
||||
WHERE t.is_ephemeral = 0 AND (? IS NULL OR s.started_at >= ?)
|
||||
GROUP BY tr.repository_id
|
||||
) session_stats ON session_stats.repository_id = r.id
|
||||
LEFT JOIN (
|
||||
SELECT tr.repository_id,
|
||||
COALESCE(SUM(CASE WHEN turn.completed_at IS NOT NULL THEN %s ELSE 0 END), 0) as total_duration_ms
|
||||
FROM task_repositories tr
|
||||
JOIN tasks t ON t.id = tr.task_id
|
||||
JOIN task_sessions s ON s.task_id = tr.task_id
|
||||
LEFT JOIN task_session_turns turn ON turn.task_session_id = s.id
|
||||
WHERE (? IS NULL OR s.started_at >= ?)
|
||||
WHERE t.is_ephemeral = 0 AND (? IS NULL OR s.started_at >= ?)
|
||||
GROUP BY tr.repository_id
|
||||
) duration_stats ON duration_stats.repository_id = r.id
|
||||
LEFT JOIN (
|
||||
@@ -404,7 +407,8 @@ func buildRepositoryStatsQuery(drv string) string {
|
||||
COALESCE(SUM(c.deletions), 0) as total_deletions
|
||||
FROM task_session_commits c
|
||||
JOIN task_sessions s ON s.id = c.session_id
|
||||
WHERE s.repository_id != '' AND (? IS NULL OR c.committed_at >= ?)
|
||||
JOIN tasks t ON t.id = s.task_id
|
||||
WHERE t.is_ephemeral = 0 AND s.repository_id != '' AND (? IS NULL OR c.committed_at >= ?)
|
||||
GROUP BY s.repository_id
|
||||
) git_stats ON git_stats.repository_id = r.id
|
||||
WHERE r.workspace_id = ? AND r.deleted_at IS NULL
|
||||
@@ -438,7 +442,7 @@ func (r *Repository) GetAgentUsage(ctx context.Context, workspaceID string, limi
|
||||
FROM task_sessions s
|
||||
JOIN tasks t ON t.id = s.task_id
|
||||
LEFT JOIN task_session_turns turn ON turn.task_session_id = s.id
|
||||
WHERE t.workspace_id = ? AND s.agent_profile_id != '' AND (? IS NULL OR s.started_at >= ?)
|
||||
WHERE t.workspace_id = ? AND t.is_ephemeral = 0 AND s.agent_profile_id != '' AND (? IS NULL OR s.started_at >= ?)
|
||||
GROUP BY s.agent_profile_id
|
||||
ORDER BY session_count DESC
|
||||
LIMIT ?
|
||||
@@ -484,7 +488,7 @@ func (r *Repository) GetGitStats(ctx context.Context, workspaceID string, start
|
||||
FROM task_session_commits c
|
||||
JOIN task_sessions s ON s.id = c.session_id
|
||||
JOIN tasks t ON t.id = s.task_id
|
||||
WHERE t.workspace_id = ? AND (? IS NULL OR c.committed_at >= ?)
|
||||
WHERE t.workspace_id = ? AND t.is_ephemeral = 0 AND (? IS NULL OR c.committed_at >= ?)
|
||||
`
|
||||
|
||||
var stats models.GitStats
|
||||
|
||||
Reference in New Issue
Block a user