Skip to content

Commit 5386183

Browse files
authored
Merge pull request #51 from VectorInstitute/fix_user_engagement
Fix user engagement counts
2 parents 0a46b61 + 428aa1d commit 5386183

File tree

2 files changed

+61
-15
lines changed

2 files changed

+61
-15
lines changed

services/analytics/app/api/snapshot/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export async function GET() {
4141
const teamMetrics = aggregateByTeam(workspaceMetrics);
4242
const platformMetrics = calculatePlatformMetrics(workspaceMetrics);
4343
const templateMetrics = calculateTemplateMetrics(workspaceMetrics, snapshot.templates);
44-
const dailyEngagement = calculateDailyEngagement(workspaceMetrics);
44+
const dailyEngagement = calculateDailyEngagement(snapshot.workspaces);
4545

4646
console.log(`Calculated metrics for ${teamMetrics.length} teams and ${dailyEngagement.length} days of engagement`);
4747

services/analytics/lib/metrics.ts

Lines changed: 60 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -506,8 +506,11 @@ export function calculateTemplateMetrics(
506506
/**
507507
* Calculate daily user engagement from workspace data
508508
* Returns array of daily unique users and active workspaces for the last 60 days
509+
*
510+
* A user is considered "engaged" if they initiate a connection to their workspace via apps, web terminal, or SSH.
511+
* This function processes all builds for each workspace and extracts all agent connection timestamps.
509512
*/
510-
export function calculateDailyEngagement(workspaces: WorkspaceMetrics[]): DailyEngagement[] {
513+
export function calculateDailyEngagement(workspaces: CoderWorkspace[]): DailyEngagement[] {
511514
const now = new Date();
512515
const daysToShow = 60;
513516

@@ -524,22 +527,65 @@ export function calculateDailyEngagement(workspaces: WorkspaceMetrics[]): DailyE
524527
workspaceActivityMap.set(dateStr, new Set());
525528
}
526529

527-
// Process each workspace
530+
// Process each workspace and all its builds
528531
workspaces.forEach((workspace) => {
529-
const createdDate = new Date(workspace.created_at).toISOString().split('T')[0];
530-
const lastActiveDate = workspace.last_active ? new Date(workspace.last_active).toISOString().split('T')[0] : createdDate;
532+
const workspaceId = workspace.id;
533+
const ownerHandle = workspace.owner_name.toLowerCase();
534+
const allBuilds = workspace.all_builds || [];
535+
536+
// Track dates when this workspace had connections
537+
const workspaceConnectionDates = new Set<string>();
538+
539+
// Process all builds to find all connection dates
540+
allBuilds.forEach((build) => {
541+
try {
542+
// Count workspace start actions as engagement (user initiating connection)
543+
if (build.transition === 'start' && build.created_at) {
544+
const dateStr = build.created_at.split('T')[0];
545+
if (engagementMap.has(dateStr)) {
546+
engagementMap.get(dateStr)!.add(ownerHandle);
547+
workspaceConnectionDates.add(dateStr);
548+
}
549+
}
531550

532-
// Add user to created date
533-
if (engagementMap.has(createdDate)) {
534-
engagementMap.get(createdDate)!.add(workspace.owner_github_handle);
535-
workspaceActivityMap.get(createdDate)!.add(workspace.workspace_id);
536-
}
551+
const resources = build.resources || [];
552+
553+
for (const resource of resources) {
554+
const agents = resource.agents || [];
555+
556+
for (const agent of agents) {
557+
// Extract all connection timestamps from this agent
558+
const firstConnected = agent.first_connected_at;
559+
const lastConnected = agent.last_connected_at;
560+
561+
// Add engagement for first connection date
562+
if (firstConnected) {
563+
const dateStr = firstConnected.split('T')[0];
564+
if (engagementMap.has(dateStr)) {
565+
engagementMap.get(dateStr)!.add(ownerHandle);
566+
workspaceConnectionDates.add(dateStr);
567+
}
568+
}
569+
570+
// Add engagement for last connection date (if different from first)
571+
if (lastConnected && lastConnected !== firstConnected) {
572+
const dateStr = lastConnected.split('T')[0];
573+
if (engagementMap.has(dateStr)) {
574+
engagementMap.get(dateStr)!.add(ownerHandle);
575+
workspaceConnectionDates.add(dateStr);
576+
}
577+
}
578+
}
579+
}
580+
} catch (error) {
581+
console.warn(`Error processing build for workspace ${workspaceId}:`, error);
582+
}
583+
});
537584

538-
// Add user to last active date
539-
if (lastActiveDate !== createdDate && engagementMap.has(lastActiveDate)) {
540-
engagementMap.get(lastActiveDate)!.add(workspace.owner_github_handle);
541-
workspaceActivityMap.get(lastActiveDate)!.add(workspace.workspace_id);
542-
}
585+
// Add workspace to active workspaces for all dates it had connections
586+
workspaceConnectionDates.forEach((dateStr) => {
587+
workspaceActivityMap.get(dateStr)?.add(workspaceId);
588+
});
543589
});
544590

545591
// Convert to array and sort by date

0 commit comments

Comments
 (0)