Source code for core.workflow_effort_calculator

"""
工作量计算器 - 解决循环依赖问题

提供独立的工作量计算功能,避免在工作流生成过程中产生循环依赖。
"""

import logging
from typing import Dict, List, Optional

from .workflow_models import (
    ComplexityLevel,
    EffortEstimation,
    PersonaType,
    Requirement,
    RequirementCategories,
    WorkflowStrategy,
)


[docs] class EffortCalculator: """独立的工作量计算器"""
[docs] def __init__(self) -> None: self.logger = logging.getLogger("EffortCalculator") # 基于复杂度的工作量映射表 self.complexity_effort_map = { ComplexityLevel.SIMPLE: 8, # 8小时 ComplexityLevel.MEDIUM: 16, # 16小时 ComplexityLevel.COMPLEX: 32, # 32小时 ComplexityLevel.ENTERPRISE: 64, # 64小时 } # 不同策略的系统开销比例 self.strategy_overhead_map = { WorkflowStrategy.SYSTEMATIC: 0.35, # 系统化策略开销35% WorkflowStrategy.AGILE: 0.25, # 敏捷策略开销25% WorkflowStrategy.MVP: 0.15, # MVP策略开销15% } # 专家人格工作量分配比例 self.persona_allocation_map = { PersonaType.BACKEND: 0.35, # 后端35% PersonaType.FRONTEND: 0.25, # 前端25% PersonaType.ARCHITECT: 0.15, # 架构15% PersonaType.SECURITY: 0.10, # 安全10% PersonaType.QA: 0.10, # 测试10% PersonaType.DEVOPS: 0.05, # 运维5% }
[docs] def calculate_base_effort( self, requirements: List[Requirement], strategy: WorkflowStrategy = WorkflowStrategy.SYSTEMATIC ) -> int: """ 基于需求直接计算基础工作量 Args: requirements: 需求列表 strategy: 工作流策略 Returns: 基础工作量(小时) """ try: # 1. 计算需求核心工作量 core_effort = 0 for req in requirements: if req.estimated_effort is not None: core_effort += req.estimated_effort else: # 基于复杂度估算 req_effort = self.complexity_effort_map.get(req.complexity, 16) core_effort += req_effort # 2. 添加策略相关的系统开销 overhead_ratio = self.strategy_overhead_map.get(strategy, 0.3) system_overhead = int(core_effort * overhead_ratio) total_effort = core_effort + system_overhead self.logger.info(f"计算基础工作量: 核心={core_effort}h, 开销={system_overhead}h, 总计={total_effort}h") return total_effort except Exception as e: self.logger.error(f"计算基础工作量失败: {str(e)}") # 返回默认值 return len(requirements) * 16 + 40 # 默认每需求16h + 40h开销
[docs] def calculate_detailed_estimation( self, requirements: List[Requirement], requirement_categories: Optional[RequirementCategories] = None, strategy: WorkflowStrategy = WorkflowStrategy.SYSTEMATIC, team_size: int = 1, ) -> EffortEstimation: """ 计算详细的工作量估算 Args: requirements: 需求列表 requirement_categories: 需求分类 strategy: 工作流策略 team_size: 团队规模 Returns: 详细的工作量估算 """ try: # 1. 计算基础工作量 base_effort = self.calculate_base_effort(requirements, strategy) # 2. 团队规模调整 if team_size > 1: # 大团队有沟通开销,但可以并行工作 communication_overhead = 1 + (team_size - 1) * 0.1 # 每增加1人增加10%开销 parallelization_benefit = 1 - (team_size - 1) * 0.15 # 每增加1人减少15%时间 team_adjustment = communication_overhead * parallelization_benefit base_effort = int(base_effort * team_adjustment) # 3. 计算专家人格分工 persona_breakdown = self._calculate_persona_breakdown(requirements, base_effort, requirement_categories) # 4. 计算置信度 confidence = self._calculate_confidence_level(requirements, strategy) # 5. 计算缓冲 buffer_percentage = self._calculate_buffer_percentage(strategy, confidence) estimation = EffortEstimation( total_hours=base_effort, breakdown_by_persona=persona_breakdown, confidence_level=confidence, estimation_method="requirement_based_calculation", buffer_percentage=buffer_percentage, metadata={ "base_effort": base_effort, "team_size": team_size, "strategy": strategy.value, "requirements_count": len(requirements), }, ) self.logger.info(f"详细工作量估算完成: {base_effort}h, 置信度: {confidence:.1%}") return estimation except Exception as e: self.logger.error(f"计算详细工作量估算失败: {str(e)}") # 返回默认估算 return EffortEstimation( total_hours=len(requirements) * 20, confidence_level=0.5, estimation_method="fallback_calculation" )
def _calculate_persona_breakdown( self, requirements: List[Requirement], total_effort: int, requirement_categories: Optional[RequirementCategories] = None, ) -> Dict[PersonaType, int]: """计算专家人格工作量分配""" breakdown: Dict[PersonaType, int] = {} try: if requirement_categories: # 基于需求分类智能分配 total_reqs = len(requirements) if total_reqs == 0: return breakdown # 后端工作量 backend_ratio = ( len(requirement_categories.api_requirements) + len(requirement_categories.data_requirements) ) / total_reqs backend_ratio = max(0.2, min(0.5, backend_ratio)) # 限制在20%-50% # 前端工作量 frontend_ratio = len(requirement_categories.ui_requirements) / total_reqs frontend_ratio = max(0.15, min(0.4, frontend_ratio)) # 限制在15%-40% # 安全工作量 security_ratio = len(requirement_categories.security_requirements) / total_reqs security_ratio = max(0.05, min(0.2, security_ratio)) # 限制在5%-20% # 其他角色按剩余比例分配 remaining_ratio = 1.0 - backend_ratio - frontend_ratio - security_ratio breakdown[PersonaType.BACKEND] = int(total_effort * backend_ratio) breakdown[PersonaType.FRONTEND] = int(total_effort * frontend_ratio) breakdown[PersonaType.SECURITY] = int(total_effort * security_ratio) breakdown[PersonaType.ARCHITECT] = int(total_effort * remaining_ratio * 0.4) breakdown[PersonaType.QA] = int(total_effort * remaining_ratio * 0.35) breakdown[PersonaType.DEVOPS] = int(total_effort * remaining_ratio * 0.25) else: # 使用默认分配比例 for persona, ratio in self.persona_allocation_map.items(): breakdown[persona] = int(total_effort * ratio) # 确保总和等于总工作量 current_total = sum(breakdown.values()) if current_total != total_effort: # 调整最大的分配项 max_persona = max(breakdown.keys(), key=lambda x: breakdown[x]) breakdown[max_persona] += total_effort - current_total return breakdown except Exception as e: self.logger.error(f"计算专家人格分配失败: {str(e)}") # 返回默认分配 return {PersonaType.BACKEND: int(total_effort * 0.5)} def _calculate_confidence_level(self, requirements: List[Requirement], strategy: WorkflowStrategy) -> float: """计算估算置信度""" confidence = 0.8 # 基础置信度80% try: # 基于需求清晰度调整 clear_requirements = sum(1 for req in requirements if req.description and len(req.description) > 20) clarity_ratio = clear_requirements / len(requirements) if requirements else 0 confidence *= 0.7 + clarity_ratio * 0.3 # 70%-100%基于清晰度 # 基于策略调整 strategy_confidence_map = { WorkflowStrategy.SYSTEMATIC: 1.0, # 系统化策略置信度最高 WorkflowStrategy.AGILE: 0.9, # 敏捷策略次之 WorkflowStrategy.MVP: 0.8, # MVP策略最低 } confidence *= strategy_confidence_map.get(strategy, 0.8) # 基于需求数量调整(需求太少或太多都降低置信度) req_count = len(requirements) if req_count < 3: confidence *= 0.8 # 需求太少 elif req_count > 20: confidence *= 0.9 # 需求太多 return max(0.3, min(0.95, confidence)) # 限制在30%-95% except Exception as e: self.logger.error(f"计算置信度失败: {str(e)}") return 0.7 # 默认70%置信度 def _calculate_buffer_percentage(self, strategy: WorkflowStrategy, confidence: float) -> float: """计算缓冲比例""" try: # 基础缓冲比例 base_buffer = { WorkflowStrategy.SYSTEMATIC: 0.15, # 系统化15%缓冲 WorkflowStrategy.AGILE: 0.20, # 敏捷20%缓冲 WorkflowStrategy.MVP: 0.25, # MVP 25%缓冲 } buffer = base_buffer.get(strategy, 0.2) # 基于置信度调整缓冲 if confidence < 0.6: buffer += 0.1 # 低置信度增加10%缓冲 elif confidence > 0.9: buffer -= 0.05 # 高置信度减少5%缓冲 return max(0.1, min(0.4, buffer)) # 限制在10%-40% except Exception as e: self.logger.error(f"计算缓冲比例失败: {str(e)}") return 0.2 # 默认20%缓冲
[docs] class PhaseEffortDistributor: """阶段工作量分配器"""
[docs] def __init__(self) -> None: self.logger = logging.getLogger("PhaseEffortDistributor") # 不同策略的阶段分配模板 self.phase_distribution_templates = { WorkflowStrategy.SYSTEMATIC: { "需求分析": 0.15, "架构设计": 0.20, "依赖映射": 0.10, "核心开发": 0.35, "测试验证": 0.15, "部署上线": 0.05, }, WorkflowStrategy.AGILE: { "Epic分解": 0.10, "Sprint规划": 0.05, "MVP定义": 0.15, "迭代开发": 0.50, "持续集成": 0.15, "回顾优化": 0.05, }, WorkflowStrategy.MVP: { "核心功能识别": 0.20, "快速原型": 0.35, "技术债务规划": 0.10, "验证指标": 0.15, "扩展路线图": 0.15, "反馈集成": 0.05, }, }
[docs] def distribute_effort_to_phases(self, total_effort: int, strategy: WorkflowStrategy) -> Dict[str, int]: """将总工作量分配到各个阶段""" try: template = self.phase_distribution_templates.get( strategy, self.phase_distribution_templates[WorkflowStrategy.SYSTEMATIC] ) phase_efforts = {} remaining_effort = total_effort # 按比例分配,最后一个阶段承担余数 phase_names = list(template.keys()) for i, (phase_name, ratio) in enumerate(template.items()): if i == len(phase_names) - 1: # 最后一个阶段承担余数 phase_efforts[phase_name] = remaining_effort else: effort = int(total_effort * ratio) phase_efforts[phase_name] = effort remaining_effort -= effort self.logger.info(f"阶段工作量分配完成: {strategy.value} 策略, 总计 {total_effort}h") return phase_efforts except Exception as e: self.logger.error(f"阶段工作量分配失败: {str(e)}") # 返回平均分配 phase_count = 4 # 默认4个阶段 avg_effort = total_effort // phase_count return { f"阶段{i+1}": avg_effort + (total_effort % phase_count if i == 0 else 0) for i in range(phase_count) }