Skip to main content
Glama

π“‚€π“’π“‹Ήπ”Έβ„•π•Œπ”Ήπ•€π•Šπ“‹Ήπ“’π“‚€ - Intelligent Guidance for

by Hive-Academy
progress-calculator.service.tsβ€’7.37 kB
import { Injectable, Inject } from '@nestjs/common'; import { IProgressCalculationRepository } from '../repositories/interfaces/progress-calculation.repository.interface'; import { ProgressCalculationResult, ProgressMetrics, } from '../types/progress-calculator.types'; import { createErrorResult } from '../utils/type-safety.utils'; import { WorkflowGuidance } from './workflow-guidance.service'; @Injectable() export class ProgressCalculatorService { constructor( @Inject('IProgressCalculationRepository') private readonly progressCalculationRepository: IProgressCalculationRepository, ) {} /** * Calculate real-time progress from database with type safety */ async calculateProgress( taskId: number, guidance: WorkflowGuidance, currentStepId?: string | null, ): Promise<ProgressCalculationResult> { try { // Get basic task information const task = await this.getTaskBasicInfo(taskId); if (!task) { return { success: true, metrics: this.getDefaultProgress(), context: { taskId, roleName: guidance.currentRole.name, stepId: currentStepId || null, }, }; } // Get role steps and progress data const [roleSteps, stepProgressResult] = await Promise.all([ this.getRoleSteps(guidance.currentRole.name), this.getStepProgress(taskId), ]); const stepProgress = stepProgressResult || []; // Calculate progress metrics with type safety const currentStepProgress = this.calculateCurrentStepProgress( currentStepId || null, stepProgress, ); const roleProgress = this.calculateRoleProgress( roleSteps, stepProgress, guidance.currentRole.name, ); const overallProgress = this.calculateOverallProgress( stepProgress, roleSteps.length, ); // Calculate additional metrics const completedSteps = this.countCompletedSteps(stepProgress); const totalSteps = roleSteps.length; const estimatedTimeRemaining = this.estimateTimeRemaining(roleProgress); const nextMilestone = this.getNextMilestone(guidance.currentRole.name); const metrics: ProgressMetrics = { currentStepProgress, roleProgress, overallProgress, completedSteps, totalSteps, estimatedTimeRemaining, nextMilestone, }; return { success: true, metrics, context: { taskId, roleName: guidance.currentRole.name, stepId: currentStepId || null, }, }; } catch (error) { return createErrorResult(error, 'Progress calculation failed'); } } /** * Get basic task information from database */ private async getTaskBasicInfo(taskId: number) { const result = await this.progressCalculationRepository.findTaskBasicInfo(taskId); return result.success ? result.data : null; } /** * Get steps for a specific role from database */ private async getRoleSteps(roleName: string) { const result = await this.progressCalculationRepository.findRoleWithSteps(roleName); return result.success && result.data ? result.data.steps : []; } /** * Get step progress for task */ private async getStepProgress(taskId: number) { const result = await this.progressCalculationRepository.findStepProgressByTaskId(taskId); return result.success ? result.data : []; } /** * Calculate current step progress with type safety */ private calculateCurrentStepProgress( currentStepId: string | null, stepProgress: any[], ): number { if (!currentStepId) { return 0; } // Find current step progress const currentProgress = stepProgress.find( (progress) => progress.step.id === currentStepId, ); if (!currentProgress) { return 0; } // Calculate progress based on status switch (currentProgress.status) { case 'completed': return 100; case 'in_progress': return 50; // Default to 50% for in-progress steps case 'failed': return 0; default: return 0; } } /** * Calculate role progress with type safety */ private calculateRoleProgress( roleSteps: any[], stepProgress: any[], currentRoleName: string, ): number { if (roleSteps.length === 0) { return 0; } // Filter progress for current role const roleProgress = stepProgress.filter( (progress) => progress.role.name === currentRoleName, ); // Count completed steps const completedSteps = roleProgress.filter( (progress) => progress.status === 'completed', ).length; // Add partial progress for in-progress steps const inProgressSteps = roleProgress.filter( (progress) => progress.status === 'in_progress', ).length; const totalProgress = completedSteps + inProgressSteps * 0.5; return Math.round((totalProgress / roleSteps.length) * 100); } /** * Calculate overall workflow progress */ private calculateOverallProgress( stepProgress: any[], totalRoleSteps: number, ): number { if (totalRoleSteps === 0) { return 0; } // Count completed steps across all roles const completedSteps = stepProgress.filter( (progress) => progress.status === 'completed', ).length; // Add partial progress for in-progress steps const inProgressSteps = stepProgress.filter( (progress) => progress.status === 'in_progress', ).length; const totalProgress = completedSteps + inProgressSteps * 0.5; return Math.round((totalProgress / totalRoleSteps) * 100); } /** * Count completed steps with type safety */ private countCompletedSteps(stepProgress: any[]): number { return stepProgress.filter((progress) => progress.status === 'completed') .length; } /** * Estimate time remaining based on role progress */ private estimateTimeRemaining(roleProgress: number): string | undefined { if (roleProgress >= 100) { return undefined; } // Simple estimation based on progress const remainingPercentage = 100 - roleProgress; const estimatedMinutes = Math.round(remainingPercentage * 2); // 2 minutes per percentage point if (estimatedMinutes < 60) { return `${estimatedMinutes} minutes`; } else { const hours = Math.round(estimatedMinutes / 60); return `${hours} hour${hours > 1 ? 's' : ''}`; } } /** * Get next milestone for role */ private getNextMilestone(currentRoleName: string): string | undefined { // Simple milestone mapping const milestones: Record<string, string> = { 'product-manager': 'Task Analysis Complete', architect: 'Subtasks Ready', 'senior-developer': 'Implementation Complete', 'code-review': 'Quality Review Complete', }; return milestones[currentRoleName]; } /** * Get default progress when calculation fails */ private getDefaultProgress(): ProgressMetrics { return { currentStepProgress: 0, roleProgress: 0, overallProgress: 0, completedSteps: 0, totalSteps: 0, estimatedTimeRemaining: undefined, nextMilestone: undefined, }; } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Hive-Academy/Anubis-MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server