Coverage for packages / core / common / utils.py: 59%

32 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-05-08 08:37 +1200

1"""Common utility functions for HarnessElo.""" 

2 

3from collections.abc import Sequence 

4from datetime import date, datetime 

5from typing import Any 

6 

7 

8def parse_date(date_str: str) -> date: 

9 """Parse date string in YYYY-MM-DD format. 

10 

11 Args: 

12 date_str: Date string to parse 

13 

14 Returns: 

15 Parsed date object 

16 

17 Raises: 

18 ValueError: If date string is invalid 

19 """ 

20 try: 

21 return datetime.strptime(date_str, "%Y-%m-%d").date() 

22 except ValueError as e: 

23 raise ValueError( 

24 f"Invalid date format '{date_str}', expected YYYY-MM-DD" 

25 ) from e 

26 

27 

28def format_date(d: date) -> str: 

29 """Format date as YYYY-MM-DD string. 

30 

31 Args: 

32 d: Date to format 

33 

34 Returns: 

35 Formatted date string 

36 """ 

37 return d.strftime("%Y-%m-%d") 

38 

39 

40def get_distance_bucket( 

41 distance_m: int | None, 

42 buckets: Sequence[int], 

43 mode: str = "thresholds", 

44 bucket_size: int | None = None, 

45) -> str: 

46 """Get distance bucket label for a given distance. 

47 

48 Args: 

49 distance_m: Distance in meters (can be None) 

50 buckets: List of bucket thresholds in ascending order 

51 mode: Bucketing mode ("thresholds" or "fixed") 

52 bucket_size: Fixed bucket size in meters (required if mode is "fixed") 

53 

54 Returns: 

55 Bucket label (e.g., "<1700", "1700-2000", ">2400") 

56 """ 

57 if distance_m is None: 

58 return "unknown" 

59 

60 if mode == "fixed": 

61 if not bucket_size or bucket_size <= 0: 

62 return "unknown" 

63 start = (distance_m // bucket_size) * bucket_size 

64 end = start + bucket_size - 1 

65 return f"{start}-{end}" 

66 

67 for i, threshold in enumerate(buckets): 

68 if distance_m < threshold: 

69 if i == 0: 

70 return f"<{threshold}" 

71 else: 

72 return f"{buckets[i-1]}-{threshold}" 

73 

74 # Distance is greater than all thresholds 

75 return f">{buckets[-1]}" 

76 

77 

78def safe_get(d: dict[str, Any], *keys: str, default: Any = None) -> Any: 

79 """Safely get nested dictionary value. 

80 

81 Args: 

82 d: Dictionary to query 

83 *keys: Sequence of keys to traverse 

84 default: Default value if key path not found 

85 

86 Returns: 

87 Value at key path or default 

88 

89 Example: 

90 >>> data = {"a": {"b": {"c": 123}}} 

91 >>> safe_get(data, "a", "b", "c") 

92 123 

93 >>> safe_get(data, "a", "x", "y", default=0) 

94 0 

95 """ 

96 current = d 

97 for key in keys: 

98 if not isinstance(current, dict) or key not in current: 

99 return default 

100 current = current[key] 

101 return current