opencode-db 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
utils.py ADDED
@@ -0,0 +1,115 @@
1
+ """Вспомогательные утилиты.
2
+
3
+ Функции общего назначения:
4
+ - parse_ts() / format_ts() — работа с миллисекундными таймштампами
5
+ - summarize_val() — обрезка длинных строк
6
+ - parse_time_spec() — парсинг '30d', '6m', '1y'
7
+ - confirm() — запрос подтверждения
8
+ """
9
+
10
+ import re
11
+ from datetime import UTC, datetime, timedelta
12
+ from typing import Literal, LiteralString
13
+
14
+ from i18n import _
15
+
16
+
17
+ def parse_ts(ts_ms) -> datetime:
18
+ """Конвертирует Unix timestamp в миллисекундах в datetime (UTC)."""
19
+ return datetime.fromtimestamp(ts_ms / 1000, tz=UTC)
20
+
21
+
22
+ def format_ts(ts_ms, fmt="%Y-%m-%d %H:%M:%S") -> str:
23
+ """Форматирует timestamp в строку."""
24
+ return parse_ts(ts_ms).strftime(fmt)
25
+
26
+
27
+ def format_ts_short(ts_ms) -> str:
28
+ """Форматирует timestamp в компактный формат для имён файлов."""
29
+ return format_ts(ts_ms, "%Y-%m-%d_%H.%M.%S")
30
+
31
+
32
+ def summarize_val(v, max_len=120) -> str:
33
+ """Обрезает длинное значение для компактного отображения."""
34
+ s = str(v)
35
+ if len(s) > max_len:
36
+ s = s[:max_len] + "..."
37
+ return s
38
+
39
+
40
+ def parse_time_spec(spec) -> None | datetime:
41
+ """Парсит спецификацию времени ('30d', '6m', '1y') в timedelta."""
42
+ if not spec or not isinstance(spec, str):
43
+ return None
44
+
45
+ spec = spec.strip().lower()
46
+ match = re.match(r"^(\d+)\s*(d|day|days|m|mo|mon|month|months|y|year|years)$", spec)
47
+ if not match:
48
+ return None
49
+
50
+ value = int(match.group(1))
51
+ unit = match.group(2)
52
+
53
+ now = datetime.now(UTC)
54
+
55
+ if unit == "d" or unit.startswith("day"):
56
+ delta = timedelta(days=value)
57
+ elif unit == "m" or unit.startswith("mon"):
58
+ delta = timedelta(days=value * 30.44)
59
+ elif unit == "y" or unit.startswith("year"):
60
+ delta = timedelta(days=value * 365.25)
61
+ else:
62
+ return None
63
+
64
+ return now - delta
65
+
66
+
67
+ def confirm(prompt=None, default=False) -> bool:
68
+ """Запрашивает подтверждение y/N.
69
+
70
+ Args:
71
+ prompt: str, текст приглашения (если None — используется общий)
72
+ default: bool, что возвращать при пустом вводе
73
+
74
+ Returns:
75
+ bool
76
+ """
77
+ if prompt is None:
78
+ prompt = _("yes_no_prompt")
79
+ suffix = _("yes_no_true") if default else _("yes_no_false")
80
+ while True:
81
+ try:
82
+ answer = input(prompt + suffix).strip().lower()
83
+ except (EOFError, KeyboardInterrupt):
84
+ print()
85
+ return False
86
+ if not answer:
87
+ return default
88
+ if answer in ("y", "yes", _("cli.yn_yes")):
89
+ return True
90
+ if answer in ("n", "no", "нет"):
91
+ return False
92
+
93
+
94
+ def format_cost(cost_value) -> str:
95
+ """Форматирует стоимость в долларах."""
96
+ if cost_value is None:
97
+ return "—"
98
+ if cost_value < 0.01:
99
+ return f"${cost_value:.4f}"
100
+ if cost_value < 1:
101
+ return f"${cost_value:.3f}"
102
+ return f"${cost_value:.2f}"
103
+
104
+
105
+ def format_tokens(count) -> LiteralString | Literal["—"]:
106
+ """Форматирует количество токенов с разделителями."""
107
+ if count is None:
108
+ return "—"
109
+ s = str(int(count))
110
+ result = []
111
+ for i, ch in enumerate(reversed(s)):
112
+ if i > 0 and i % 3 == 0:
113
+ result.append("\u202f")
114
+ result.append(ch)
115
+ return "".join(reversed(result))