File size: 3,415 Bytes
c2cb41b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
from typing import Optional, List, Dict, Any
from datetime import datetime, date, timedelta
import json


def parse_date_string(date_str: Optional[str], default: Optional[date] = None) -> Optional[date]:
    """Parse date string to date object"""
    if not date_str:
        return default
    try:
        return date.fromisoformat(date_str)
    except ValueError:
        return default


def parse_datetime_string(datetime_str: Optional[str], default: Optional[datetime] = None) -> Optional[datetime]:
    """Parse datetime string to datetime object"""
    if not datetime_str:
        return default
    try:
        return datetime.fromisoformat(datetime_str.replace('Z', '+00:00'))
    except ValueError:
        return default


def get_date_range(days: int = 14) -> tuple[date, date]:
    """Get date range for last N days"""
    end_date = date.today()
    start_date = end_date - timedelta(days=days)
    return start_date, end_date


def calculate_percentage(part: float, whole: float, decimals: int = 2) -> float:
    """Calculate percentage safely"""
    if whole == 0:
        return 0.0
    return round((part / whole) * 100, decimals)


def safe_divide(numerator: float, denominator: float, default: float = 0.0) -> float:
    """Divide safely, returning default if denominator is 0"""
    if denominator == 0:
        return default
    return numerator / denominator


def format_hours(seconds: int) -> float:
    """Convert seconds to hours"""
    if not seconds:
        return 0.0
    return round(seconds / 3600, 2)


def format_days(hours: float) -> float:
    """Convert hours to days (8-hour workday)"""
    if not hours:
        return 0.0
    return round(hours / 8, 2)


def extract_unique_values(items: List[Dict[str, Any]], key: str) -> List[Any]:
    """Extract unique values from list of dictionaries"""
    return list(set([item.get(key) for item in items if item.get(key)]))


def group_by_key(items: List[Dict[str, Any]], key: str) -> Dict[Any, List[Dict[str, Any]]]:
    """Group list of dictionaries by a specific key"""
    groups = {}
    for item in items:
        key_value = item.get(key)
        if key_value:
            if key_value not in groups:
                groups[key_value] = []
            groups[key_value].append(item)
    return groups


def serialize_datetime(obj):
    """JSON serializer for datetime objects"""
    if isinstance(obj, (datetime, date)):
        return obj.isoformat()
    raise TypeError(f"Type {type(obj)} not serializable")


def to_json(data: Any) -> str:
    """Convert data to JSON string with datetime support"""
    return json.dumps(data, default=serialize_datetime, indent=2)


class PerformanceTimer:
    """Simple performance timer context manager"""
    
    def __init__(self, name: str = "Operation"):
        self.name = name
        self.start_time = None
        self.end_time = None
    
    def __enter__(self):
        self.start_time = datetime.now()
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.end_time = datetime.now()
        duration = (self.end_time - self.start_time).total_seconds()
        print(f"{self.name} took {duration:.2f} seconds")
    
    @property
    def duration(self) -> float:
        """Get duration in seconds"""
        if self.start_time and self.end_time:
            return (self.end_time - self.start_time).total_seconds()
        return 0.0