Spaces:
Sleeping
Sleeping
| import logging | |
| from typing import Any, Dict, List | |
| from intelligence.models import CapacityPlan, ResourceRole | |
| from sales.models import Deal, DealStage | |
| from sqlalchemy import func | |
| from sqlalchemy.orm import Session | |
| logger = logging.getLogger(__name__) | |
| class StaffingForecaster: | |
| def __init__(self, db: Session): | |
| self.db = db | |
| def predict_resource_demand(self, workspace_id: str) -> Dict[str, float]: | |
| """ | |
| Calculates demand based on open pipeline probability. | |
| Heuristic: $100k Deal Value = 500 Engineering Hours (Rate $200/hr) | |
| """ | |
| # Fetch Open Pipeline | |
| pipeline = self.db.query(Deal).filter( | |
| Deal.workspace_id == workspace_id, | |
| Deal.stage.notin_([DealStage.CLOSED_WON, DealStage.CLOSED_LOST]) | |
| ).all() | |
| weighted_pipeline_value = 0.0 | |
| for deal in pipeline: | |
| # Simple probability map | |
| prob = 0.1 | |
| if deal.stage == DealStage.NEGOTIATION: prob = 0.8 | |
| elif deal.stage == DealStage.PROPOSAL: prob = 0.5 | |
| weighted_pipeline_value += (deal.value * prob) | |
| # Convert to Hours (Simplified Model) | |
| # Assume 50% of revenue goes to Engineering Labor at $100/hr cost | |
| labor_budget = weighted_pipeline_value * 0.5 | |
| demand_hours = labor_budget / 100.0 | |
| return { | |
| "weighted_pipeline_value": weighted_pipeline_value, | |
| "estimated_engineering_hours": demand_hours | |
| } | |
| def check_capacity_gap(self, workspace_id: str, demand_hours: float) -> Dict[str, Any]: | |
| """ | |
| Compare Demand vs Supply (Capacity Plans) | |
| """ | |
| # Sum active capacity | |
| plans = self.db.query(CapacityPlan).filter( | |
| CapacityPlan.workspace_id == workspace_id | |
| ).all() | |
| supply_hours = sum(p.available_hours for p in plans) | |
| if demand_hours > supply_hours: | |
| gap = demand_hours - supply_hours | |
| return { | |
| "status": "SHORTAGE", | |
| "gap_hours": gap, | |
| "message": f"Capacity Shortage: Need {int(gap)} more hours to support pipeline." | |
| } | |
| return {"status": "OK", "surplus_hours": supply_hours - demand_hours} | |