| """ |
| Workload score calculation service. |
| Computes route difficulty, estimated time, and overall workload score. |
| """ |
|
|
| from typing import Union |
| from dataclasses import dataclass |
|
|
| from app.config import get_settings |
|
|
|
|
| @dataclass |
| class RouteMetrics: |
| """Route metrics for workload calculation.""" |
| num_packages: int |
| total_weight_kg: float |
| num_stops: int |
| route_difficulty_score: float |
| estimated_time_minutes: int |
|
|
|
|
| def calculate_route_difficulty( |
| total_weight_kg: float, |
| num_stops: int, |
| avg_fragility: float = 1.0, |
| ) -> float: |
| """ |
| Calculate route difficulty score based on weight, stops, and fragility. |
| |
| Args: |
| total_weight_kg: Total weight of packages in kg |
| num_stops: Number of delivery stops |
| avg_fragility: Average fragility level (1-5) |
| |
| Returns: |
| Route difficulty score (higher = more difficult) |
| """ |
| settings = get_settings() |
| |
| |
| difficulty = settings.difficulty_base |
| |
| |
| difficulty += total_weight_kg * settings.difficulty_weight_per_kg |
| |
| |
| difficulty += num_stops * settings.difficulty_weight_per_stop |
| |
| |
| fragility_multiplier = 1.0 + (avg_fragility - 1) * 0.1 |
| difficulty *= fragility_multiplier |
| |
| return round(difficulty, 2) |
|
|
|
|
| def estimate_route_time( |
| num_packages: int, |
| num_stops: int, |
| total_distance_km: float = 0.0, |
| ) -> int: |
| """ |
| Estimate route completion time in minutes. |
| |
| Args: |
| num_packages: Number of packages to deliver |
| num_stops: Number of delivery stops |
| total_distance_km: Total route distance (optional) |
| |
| Returns: |
| Estimated time in minutes |
| """ |
| settings = get_settings() |
| |
| |
| time = settings.base_route_time |
| |
| |
| time += num_packages * settings.time_per_package |
| |
| |
| time += num_stops * settings.time_per_stop |
| |
| |
| if total_distance_km > 0: |
| time += (total_distance_km / 30) * 60 |
| |
| return int(round(time)) |
|
|
|
|
| def calculate_workload( |
| route_metrics: Union[RouteMetrics, dict], |
| weights: dict = None, |
| ) -> float: |
| """ |
| Calculate the overall workload score for a route. |
| |
| Formula: workload = a * num_packages + b * total_weight_kg |
| + c * route_difficulty_score + d * estimated_time_minutes |
| |
| Args: |
| route_metrics: RouteMetrics object or dict with route info |
| weights: Optional custom weights dict {a, b, c, d} |
| |
| Returns: |
| Workload score (higher = more work) |
| """ |
| settings = get_settings() |
| |
| |
| if weights is None: |
| weights = { |
| "a": settings.workload_weight_a, |
| "b": settings.workload_weight_b, |
| "c": settings.workload_weight_c, |
| "d": settings.workload_weight_d, |
| } |
| |
| |
| if isinstance(route_metrics, dict): |
| num_packages = route_metrics["num_packages"] |
| total_weight_kg = route_metrics["total_weight_kg"] |
| route_difficulty_score = route_metrics["route_difficulty_score"] |
| estimated_time_minutes = route_metrics["estimated_time_minutes"] |
| else: |
| num_packages = route_metrics.num_packages |
| total_weight_kg = route_metrics.total_weight_kg |
| route_difficulty_score = route_metrics.route_difficulty_score |
| estimated_time_minutes = route_metrics.estimated_time_minutes |
| |
| |
| workload = ( |
| weights["a"] * num_packages + |
| weights["b"] * total_weight_kg + |
| weights["c"] * route_difficulty_score + |
| weights["d"] * estimated_time_minutes |
| ) |
| |
| return round(workload, 2) |
|
|