File size: 3,976 Bytes
fcf8749
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
"""
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()
    
    # Base difficulty
    difficulty = settings.difficulty_base
    
    # Weight contribution
    difficulty += total_weight_kg * settings.difficulty_weight_per_kg
    
    # Stops contribution
    difficulty += num_stops * settings.difficulty_weight_per_stop
    
    # Fragility multiplier (fragility 1 = 1.0x, fragility 5 = 1.4x)
    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()
    
    # Base time for the route
    time = settings.base_route_time
    
    # Time per package (handling, scanning, handover)
    time += num_packages * settings.time_per_package
    
    # Time per stop (parking, navigation, walking)
    time += num_stops * settings.time_per_stop
    
    # Driving time (assume 30 km/h average speed in urban areas)
    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()
    
    # Default weights from 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,
        }
    
    # Extract metrics
    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
    
    # Calculate workload score
    workload = (
        weights["a"] * num_packages +
        weights["b"] * total_weight_kg +
        weights["c"] * route_difficulty_score +
        weights["d"] * estimated_time_minutes
    )
    
    return round(workload, 2)